Enable badge creation using Mint Rule: By Keys

This commit is contained in:
Serkan Reis 2023-02-27 21:43:57 +03:00
parent 68efd8d361
commit cc31179516
3 changed files with 104 additions and 19 deletions

View File

@ -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

View File

@ -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',
}
const data = await badgeHubDispatchExecute(payload)
console.log(data)
setCreatingBadge(false)
setTransactionHash(data.split(':')[0])
setBadgeId(data.split(':')[1])
} catch (error: any) {
toast.error(error.message, { style: { maxWidth: 'none' } })
if (mintRule !== 'by_keys') {
const data = await badgeHubDispatchExecute(payload)
console.log(data)
setCreatingBadge(false)
setTransactionHash(data.split(':')[0])
setBadgeId(data.split(':')[1])
} 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 />

View File

@ -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
}