Skip to content
This repository has been archived by the owner on Mar 1, 2024. It is now read-only.

Commit

Permalink
Switch to address loopup merge (#50)
Browse files Browse the repository at this point in the history
* Merge main.

* Update to env reference.

* Move to address lookup.

* Better handling.

* Refactor code base for verify-nft

---------

Co-authored-by: rk1129 <[email protected]>
  • Loading branch information
captain-munch and rk1129 authored Apr 6, 2023
1 parent 42c3217 commit 2f99083
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 86 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ DB_DIALECT=sqlite
DB_URL=sqlite://database.sqlite

INSCRIPTION_API=https://api.hiro.so/ordinals/v1/inscriptions
ADDRESS_API=https://ordapi.xyz/address

AXIOM_TOKEN=
AXIOM_DATASET=
Expand Down
14 changes: 7 additions & 7 deletions button/verify.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const BipMessages = require('../db/bip-messages')

const MODAL_ID = 'verifyNFTModal'
const SIGNATURE_ID = 'signatureInput'
const INS_ID_ID = 'insIdInput'
const ADDRESS = 'addressInput'

module.exports = {
async execute(interaction) {
Expand All @@ -20,9 +20,9 @@ module.exports = {
if (channelId) {
const modal = new ModalBuilder().setCustomId(MODAL_ID).setTitle('Verify Your Ownership')

const insIdInput = new TextInputBuilder()
.setCustomId(INS_ID_ID)
.setLabel('Inscription ID')
const addressInput = new TextInputBuilder()
.setCustomId(ADDRESS)
.setLabel('Wallet Address')
.setStyle(TextInputStyle.Short)
.setMaxLength(70)

Expand Down Expand Up @@ -73,11 +73,11 @@ module.exports = {
.setValue(message)
.setRequired(false)

const insIdActionRow = new ActionRowBuilder().addComponents(insIdInput)
const addressActionRow = new ActionRowBuilder().addComponents(addressInput)
const signatureActionRow = new ActionRowBuilder().addComponents(signatureInput)
const bipMessageActionRow = new ActionRowBuilder().addComponents(bipMessageInput)

modal.addComponents(insIdActionRow, signatureActionRow, bipMessageActionRow)
modal.addComponents(addressActionRow, signatureActionRow, bipMessageActionRow)

await interaction.showModal(modal)

Expand All @@ -95,5 +95,5 @@ module.exports = {
},
MODAL_ID,
SIGNATURE_ID,
INS_ID_ID,
ADDRESS,
}
17 changes: 17 additions & 0 deletions db/collections-inscriptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,27 @@ const Inscriptions = sequelize.define('Inscriptions', {
},
})

const getInscription = async (inscriptionId, channelId) => {
const inscription = await Inscriptions.findOne({
where: {
inscriptionRef: inscriptionId,
},
include: {
model: Collections,
where: {
channelId,
},
},
})

return inscription
}

Collections.hasMany(Inscriptions, { foreignKey: 'collectionId' })
Inscriptions.belongsTo(Collections, { foreignKey: 'collectionId' })

module.exports = {
Collections,
Inscriptions,
getInscription,
}
177 changes: 98 additions & 79 deletions modal/verify-nft.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,109 +2,128 @@ const axios = require('axios')
const errorEmbed = require('../embed/error-embed')
const successEmbed = require('../embed/success-embed')
const warningEmbed = require('../embed/warning-embed')
const infoEmbed = require('../embed/info-embed')
const roleEmbed = require('../embed/role-embed')
const { Collections, Inscriptions } = require('../db/collections-inscriptions')
const { getInscription } = require('../db/collections-inscriptions')
const UserInscriptions = require('../db/user-inscriptions')
const BipMessages = require('../db/bip-messages')
const { MODAL_ID, SIGNATURE_ID, INS_ID_ID } = require('../button/verify')
const { MODAL_ID, SIGNATURE_ID, ADDRESS } = require('../button/verify')

const checkSignature = async (address, signature, bipMessage) => {
const data = {
jsonrpc: '1.0',
id: 'curltest',
method: 'verifymessage',
params: [address, signature, bipMessage],
}

const config = {
headers: {
'content-type': 'text/plain',
},
auth: {
username: process.env.RPC_USERNAME,
password: process.env.RPC_PASSWORD,
},
}

const res = await axios.post(`https://${process.env.RPC_HOST}:${process.env.RPC_PORT}/`, data, config)
return res.data.result
}

module.exports = {
data: MODAL_ID,
async execute(interaction) {
try {
const signature = interaction.fields.getTextInputValue(SIGNATURE_ID)
const inscriptionId = interaction.fields.getTextInputValue(INS_ID_ID)

const inscription = await Inscriptions.findOne({
where: {
inscriptionRef: inscriptionId,
},
include: {
model: Collections,
const address = interaction.fields.getTextInputValue(ADDRESS)

try {
const bipMessage = await BipMessages.findOne({
where: {
channelId: interaction.channelId,
userId: interaction.user.id,
},
},
})

if (inscription) {
try {
const bipMessage = await BipMessages.findOne({
where: {
channelId: interaction.channelId,
userId: interaction.user.id,
},
})

if (!bipMessage) {
const embed = warningEmbed('Signature not found', "Couldn't fetch the signature to validate against.")
return interaction.reply({ embeds: [embed], ephemeral: true })
}
})

const { data: insInfo } = await axios.get(`${process.env.INSCRIPTION_API}/${inscriptionId}`)
if (!bipMessage) {
const embed = warningEmbed('Signature not found', "Couldn't fetch the signature to validate against.")
return interaction.reply({ embeds: [embed], ephemeral: true })
}

const data = {
jsonrpc: '1.0',
id: 'curltest',
method: 'verifymessage',
params: [insInfo.address, signature, bipMessage.message],
}
const embed = infoEmbed('Checking signature', 'Please wait...')
await interaction.reply({
embeds: [embed],
ephemeral: true,
})

const config = {
headers: {
'content-type': 'text/plain',
},
auth: {
username: process.env.RPC_USERNAME,
password: process.env.RPC_PASSWORD,
},
}
const res = await checkSignature(address, signature, bipMessage.message)

if (!res) {
const warning = warningEmbed('Verify Problem', "The BIP-322 node couldn't verify your signature.")
return await interaction.editReply({ embeds: [warning], ephemeral: true })
}

const res = await axios.post(`https://${process.env.RPC_HOST}:${process.env.RPC_PORT}/`, data, config)
const inscriptions = await axios.get(`${process.env.ADDRESS_API}/${address}`)
const addedRoles = []
const notFoundRoles = []

if (!res.data.result) {
const warning = warningEmbed('Verify Problem', "The BIP-322 node couldn't verify your signature.")
return await interaction.reply({ embeds: [warning], ephemeral: true })
for (const insInfo of inscriptions.data) {
const inscription = await getInscription(insInfo.id, interaction.channelId)
if (inscription) {
const role = interaction.member.guild.roles.cache.find(
(roleItem) => roleItem.name === inscription.Collection.role
)

if (role) {
await interaction.member.roles.add(role)
addedRoles.push(roleEmbed(interaction, role.name))
// Everything has been allocated, lets upsert into the UserInscriptions table
await UserInscriptions.upsert({
userId: interaction.user.id,
inscriptionId: inscription.id,
})
} else {
notFoundRoles.push(role.name)
}
}
}

const role = interaction.member.guild.roles.cache.find(
(roleItem) => roleItem.name === inscription.Collection.role
// Valid roles
if (addedRoles.length > 0) {
const roleRef = addedRoles.length > 1 ? 'roles' : 'role'
const embed = successEmbed(
'Successfully verified',
`Your signature was validated and you were assigned the ${addedRoles.join(' ')} ${roleRef}.`
)

if (role) {
await interaction.member.roles.add(role)
const embed = successEmbed(
'Successfully verified',
`Your signature was validated and you were assigned the ${roleEmbed(interaction, role.name)} role.`
)
return interaction.editReply({ embeds: [embed], ephemeral: true })
}

// Everything has been allocated, lets upsert into the UserInscriptions table
await UserInscriptions.upsert({
userId: interaction.user.id,
inscriptionId: inscription.id,
})

return interaction.reply({ embeds: [embed], ephemeral: true })
} else {
const embed = warningEmbed(
'Role not found',
`The ${inscription.Collection.role} role that was assigned to this collection isn't available.`
)
return interaction.reply({ embeds: [embed], ephemeral: true })
}
} catch (error) {
// Valid error from the RPC node
if (error.response && error.response.status === 500) {
const warning = warningEmbed('Verify Problem', "Your BIP-322 signature couldn't be verified.")
return await interaction.reply({ embeds: [warning], ephemeral: true })
}
const embed = errorEmbed(error)
return interaction.reply({ embeds: [embed], ephemeral: true })
// Invalid Roles
if (notFoundRoles.length > 0) {
const embed =
notFoundRoles.length > 1
? warningEmbed('Roles not found', `The **${notFoundRoles.join(' ')}** roles aren't available.`)
: warningEmbed('Role not found', `The **${notFoundRoles.join(' ')}** role isn't available.`)
return interaction.editReply({ embeds: [embed], ephemeral: true })
}
}

const warning = warningEmbed('Verify Problem', "There's no matching collection for that inscription.")
return interaction.reply({ embeds: [warning], ephemeral: true })
// Catch where no collections were matched
const warning = warningEmbed(
'Verify Problem',
"There's no matching collections for the inscriptions in your wallet."
)
return interaction.reply({ embeds: [warning], ephemeral: true })
} catch (error) {
// Valid error from the RPC node
if (error.response && error.response.status === 500) {
const warning = warningEmbed('Verify Problem', "Your BIP-322 signature couldn't be verified.")
return await interaction.reply({ embeds: [warning], ephemeral: true })
}
const embed = errorEmbed(error)
return interaction.reply({ embeds: [embed], ephemeral: true })
}
} catch (error) {
const embed = errorEmbed(error)
return interaction.reply({ embeds: [embed], ephemeral: true })
Expand Down

0 comments on commit 2f99083

Please sign in to comment.