Enable badge creation using Mint Rule: By Keys
This commit is contained in:
parent
68efd8d361
commit
cc31179516
@ -28,7 +28,7 @@ export interface MigrateResponse {
|
||||
export interface Rule {
|
||||
by_key?: string
|
||||
by_minter?: string
|
||||
by_keys?: string[]
|
||||
by_keys?: string
|
||||
}
|
||||
|
||||
export interface Trait {
|
||||
@ -53,7 +53,7 @@ export interface Badge {
|
||||
manager: string
|
||||
metadata: Metadata
|
||||
transferrable: boolean
|
||||
rule: Rule
|
||||
rule: Rule | string
|
||||
expiry?: number
|
||||
max_supply?: number
|
||||
}
|
||||
@ -102,7 +102,7 @@ export interface CreateBadgeMessage {
|
||||
manager: string
|
||||
metadata: Metadata
|
||||
transferrable: boolean
|
||||
rule: Rule
|
||||
rule: Rule | string
|
||||
expiry?: number
|
||||
max_supply?: number
|
||||
}
|
||||
@ -349,6 +349,16 @@ export const badgeHub = (client: SigningCosmWasmClient, txSigner: string): Badge
|
||||
}
|
||||
|
||||
const addKeys = async (senderAddress: string, id: number, keys: string[]): Promise<string> => {
|
||||
const feeRateRaw = await client.queryContractRaw(
|
||||
contractAddress,
|
||||
toUtf8(Buffer.from(Buffer.from('fee_rate').toString('hex'), 'hex').toString()),
|
||||
)
|
||||
console.log('Fee Rate Raw: ', feeRateRaw)
|
||||
const feeRate = JSON.parse(new TextDecoder().decode(feeRateRaw as Uint8Array))
|
||||
console.log('Fee Rate:', feeRate)
|
||||
|
||||
console.log('keys size: ', sizeof(keys))
|
||||
console.log(keys)
|
||||
const res = await client.execute(
|
||||
senderAddress,
|
||||
contractAddress,
|
||||
@ -360,6 +370,7 @@ export const badgeHub = (client: SigningCosmWasmClient, txSigner: string): Badge
|
||||
},
|
||||
'auto',
|
||||
'',
|
||||
[coin(Math.ceil((Number(sizeof(keys)) * 1.1 * Number(feeRate.key)) / 2), 'ustars')],
|
||||
)
|
||||
|
||||
return res.transactionHash
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* eslint-disable eslint-comments/disable-enable-pair */
|
||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||
/* eslint-disable no-nested-ternary */
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
@ -16,8 +17,8 @@ import type { ImageUploadDetailsDataProps, MintRule } from 'components/badges/cr
|
||||
import { ImageUploadDetails } from 'components/badges/creation/ImageUploadDetails'
|
||||
import { Button } from 'components/Button'
|
||||
import { Conditional } from 'components/Conditional'
|
||||
import { TextInput } from 'components/forms/FormInput'
|
||||
import { useInputState } from 'components/forms/FormInput.hooks'
|
||||
import { NumberInput, TextInput } from 'components/forms/FormInput'
|
||||
import { useInputState, useNumberInputState } from 'components/forms/FormInput.hooks'
|
||||
import { Tooltip } from 'components/Tooltip'
|
||||
import { useContracts } from 'contexts/contracts'
|
||||
import { useWallet } from 'contexts/wallet'
|
||||
@ -39,6 +40,8 @@ import { withMetadata } from 'utils/layout'
|
||||
import { links } from 'utils/links'
|
||||
import { truncateMiddle } from 'utils/text'
|
||||
|
||||
import { generateKeyPairs } from '../../utils/hash'
|
||||
|
||||
const BadgeCreationPage: NextPage = () => {
|
||||
const wallet = useWallet()
|
||||
const { badgeHub: badgeHubContract } = useContracts()
|
||||
@ -51,6 +54,7 @@ const BadgeCreationPage: NextPage = () => {
|
||||
|
||||
const [uploading, setUploading] = useState(false)
|
||||
const [creatingBadge, setCreatingBadge] = useState(false)
|
||||
const [isAddingKeysComplete, setIsAddingKeysComplete] = useState(false)
|
||||
const [readyToCreateBadge, setReadyToCreateBadge] = useState(false)
|
||||
const [mintRule, setMintRule] = useState<MintRule>('by_key')
|
||||
|
||||
@ -58,6 +62,7 @@ const BadgeCreationPage: NextPage = () => {
|
||||
const [imageUrl, setImageUrl] = useState<string | null>(null)
|
||||
const [createdBadgeKey, setCreatedBadgeKey] = useState<string | undefined>(undefined)
|
||||
const [transactionHash, setTransactionHash] = useState<string | null>(null)
|
||||
const [keyPairs, setKeyPairs] = useState<{ publicKey: string; privateKey: string }[]>([])
|
||||
const qrRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const keyState = useInputState({
|
||||
@ -67,6 +72,14 @@ const BadgeCreationPage: NextPage = () => {
|
||||
subtitle: 'Part of the key pair to be utilized for post-creation access control',
|
||||
})
|
||||
|
||||
const numberOfKeysState = useNumberInputState({
|
||||
id: 'numberOfKeys',
|
||||
name: 'numberOfKeys',
|
||||
title: 'Number of Keys',
|
||||
subtitle: 'The number of key pairs to be generated for post-creation access control',
|
||||
defaultValue: 1,
|
||||
})
|
||||
|
||||
const designatedMinterState = useInputState({
|
||||
id: 'designatedMinter',
|
||||
name: 'designatedMinter',
|
||||
@ -151,9 +164,7 @@ const BadgeCreationPage: NextPage = () => {
|
||||
? {
|
||||
by_minter: designatedMinterState.value.trim(),
|
||||
}
|
||||
: {
|
||||
by_keys: [keyState.value],
|
||||
},
|
||||
: 'by_keys',
|
||||
expiry: badgeDetails?.expiry || undefined,
|
||||
max_supply: badgeDetails?.max_supply || undefined,
|
||||
}
|
||||
@ -165,13 +176,46 @@ const BadgeCreationPage: NextPage = () => {
|
||||
badge,
|
||||
type: 'create_badge',
|
||||
}
|
||||
if (mintRule !== 'by_keys') {
|
||||
const data = await badgeHubDispatchExecute(payload)
|
||||
console.log(data)
|
||||
setCreatingBadge(false)
|
||||
setTransactionHash(data.split(':')[0])
|
||||
setBadgeId(data.split(':')[1])
|
||||
} catch (error: any) {
|
||||
} else {
|
||||
setKeyPairs([])
|
||||
const generatedKeyPairs = generateKeyPairs(numberOfKeysState.value)
|
||||
setKeyPairs(generatedKeyPairs)
|
||||
await badgeHubDispatchExecute(payload)
|
||||
.then(async (data) => {
|
||||
setCreatingBadge(false)
|
||||
setTransactionHash(data.split(':')[0])
|
||||
setBadgeId(data.split(':')[1])
|
||||
const res = await toast.promise(
|
||||
badgeHubContract.use(BADGE_HUB_ADDRESS)?.addKeys(
|
||||
wallet.address,
|
||||
Number(data.split(':')[1]),
|
||||
generatedKeyPairs.map((key) => key.publicKey),
|
||||
) as Promise<string>,
|
||||
{
|
||||
loading: 'Adding keys...',
|
||||
success: (result) => {
|
||||
setIsAddingKeysComplete(true)
|
||||
return `Keys added successfully! Tx Hash: ${result}`
|
||||
},
|
||||
error: (error: { message: any }) => `Failed to add keys: ${error.message}`,
|
||||
},
|
||||
)
|
||||
})
|
||||
.catch((error: { message: any }) => {
|
||||
toast.error(error.message, { style: { maxWidth: 'none' } })
|
||||
setUploading(false)
|
||||
setIsAddingKeysComplete(false)
|
||||
setCreatingBadge(false)
|
||||
})
|
||||
}
|
||||
} catch (err: any) {
|
||||
toast.error(err.message, { style: { maxWidth: 'none' } })
|
||||
setCreatingBadge(false)
|
||||
setUploading(false)
|
||||
}
|
||||
@ -263,6 +307,12 @@ const BadgeCreationPage: NextPage = () => {
|
||||
setReadyToCreateBadge(false)
|
||||
}, [imageUploadDetails?.uploadMethod])
|
||||
|
||||
useEffect(() => {
|
||||
if (keyPairs.length > 0) {
|
||||
toast.success('Key pairs generated successfully.')
|
||||
}
|
||||
}, [keyPairs])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<NextSeo title="Create Badge" />
|
||||
@ -463,12 +513,11 @@ const BadgeCreationPage: NextPage = () => {
|
||||
'isolate space-y-1 border-2',
|
||||
'first-of-type:rounded-tl-md last-of-type:rounded-tr-md',
|
||||
mintRule === 'by_keys' ? 'border-stargaze' : 'border-transparent',
|
||||
mintRule !== 'by_keys' ? 'text-slate-500 bg-stargaze/5 hover:bg-gray/20' : 'hover:bg-white/5',
|
||||
mintRule !== 'by_keys' ? 'bg-stargaze/5 hover:bg-stargaze/80' : 'hover:bg-white/5',
|
||||
)}
|
||||
>
|
||||
<button
|
||||
className="p-4 w-full h-full text-left bg-transparent"
|
||||
disabled
|
||||
onClick={() => {
|
||||
setMintRule('by_keys')
|
||||
setReadyToCreateBadge(false)
|
||||
@ -476,7 +525,7 @@ const BadgeCreationPage: NextPage = () => {
|
||||
type="button"
|
||||
>
|
||||
<h4 className="font-bold">Mint Rule: By Keys</h4>
|
||||
<span className="text-sm text-slate-500 line-clamp-2">
|
||||
<span className="text-sm text-white/80 line-clamp-2">
|
||||
Similar to the By Key rule, however each designated private key can only be used once to mint a badge.
|
||||
</span>
|
||||
</button>
|
||||
@ -498,7 +547,7 @@ const BadgeCreationPage: NextPage = () => {
|
||||
type="button"
|
||||
>
|
||||
<h4 className="font-bold">Mint Rule: By Minter</h4>
|
||||
<span className="text-sm line-clamp-2 text-slate/500">
|
||||
<span className="text-sm text-white/80 line-clamp-2">
|
||||
Badges can be minted by a designated minter account.
|
||||
</span>
|
||||
</button>
|
||||
@ -517,6 +566,12 @@ const BadgeCreationPage: NextPage = () => {
|
||||
</div>
|
||||
</Conditional>
|
||||
|
||||
<Conditional test={mintRule === 'by_keys'}>
|
||||
<div className="flex flex-row justify-start py-3 px-8 mb-3 w-full rounded border-2 border-white/20">
|
||||
<NumberInput className="ml-4 w-full max-w-2xl" {...numberOfKeysState} required />
|
||||
</div>
|
||||
</Conditional>
|
||||
|
||||
<Conditional test={mintRule === 'by_minter'}>
|
||||
<div className="flex flex-row justify-start py-3 px-8 mb-3 w-full rounded border-2 border-white/20">
|
||||
<TextInput className="ml-4 w-full max-w-2xl" {...designatedMinterState} required />
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* eslint-disable eslint-comments/disable-enable-pair */
|
||||
|
||||
import * as crypto from 'crypto'
|
||||
import { Word32Array } from 'jscrypto'
|
||||
import { SHA256 } from 'jscrypto/SHA256'
|
||||
import * as secp256k1 from 'secp256k1'
|
||||
@ -23,3 +24,21 @@ export function generateSignature(id: number, owner: string, privateKey: string)
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
export function generateKeyPairs(amount: number) {
|
||||
const keyPairs: { publicKey: string; privateKey: string }[] = []
|
||||
for (let i = 0; i < amount; i++) {
|
||||
let privKey: Buffer
|
||||
do {
|
||||
privKey = crypto.randomBytes(32)
|
||||
} while (!secp256k1.privateKeyVerify(privKey))
|
||||
|
||||
const privateKey = privKey.toString('hex')
|
||||
const publicKey = Buffer.from(secp256k1.publicKeyCreate(privKey)).toString('hex')
|
||||
keyPairs.push({
|
||||
publicKey,
|
||||
privateKey,
|
||||
})
|
||||
}
|
||||
return keyPairs
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user