Implement initial mint_by_key logic for Badge Actions

This commit is contained in:
Serkan Reis 2023-02-21 18:26:10 +03:00
parent bdd39d2dc2
commit e1adca8ddf
4 changed files with 63 additions and 7 deletions

View File

@ -21,10 +21,11 @@ import { toast } from 'react-hot-toast'
import { FaArrowRight } from 'react-icons/fa'
import { useMutation } from 'react-query'
import * as secp256k1 from 'secp256k1'
import { sha256 } from 'utils/hash'
import type { AirdropAllocation } from 'utils/isValidAccountsFile'
import { resolveAddress } from 'utils/resolveAddress'
import { TextInput } from '../../forms/FormInput'
import { AddressInput, TextInput } from '../../forms/FormInput'
import type { MintRule } from '../creation/ImageUploadDetails'
interface BadgeActionsProps {
@ -49,6 +50,7 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
const [editFee, setEditFee] = useState<number | undefined>(undefined)
const [triggerDispatch, setTriggerDispatch] = useState<boolean>(false)
const [keyPairs, setKeyPairs] = useState<string[]>([])
const [signature, setSignature] = useState<string>('')
const actionComboboxState = useActionsComboboxState()
const type = actionComboboxState.value?.id
@ -139,6 +141,7 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
name: 'owner',
title: 'Owner',
subtitle: 'The owner of the badge',
defaultValue: wallet.address,
})
const pubkeyState = useInputState({
@ -148,11 +151,11 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
subtitle: 'The public key for the badge',
})
const signatureState = useInputState({
id: 'signature',
name: 'signature',
title: 'Signature',
subtitle: 'The signature for the badge',
const privateKeyState = useInputState({
id: 'privateKey',
name: 'privateKey',
title: 'Private Key',
subtitle: 'The private key to claim the badge with',
})
const nftState = useInputState({
@ -170,6 +173,8 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
})
const showMetadataField = isEitherType(type, ['edit_badge'])
const showOwnerField = isEitherType(type, ['mint_by_key', 'mint_by_keys'])
const showPrivateKeyField = isEitherType(type, ['mint_by_key', 'mint_by_keys'])
const payload: DispatchExecuteArgs = {
badge: {
@ -223,7 +228,7 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
editFee,
owner: resolvedOwnerAddress,
pubkey: pubkeyState.value,
signature: signatureState.value,
signature,
keys: [],
limit: limitState.value,
owners: [],
@ -339,6 +344,11 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
})
}, [triggerDispatch])
useEffect(() => {
if (privateKeyState.value.length === 64 && resolvedOwnerAddress)
handleGenerateSignature(badgeId, resolvedOwnerAddress, privateKeyState.value)
}, [privateKeyState.value, resolvedOwnerAddress])
useEffect(() => {
const addresses: string[] = []
airdropAllocationArray.forEach((allocation) => {
@ -356,6 +366,9 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
const { isLoading, mutate } = useMutation(
async (event: FormEvent) => {
if (!wallet.client) {
throw new Error('Please connect your wallet.')
}
event.preventDefault()
if (!type) {
throw new Error('Please select an action.')
@ -364,6 +377,10 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
throw new Error('Please enter the Badge Hub contract addresses.')
}
if (type === 'mint_by_key' && privateKeyState.value.length !== 64) {
throw new Error('Please enter a valid private key.')
}
if (wallet.client && type === 'edit_badge') {
const feeRateRaw = await wallet.client.queryContractRaw(
badgeHubContractAddress,
@ -442,6 +459,22 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
setAirdropAllocationArray(data)
}
const handleGenerateSignature = (id: number, owner: string, privateKey: string) => {
try {
const message = `claim badge ${id} for user ${owner}`
const privKey = Buffer.from(privateKey, 'hex')
// const pubKey = Buffer.from(secp256k1.publicKeyCreate(privKey, true))
const msgBytes = Buffer.from(message, 'utf8')
const msgHashBytes = sha256(msgBytes)
const signedMessage = secp256k1.ecdsaSign(msgHashBytes, privKey)
setSignature(Buffer.from(signedMessage.signature).toString('hex'))
} catch (error) {
console.log(error)
toast.error('Error generating signature.')
}
}
const handleGenerateKeys = (amount: number) => {
for (let i = 0; i < amount; i++) {
let privKey: Buffer
@ -482,6 +515,15 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
<TextInput className="mt-2" {...youtubeUrlState} />
</div>
)}
{showOwnerField && (
<AddressInput
className="mt-2"
{...ownerState}
subtitle="The address that the badge will be minted to"
title="Owner"
/>
)}
{showPrivateKeyField && <TextInput className="mt-2" {...privateKeyState} />}
{/* {showAirdropFileField && (
<FormGroup

View File

@ -21,6 +21,7 @@
"@fontsource/jetbrains-mono": "^4",
"@fontsource/roboto": "^4",
"@headlessui/react": "^1",
"jscrypto": "^1.0.3",
"@keplr-wallet/cosmos": "^0.9.16",
"@pinata/sdk": "^1.1.26",
"@popperjs/core": "^2",

8
utils/hash.ts Normal file
View File

@ -0,0 +1,8 @@
/* eslint-disable eslint-comments/disable-enable-pair */
import { Word32Array } from 'jscrypto'
import { SHA256 } from 'jscrypto/SHA256'
export function sha256(data: Buffer): Buffer {
return Buffer.from(SHA256.hash(new Word32Array(data)).toUint8Array())
}

View File

@ -5627,6 +5627,11 @@ js-yaml@^4.1.0:
dependencies:
argparse "^2.0.1"
jscrypto@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/jscrypto/-/jscrypto-1.0.3.tgz#598febca2a939d6f679c54f56e1fe364cef30cc9"
integrity sha512-lryZl0flhodv4SZHOqyb1bx5sKcJxj0VBo0Kzb4QMAg3L021IC9uGpl0RCZa+9KJwlRGSK2C80ITcwbe19OKLQ==
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz"