Implement initial mint_by_key logic for Badge Actions
This commit is contained in:
parent
bdd39d2dc2
commit
e1adca8ddf
@ -21,10 +21,11 @@ import { toast } from 'react-hot-toast'
|
|||||||
import { FaArrowRight } from 'react-icons/fa'
|
import { FaArrowRight } from 'react-icons/fa'
|
||||||
import { useMutation } from 'react-query'
|
import { useMutation } from 'react-query'
|
||||||
import * as secp256k1 from 'secp256k1'
|
import * as secp256k1 from 'secp256k1'
|
||||||
|
import { sha256 } from 'utils/hash'
|
||||||
import type { AirdropAllocation } from 'utils/isValidAccountsFile'
|
import type { AirdropAllocation } from 'utils/isValidAccountsFile'
|
||||||
import { resolveAddress } from 'utils/resolveAddress'
|
import { resolveAddress } from 'utils/resolveAddress'
|
||||||
|
|
||||||
import { TextInput } from '../../forms/FormInput'
|
import { AddressInput, TextInput } from '../../forms/FormInput'
|
||||||
import type { MintRule } from '../creation/ImageUploadDetails'
|
import type { MintRule } from '../creation/ImageUploadDetails'
|
||||||
|
|
||||||
interface BadgeActionsProps {
|
interface BadgeActionsProps {
|
||||||
@ -49,6 +50,7 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
|
|||||||
const [editFee, setEditFee] = useState<number | undefined>(undefined)
|
const [editFee, setEditFee] = useState<number | undefined>(undefined)
|
||||||
const [triggerDispatch, setTriggerDispatch] = useState<boolean>(false)
|
const [triggerDispatch, setTriggerDispatch] = useState<boolean>(false)
|
||||||
const [keyPairs, setKeyPairs] = useState<string[]>([])
|
const [keyPairs, setKeyPairs] = useState<string[]>([])
|
||||||
|
const [signature, setSignature] = useState<string>('')
|
||||||
|
|
||||||
const actionComboboxState = useActionsComboboxState()
|
const actionComboboxState = useActionsComboboxState()
|
||||||
const type = actionComboboxState.value?.id
|
const type = actionComboboxState.value?.id
|
||||||
@ -139,6 +141,7 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
|
|||||||
name: 'owner',
|
name: 'owner',
|
||||||
title: 'Owner',
|
title: 'Owner',
|
||||||
subtitle: 'The owner of the badge',
|
subtitle: 'The owner of the badge',
|
||||||
|
defaultValue: wallet.address,
|
||||||
})
|
})
|
||||||
|
|
||||||
const pubkeyState = useInputState({
|
const pubkeyState = useInputState({
|
||||||
@ -148,11 +151,11 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
|
|||||||
subtitle: 'The public key for the badge',
|
subtitle: 'The public key for the badge',
|
||||||
})
|
})
|
||||||
|
|
||||||
const signatureState = useInputState({
|
const privateKeyState = useInputState({
|
||||||
id: 'signature',
|
id: 'privateKey',
|
||||||
name: 'signature',
|
name: 'privateKey',
|
||||||
title: 'Signature',
|
title: 'Private Key',
|
||||||
subtitle: 'The signature for the badge',
|
subtitle: 'The private key to claim the badge with',
|
||||||
})
|
})
|
||||||
|
|
||||||
const nftState = useInputState({
|
const nftState = useInputState({
|
||||||
@ -170,6 +173,8 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
|
|||||||
})
|
})
|
||||||
|
|
||||||
const showMetadataField = isEitherType(type, ['edit_badge'])
|
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 = {
|
const payload: DispatchExecuteArgs = {
|
||||||
badge: {
|
badge: {
|
||||||
@ -223,7 +228,7 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
|
|||||||
editFee,
|
editFee,
|
||||||
owner: resolvedOwnerAddress,
|
owner: resolvedOwnerAddress,
|
||||||
pubkey: pubkeyState.value,
|
pubkey: pubkeyState.value,
|
||||||
signature: signatureState.value,
|
signature,
|
||||||
keys: [],
|
keys: [],
|
||||||
limit: limitState.value,
|
limit: limitState.value,
|
||||||
owners: [],
|
owners: [],
|
||||||
@ -339,6 +344,11 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
|
|||||||
})
|
})
|
||||||
}, [triggerDispatch])
|
}, [triggerDispatch])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (privateKeyState.value.length === 64 && resolvedOwnerAddress)
|
||||||
|
handleGenerateSignature(badgeId, resolvedOwnerAddress, privateKeyState.value)
|
||||||
|
}, [privateKeyState.value, resolvedOwnerAddress])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const addresses: string[] = []
|
const addresses: string[] = []
|
||||||
airdropAllocationArray.forEach((allocation) => {
|
airdropAllocationArray.forEach((allocation) => {
|
||||||
@ -356,6 +366,9 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
|
|||||||
|
|
||||||
const { isLoading, mutate } = useMutation(
|
const { isLoading, mutate } = useMutation(
|
||||||
async (event: FormEvent) => {
|
async (event: FormEvent) => {
|
||||||
|
if (!wallet.client) {
|
||||||
|
throw new Error('Please connect your wallet.')
|
||||||
|
}
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
if (!type) {
|
if (!type) {
|
||||||
throw new Error('Please select an action.')
|
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.')
|
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') {
|
if (wallet.client && type === 'edit_badge') {
|
||||||
const feeRateRaw = await wallet.client.queryContractRaw(
|
const feeRateRaw = await wallet.client.queryContractRaw(
|
||||||
badgeHubContractAddress,
|
badgeHubContractAddress,
|
||||||
@ -442,6 +459,22 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
|
|||||||
setAirdropAllocationArray(data)
|
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) => {
|
const handleGenerateKeys = (amount: number) => {
|
||||||
for (let i = 0; i < amount; i++) {
|
for (let i = 0; i < amount; i++) {
|
||||||
let privKey: Buffer
|
let privKey: Buffer
|
||||||
@ -482,6 +515,15 @@ export const BadgeActions = ({ badgeHubContractAddress, badgeId, badgeHubMessage
|
|||||||
<TextInput className="mt-2" {...youtubeUrlState} />
|
<TextInput className="mt-2" {...youtubeUrlState} />
|
||||||
</div>
|
</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 && (
|
{/* {showAirdropFileField && (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
"@fontsource/jetbrains-mono": "^4",
|
"@fontsource/jetbrains-mono": "^4",
|
||||||
"@fontsource/roboto": "^4",
|
"@fontsource/roboto": "^4",
|
||||||
"@headlessui/react": "^1",
|
"@headlessui/react": "^1",
|
||||||
|
"jscrypto": "^1.0.3",
|
||||||
"@keplr-wallet/cosmos": "^0.9.16",
|
"@keplr-wallet/cosmos": "^0.9.16",
|
||||||
"@pinata/sdk": "^1.1.26",
|
"@pinata/sdk": "^1.1.26",
|
||||||
"@popperjs/core": "^2",
|
"@popperjs/core": "^2",
|
||||||
|
8
utils/hash.ts
Normal file
8
utils/hash.ts
Normal 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())
|
||||||
|
}
|
@ -5627,6 +5627,11 @@ js-yaml@^4.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
argparse "^2.0.1"
|
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:
|
jsesc@^2.5.1:
|
||||||
version "2.5.2"
|
version "2.5.2"
|
||||||
resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz"
|
resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz"
|
||||||
|
Loading…
Reference in New Issue
Block a user