2023-02-11 17:16:36 +00:00
/* eslint-disable eslint-comments/disable-enable-pair */
2023-03-06 15:08:19 +00:00
2023-02-27 18:43:57 +00:00
/* eslint-disable @typescript-eslint/restrict-template-expressions */
2023-02-27 10:28:14 +00:00
/* eslint-disable no-nested-ternary */
2023-02-23 10:07:30 +00:00
2023-02-11 17:16:36 +00:00
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
2023-02-12 17:06:16 +00:00
//import { coin } from '@cosmjs/proto-signing'
2023-06-06 09:16:46 +00:00
import { Sidetab } from '@typeform/embed-react'
2023-02-11 17:16:36 +00:00
import clsx from 'clsx'
import { Alert } from 'components/Alert'
import { Anchor } from 'components/Anchor'
2023-02-13 16:59:16 +00:00
import { BadgeConfirmationModal } from 'components/BadgeConfirmationModal'
import { BadgeLoadingModal } from 'components/BadgeLoadingModal'
2023-02-12 17:06:16 +00:00
import type { BadgeDetailsDataProps } from 'components/badges/creation/BadgeDetails'
import { BadgeDetails } from 'components/badges/creation/BadgeDetails'
import type { ImageUploadDetailsDataProps , MintRule } from 'components/badges/creation/ImageUploadDetails'
import { ImageUploadDetails } from 'components/badges/creation/ImageUploadDetails'
2023-02-11 17:16:36 +00:00
import { Button } from 'components/Button'
import { Conditional } from 'components/Conditional'
2023-02-27 19:55:48 +00:00
import { TextInput } from 'components/forms/FormInput'
import { useInputState } from 'components/forms/FormInput.hooks'
2023-02-22 07:17:24 +00:00
import { Tooltip } from 'components/Tooltip'
2023-02-11 17:16:36 +00:00
import { useContracts } from 'contexts/contracts'
2023-05-03 18:18:09 +00:00
import { addLogItem } from 'contexts/log'
2023-02-11 17:16:36 +00:00
import { useWallet } from 'contexts/wallet'
2023-03-06 18:49:05 +00:00
import type { Badge } from 'contracts/badgeHub'
2023-02-11 17:16:36 +00:00
import type { DispatchExecuteArgs as BadgeHubDispatchExecuteArgs } from 'contracts/badgeHub/messages/execute'
import { dispatchExecute as badgeHubDispatchExecute } from 'contracts/badgeHub/messages/execute'
2023-02-13 10:33:36 +00:00
import * as crypto from 'crypto'
2023-02-13 14:58:28 +00:00
import { toPng } from 'html-to-image'
2023-02-11 17:16:36 +00:00
import type { NextPage } from 'next'
import { NextSeo } from 'next-seo'
2023-03-06 18:49:05 +00:00
import sizeof from 'object-sizeof'
2023-02-11 17:16:36 +00:00
import { QRCodeSVG } from 'qrcode.react'
import { useEffect , useMemo , useRef , useState } from 'react'
import { toast } from 'react-hot-toast'
2023-02-13 14:58:28 +00:00
import { FaCopy , FaSave } from 'react-icons/fa'
2023-02-13 10:33:36 +00:00
import * as secp256k1 from 'secp256k1'
2023-02-11 17:16:36 +00:00
import { upload } from 'services/upload'
2023-02-22 07:17:24 +00:00
import { copy } from 'utils/clipboard'
2023-02-13 10:33:36 +00:00
import { BADGE_HUB_ADDRESS , BLOCK_EXPLORER_URL , NETWORK } from 'utils/constants'
2023-08-18 10:10:22 +00:00
import { getAssetType } from 'utils/getAssetType'
2023-02-11 17:16:36 +00:00
import { withMetadata } from 'utils/layout'
import { links } from 'utils/links'
2023-05-03 18:18:09 +00:00
import { uid } from 'utils/random'
2023-02-28 14:09:06 +00:00
import { resolveAddress } from 'utils/resolveAddress'
2023-02-22 07:17:24 +00:00
import { truncateMiddle } from 'utils/text'
2023-02-11 17:16:36 +00:00
2023-02-27 18:43:57 +00:00
import { generateKeyPairs } from '../../utils/hash'
2023-02-11 17:16:36 +00:00
const BadgeCreationPage : NextPage = ( ) = > {
const wallet = useWallet ( )
const { badgeHub : badgeHubContract } = useContracts ( )
const scrollRef = useRef < HTMLDivElement > ( null )
const badgeHubMessages = useMemo ( ( ) = > badgeHubContract ? . use ( BADGE_HUB_ADDRESS ) , [ badgeHubContract , wallet . address ] )
const [ imageUploadDetails , setImageUploadDetails ] = useState < ImageUploadDetailsDataProps | null > ( null )
2023-02-12 17:06:16 +00:00
const [ badgeDetails , setBadgeDetails ] = useState < BadgeDetailsDataProps | null > ( null )
2023-02-11 17:16:36 +00:00
const [ uploading , setUploading ] = useState ( false )
const [ creatingBadge , setCreatingBadge ] = useState ( false )
2023-02-27 18:43:57 +00:00
const [ isAddingKeysComplete , setIsAddingKeysComplete ] = useState ( false )
2023-02-11 17:16:36 +00:00
const [ readyToCreateBadge , setReadyToCreateBadge ] = useState ( false )
const [ mintRule , setMintRule ] = useState < MintRule > ( 'by_key' )
2023-02-28 14:09:06 +00:00
const [ resolvedMinterAddress , setResolvedMinterAddress ] = useState < string > ( '' )
2023-03-06 18:49:05 +00:00
const [ tempBadge , setTempBadge ] = useState < Badge > ( )
2023-02-11 17:16:36 +00:00
const [ badgeId , setBadgeId ] = useState < string | null > ( null )
const [ imageUrl , setImageUrl ] = useState < string | null > ( null )
2023-02-13 10:33:36 +00:00
const [ createdBadgeKey , setCreatedBadgeKey ] = useState < string | undefined > ( undefined )
2023-02-11 17:16:36 +00:00
const [ transactionHash , setTransactionHash ] = useState < string | null > ( null )
2023-02-27 18:43:57 +00:00
const [ keyPairs , setKeyPairs ] = useState < { publicKey : string ; privateKey : string } [ ] > ( [ ] )
2023-02-27 19:55:48 +00:00
const [ numberOfKeys , setNumberOfKeys ] = useState ( 1 )
2023-02-13 14:58:28 +00:00
const qrRef = useRef < HTMLDivElement > ( null )
2023-02-11 17:16:36 +00:00
2023-02-13 10:33:36 +00:00
const keyState = useInputState ( {
id : 'key' ,
name : 'key' ,
2023-02-22 07:17:24 +00:00
title : 'Public Key' ,
2023-02-23 12:26:42 +00:00
subtitle : 'Part of the key pair to be utilized for post-creation access control' ,
2023-02-13 10:33:36 +00:00
} )
2023-02-27 10:28:14 +00:00
const designatedMinterState = useInputState ( {
id : 'designatedMinter' ,
name : 'designatedMinter' ,
title : 'Minter Address' ,
subtitle : 'The address of the designated minter for this badge' ,
defaultValue : wallet.address ,
} )
2023-02-11 17:16:36 +00:00
const performBadgeCreationChecks = ( ) = > {
try {
setReadyToCreateBadge ( false )
checkImageUploadDetails ( )
2023-02-13 16:59:16 +00:00
checkBadgeDetails ( )
setTimeout ( ( ) = > {
setReadyToCreateBadge ( true )
} , 100 )
2023-02-11 17:16:36 +00:00
} catch ( error : any ) {
toast . error ( error . message , { style : { maxWidth : 'none' } } )
2023-05-03 18:18:09 +00:00
addLogItem ( { id : uid ( ) , message : error.message , type : 'Error' , timestamp : new Date ( ) } )
2023-02-11 17:16:36 +00:00
setUploading ( false )
setReadyToCreateBadge ( false )
}
}
const handleImageUrl = async ( ) = > {
try {
setImageUrl ( null )
setBadgeId ( null )
setTransactionHash ( null )
if ( imageUploadDetails ? . uploadMethod === 'new' ) {
setUploading ( true )
2023-02-13 09:32:14 +00:00
const coverUrl = await upload (
2023-02-11 17:16:36 +00:00
[ imageUploadDetails . assetFile ] as File [ ] ,
imageUploadDetails . uploadService ,
'cover' ,
imageUploadDetails . nftStorageApiKey as string ,
imageUploadDetails . pinataApiKey as string ,
imageUploadDetails . pinataSecretKey as string ,
2023-02-13 09:32:14 +00:00
) . then ( ( imageBaseUrl ) = > {
setUploading ( false )
return ` ipfs:// ${ imageBaseUrl } / ${ imageUploadDetails . assetFile ? . name as string } `
} )
setImageUrl ( coverUrl )
return coverUrl
2023-02-11 17:16:36 +00:00
}
2023-02-13 09:32:14 +00:00
setImageUrl ( imageUploadDetails ? . imageUrl as string )
return imageUploadDetails ? . imageUrl as string
2023-02-11 17:16:36 +00:00
} catch ( error : any ) {
toast . error ( error . message , { style : { maxWidth : 'none' } } )
2023-05-03 18:18:09 +00:00
addLogItem ( { id : uid ( ) , message : error.message , type : 'Error' , timestamp : new Date ( ) } )
2023-02-11 17:16:36 +00:00
setCreatingBadge ( false )
setUploading ( false )
2023-02-13 09:32:14 +00:00
throw new Error ( "Couldn't upload the image." )
2023-02-11 17:16:36 +00:00
}
}
2023-02-28 14:09:06 +00:00
const resolveMinterAddress = async ( ) = > {
await resolveAddress ( designatedMinterState . value . trim ( ) , wallet ) . then ( ( resolvedAddress ) = > {
setResolvedMinterAddress ( resolvedAddress )
} )
}
useEffect ( ( ) = > {
void resolveMinterAddress ( )
} , [ designatedMinterState . value ] )
2023-03-06 18:49:05 +00:00
useEffect ( ( ) = > {
const badge = {
manager : badgeDetails?.manager as string ,
metadata : {
name : badgeDetails?.name || undefined ,
description : badgeDetails?.description || undefined ,
image : imageUrl || undefined ,
image_data : badgeDetails?.image_data || undefined ,
external_url : badgeDetails?.external_url || undefined ,
attributes : badgeDetails?.attributes || undefined ,
background_color : badgeDetails?.background_color || undefined ,
animation_url : badgeDetails?.animation_url || undefined ,
youtube_url : badgeDetails?.youtube_url || undefined ,
} ,
transferrable : badgeDetails?.transferrable as boolean ,
rule :
mintRule === 'by_key'
? {
by_key : keyState.value ,
}
: mintRule === 'by_minter'
? {
by_minter : resolvedMinterAddress ,
}
: 'by_keys' ,
expiry : badgeDetails?.expiry || undefined ,
max_supply : badgeDetails?.max_supply || undefined ,
}
setTempBadge ( badge )
} , [ badgeDetails , keyState . value , mintRule , resolvedMinterAddress , imageUrl ] )
2023-02-12 17:06:16 +00:00
const createNewBadge = async ( ) = > {
2023-02-13 09:32:14 +00:00
try {
if ( ! wallet . initialized ) throw new Error ( 'Wallet not connected' )
if ( ! badgeHubContract ) throw new Error ( 'Contract not found' )
2023-02-13 10:33:36 +00:00
setCreatingBadge ( true )
2023-02-13 09:32:14 +00:00
const coverUrl = await handleImageUrl ( )
const badge = {
manager : badgeDetails?.manager as string ,
metadata : {
name : badgeDetails?.name || undefined ,
2023-08-04 08:54:22 +00:00
description : badgeDetails?.description?.replaceAll ( '\\n' , '\n' ) || undefined ,
2023-02-13 09:32:14 +00:00
image : coverUrl || undefined ,
image_data : badgeDetails?.image_data || undefined ,
external_url : badgeDetails?.external_url || undefined ,
attributes : badgeDetails?.attributes || undefined ,
background_color : badgeDetails?.background_color || undefined ,
2023-08-18 10:10:22 +00:00
animation_url : badgeDetails?.animation_url
? badgeDetails . animation_url
: imageUploadDetails ? . assetFile && getAssetType ( imageUploadDetails . assetFile . name ) === 'video'
? coverUrl
: undefined ,
2023-02-13 09:32:14 +00:00
youtube_url : badgeDetails?.youtube_url || undefined ,
} ,
transferrable : badgeDetails?.transferrable as boolean ,
2023-02-27 10:28:14 +00:00
rule :
mintRule === 'by_key'
? {
by_key : keyState.value ,
}
: mintRule === 'by_minter'
? {
2023-02-28 14:09:06 +00:00
by_minter : resolvedMinterAddress ,
2023-02-27 10:28:14 +00:00
}
2023-02-27 18:43:57 +00:00
: 'by_keys' ,
2023-02-13 09:32:14 +00:00
expiry : badgeDetails?.expiry || undefined ,
max_supply : badgeDetails?.max_supply || undefined ,
}
2023-02-11 17:16:36 +00:00
2023-02-13 09:32:14 +00:00
const payload : BadgeHubDispatchExecuteArgs = {
contract : BADGE_HUB_ADDRESS ,
messages : badgeHubMessages ,
txSigner : wallet.address ,
badge ,
type : 'create_badge' ,
}
2023-02-27 18:43:57 +00:00
if ( mintRule !== 'by_keys' ) {
2023-02-27 19:55:48 +00:00
setBadgeId ( null )
setIsAddingKeysComplete ( false )
2023-02-27 18:43:57 +00:00
const data = await badgeHubDispatchExecute ( payload )
console . log ( data )
setCreatingBadge ( false )
setTransactionHash ( data . split ( ':' ) [ 0 ] )
setBadgeId ( data . split ( ':' ) [ 1 ] )
} else {
2023-02-27 19:55:48 +00:00
setBadgeId ( null )
setIsAddingKeysComplete ( false )
2023-02-27 18:43:57 +00:00
setKeyPairs ( [ ] )
2023-02-27 19:55:48 +00:00
const generatedKeyPairs = generateKeyPairs ( numberOfKeys )
2023-02-27 18:43:57 +00:00
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' } } )
2023-05-03 18:18:09 +00:00
addLogItem ( { id : uid ( ) , message : error.message , type : 'Error' , timestamp : new Date ( ) } )
2023-02-27 18:43:57 +00:00
setUploading ( false )
setIsAddingKeysComplete ( false )
setCreatingBadge ( false )
} )
}
2023-05-03 18:18:09 +00:00
} catch ( error : any ) {
toast . error ( error . message , { style : { maxWidth : 'none' } } )
addLogItem ( { id : uid ( ) , message : error.message , type : 'Error' , timestamp : new Date ( ) } )
2023-02-13 09:32:14 +00:00
setCreatingBadge ( false )
setUploading ( false )
2023-02-11 17:16:36 +00:00
}
}
const checkImageUploadDetails = ( ) = > {
if ( ! wallet . initialized ) throw new Error ( 'Wallet not connected.' )
if ( ! imageUploadDetails ) {
2023-02-13 09:32:14 +00:00
throw new Error ( 'Please specify the image related details.' )
2023-02-11 17:16:36 +00:00
}
2023-02-13 09:32:14 +00:00
2023-02-11 17:16:36 +00:00
if ( imageUploadDetails . uploadMethod === 'new' && imageUploadDetails . assetFile === undefined ) {
throw new Error ( 'Please select the image file' )
}
if ( imageUploadDetails . uploadMethod === 'new' ) {
if ( imageUploadDetails . uploadService === 'nft-storage' ) {
if ( imageUploadDetails . nftStorageApiKey === '' ) {
throw new Error ( 'Please enter a valid NFT.Storage API key' )
}
} else if ( imageUploadDetails . pinataApiKey === '' || imageUploadDetails . pinataSecretKey === '' ) {
throw new Error ( 'Please enter Pinata API and secret keys' )
}
}
if ( imageUploadDetails . uploadMethod === 'existing' && ! imageUploadDetails . imageUrl ? . includes ( 'ipfs://' ) ) {
throw new Error ( 'Please specify a valid image URL' )
}
}
2023-02-13 16:59:16 +00:00
const checkBadgeDetails = ( ) = > {
if ( ! badgeDetails ) throw new Error ( 'Please fill out the required fields' )
2023-02-27 10:28:14 +00:00
if ( mintRule === 'by_key' && ( keyState . value === '' || ! createdBadgeKey ) )
throw new Error ( 'Please generate a public key' )
2023-02-13 16:59:16 +00:00
if ( badgeDetails . external_url ) {
try {
const url = new URL ( badgeDetails . external_url )
} catch ( e : any ) {
throw new Error ( ` Invalid external url: Make sure to include the protocol (e.g. https://) ` )
2023-05-03 18:18:09 +00:00
addLogItem ( {
id : uid ( ) ,
message : 'Invalid external url: Make sure to include the protocol (e.g. https://)' ,
type : 'Error' ,
timestamp : new Date ( ) ,
} )
2023-02-13 16:59:16 +00:00
}
}
}
2023-02-11 17:16:36 +00:00
2023-02-13 10:33:36 +00:00
const handleGenerateKey = ( ) = > {
let privKey : Buffer
do {
privKey = crypto . randomBytes ( 32 )
} while ( ! secp256k1 . privateKeyVerify ( privKey ) )
const privateKey = privKey . toString ( 'hex' )
setCreatedBadgeKey ( privateKey )
console . log ( 'Private Key: ' , privateKey )
const publicKey = Buffer . from ( secp256k1 . publicKeyCreate ( privKey ) ) . toString ( 'hex' )
2023-02-13 16:59:16 +00:00
setBadgeId ( null )
2023-02-13 10:33:36 +00:00
keyState . onChange ( publicKey )
}
2023-02-13 14:58:28 +00:00
const handleDownloadQr = async ( ) = > {
const qrElement = qrRef . current
await toPng ( qrElement as HTMLElement ) . then ( ( dataUrl ) = > {
const link = document . createElement ( 'a' )
link . download = ` badge- ${ badgeId as string } .png `
link . href = dataUrl
link . click ( )
} )
}
2023-02-27 19:55:48 +00:00
const handleDownloadKeys = ( ) = > {
const element = document . createElement ( 'a' )
const file = new Blob ( [ JSON . stringify ( keyPairs ) ] , { type : 'text/plain' } )
element . href = URL . createObjectURL ( file )
element . download = ` badge- ${ badgeId as string } -keys.json `
document . body . appendChild ( element )
element . click ( )
}
2023-02-13 14:58:28 +00:00
const copyClaimURL = async ( ) = > {
const baseURL = NETWORK === 'testnet' ? 'https://badges.publicawesome.dev' : 'https://badges.stargaze.zone'
const claimURL = ` ${ baseURL } /?id= ${ badgeId as string } &key= ${ createdBadgeKey as string } `
await navigator . clipboard . writeText ( claimURL )
toast . success ( 'Copied claim URL to clipboard' )
}
2023-02-11 17:16:36 +00:00
const checkwalletBalance = ( ) = > {
if ( ! wallet . initialized ) throw new Error ( 'Wallet not connected.' )
// TODO: estimate creation cost and check wallet balance
}
useEffect ( ( ) = > {
if ( badgeId !== null ) scrollRef . current ? . scrollIntoView ( { behavior : 'smooth' } )
} , [ badgeId ] )
useEffect ( ( ) = > {
setImageUrl ( imageUploadDetails ? . imageUrl as string )
} , [ imageUploadDetails ? . imageUrl ] )
useEffect ( ( ) = > {
setBadgeId ( null )
setReadyToCreateBadge ( false )
} , [ imageUploadDetails ? . uploadMethod ] )
2023-02-27 18:43:57 +00:00
useEffect ( ( ) = > {
if ( keyPairs . length > 0 ) {
toast . success ( 'Key pairs generated successfully.' )
}
} , [ keyPairs ] )
2023-02-11 17:16:36 +00:00
return (
< div >
< NextSeo title = "Create Badge" / >
< div className = "mt-5 space-y-5 text-center" >
< h1 className = "font-heading text-4xl font-bold" > Create Badge < / h1 >
< Conditional test = { uploading } >
2023-02-13 16:59:16 +00:00
< BadgeLoadingModal / >
2023-02-11 17:16:36 +00:00
< / Conditional >
< p >
Make sure you check our { ' ' }
< Anchor className = "font-bold text-plumbus hover:underline" external href = { links [ 'Docs' ] } >
documentation
< / Anchor > { ' ' }
on how to create a new badge .
< / p >
< / div >
< div className = "mx-10" ref = { scrollRef } >
< Conditional test = { badgeId !== null } >
2023-02-27 15:33:57 +00:00
< Conditional test = { mintRule === 'by_key' } >
< Alert className = "mt-5" type = "info" >
< div className = "flex flex-row" >
< div >
< div className = "w-[384px] h-[384px]" ref = { qrRef } >
< QRCodeSVG
className = "mx-auto"
level = "H"
size = { 384 }
value = { ` ${
NETWORK === 'testnet' ? 'https://badges.publicawesome.dev' : 'https://badges.stargaze.zone'
} / ? id = $ { badgeId as string } & key = $ { createdBadgeKey as string } ` }
/ >
< / div >
< div className = "grid grid-cols-2 gap-2 mt-2 w-[384px]" >
< Button
className = "items-center w-full text-sm text-center rounded"
leftIcon = { < FaSave / > }
onClick = { ( ) = > void handleDownloadQr ( ) }
>
Download QR Code
< / Button >
< Button
className = "w-full text-sm text-center rounded"
isWide
leftIcon = { < FaCopy / > }
onClick = { ( ) = > void copyClaimURL ( ) }
variant = "solid"
>
Copy Claim URL
< / Button >
< / div >
2023-02-13 14:58:28 +00:00
< / div >
2023-02-27 15:33:57 +00:00
< div className = "ml-4 text-lg" >
Badge ID : { ` ${ badgeId as string } ` }
< br / >
Private Key :
< Tooltip label = "Click to copy the private key" >
< button
className = "group flex space-x-2 font-mono text-base text-white/50 hover:underline"
onClick = { ( ) = > void copy ( createdBadgeKey as string ) }
type = "button"
>
< span > { truncateMiddle ( createdBadgeKey ? createdBadgeKey : '' , 32 ) } < / span >
< FaCopy className = "opacity-50 group-hover:opacity-100" / >
< / button >
< / Tooltip >
< br / >
Transaction Hash : { ' ' }
< Conditional test = { NETWORK === 'testnet' } >
< Anchor
className = "text-stargaze hover:underline"
external
href = { ` ${ BLOCK_EXPLORER_URL } /tx/ ${ transactionHash as string } ` }
>
{ transactionHash }
< / Anchor >
< / Conditional >
< Conditional test = { NETWORK === 'mainnet' } >
< Anchor
className = "text-stargaze hover:underline"
external
href = { ` ${ BLOCK_EXPLORER_URL } /txs/ ${ transactionHash as string } ` }
>
{ transactionHash }
< / Anchor >
< / Conditional >
< br / >
< div className = "text-base" >
< div className = "flex-row pt-4 mt-4 border-t-2" >
< span >
You may click { ' ' }
< Anchor
className = "text-stargaze hover:underline"
external
href = { ` ${
NETWORK === 'testnet' ? 'https://badges.publicawesome.dev' : 'https://badges.stargaze.zone'
} / ? id = $ { badgeId as string } & key = $ { createdBadgeKey as string } ` }
>
here
< / Anchor > { ' ' }
or scan the QR code to claim a badge .
< / span >
< / div >
< br / >
< span className = "mt-4" >
You may download the QR code or copy the claim URL to share with others .
< / span >
< / div >
< br / >
2023-02-13 14:58:28 +00:00
< / div >
< / div >
2023-02-27 15:33:57 +00:00
< / Alert >
< / Conditional >
2023-02-27 19:55:48 +00:00
< Conditional test = { mintRule === 'by_keys' } >
< Alert className = "mt-5" type = "info" >
< div className = "ml-4 text-lg" >
Badge ID : { ` ${ badgeId as string } ` }
< br / >
Transaction Hash : { ' ' }
< Conditional test = { NETWORK === 'testnet' } >
< Anchor
className = "text-stargaze hover:underline"
external
href = { ` ${ BLOCK_EXPLORER_URL } /tx/ ${ transactionHash as string } ` }
>
{ transactionHash }
< / Anchor >
< / Conditional >
< Conditional test = { NETWORK === 'mainnet' } >
< Anchor
className = "text-stargaze hover:underline"
external
href = { ` ${ BLOCK_EXPLORER_URL } /txs/ ${ transactionHash as string } ` }
>
{ transactionHash }
< / Anchor >
< / Conditional >
< br / >
2023-02-28 07:05:06 +00:00
< Conditional test = { isAddingKeysComplete } >
< div className = "pt-2 mt-4 border-t-2" >
< span className = "mt-2" >
Make sure to download the whitelisted keys added during badge creation .
< / span >
< Button className = "mt-2" onClick = { ( ) = > handleDownloadKeys ( ) } >
Download Keys
< / Button >
< / div >
< / Conditional >
2023-02-27 19:55:48 +00:00
< div className = "text-base" >
< div className = "flex-row pt-4 mt-4 border-t-2" >
< span >
You may click { ' ' }
< Anchor
className = "text-stargaze hover:underline"
external
href = { ` /badges/actions/?badgeHubContractAddress= ${ BADGE_HUB_ADDRESS } &badgeId= ${
badgeId as string
} ` }
>
here
< / Anchor > { ' ' }
and select Actions { '>' } Add Keys to add ( additional ) whitelisted keys or select Actions { '>' } { ' ' }
Mint by Keys to use one of the keys to mint a badge .
< / span >
< / div >
< / div >
< / div >
< / Alert >
< / Conditional >
2023-02-27 15:33:57 +00:00
< Conditional test = { mintRule === 'by_minter' } >
< Alert className = "mt-5" type = "info" >
2023-02-13 14:58:28 +00:00
< div className = "ml-4 text-lg" >
Badge ID : { ` ${ badgeId as string } ` }
< br / >
2023-02-28 14:09:06 +00:00
Designated Minter Address : { ` ${ resolvedMinterAddress } ` }
2023-02-22 07:17:24 +00:00
< br / >
2023-02-13 14:58:28 +00:00
Transaction Hash : { ' ' }
< Conditional test = { NETWORK === 'testnet' } >
< Anchor
className = "text-stargaze hover:underline"
external
href = { ` ${ BLOCK_EXPLORER_URL } /tx/ ${ transactionHash as string } ` }
>
{ transactionHash }
< / Anchor >
< / Conditional >
< Conditional test = { NETWORK === 'mainnet' } >
< Anchor
className = "text-stargaze hover:underline"
external
href = { ` ${ BLOCK_EXPLORER_URL } /txs/ ${ transactionHash as string } ` }
>
{ transactionHash }
< / Anchor >
< / Conditional >
< br / >
< div className = "text-base" >
< div className = "flex-row pt-4 mt-4 border-t-2" >
< span >
You may click { ' ' }
< Anchor
className = "text-stargaze hover:underline"
external
2023-02-27 15:33:57 +00:00
href = { ` /badges/actions/?badgeHubContractAddress= ${ BADGE_HUB_ADDRESS } &badgeId= ${
badgeId as string
} ` }
2023-02-13 14:58:28 +00:00
>
here
< / Anchor > { ' ' }
2023-02-27 15:33:57 +00:00
and select Actions { '>' } Mint By Minter to mint a badge .
2023-02-13 14:58:28 +00:00
< / span >
< / div >
< / div >
< / div >
2023-02-27 15:33:57 +00:00
< / Alert >
< / Conditional >
2023-02-11 17:16:36 +00:00
< / Conditional >
< / div >
< div >
< div
className = { clsx (
'mx-10 mt-5' ,
'grid before:absolute relative grid-cols-3 grid-flow-col items-stretch rounded' ,
'before:inset-x-0 before:bottom-0 before:border-white/25' ,
) }
>
< div
className = { clsx (
'isolate space-y-1 border-2' ,
'first-of-type:rounded-tl-md last-of-type:rounded-tr-md' ,
mintRule === 'by_key' ? 'border-stargaze' : 'border-transparent' ,
mintRule !== 'by_key' ? 'bg-stargaze/5 hover:bg-stargaze/80' : 'hover:bg-white/5' ,
) }
>
2023-03-07 07:33:59 +00:00
< Tooltip
2023-03-07 13:13:08 +00:00
backgroundColor = "bg-blue-500"
2023-03-07 14:53:05 +00:00
className = "m-0 w-1/3"
label = "The same single private key can be utilized by multiple users to share badge minting authority. Ideal for projects with multiple administrators."
2023-03-07 07:33:59 +00:00
placement = "bottom"
2023-02-11 17:16:36 +00:00
>
2023-03-07 07:33:59 +00:00
< button
className = "p-4 w-full h-full text-left bg-transparent"
onClick = { ( ) = > {
setMintRule ( 'by_key' )
setReadyToCreateBadge ( false )
setBadgeId ( null )
} }
type = "button"
>
< h4 className = "font-bold" > Mint Rule : By Key < / h4 >
< span className = "text-sm text-white/80 line-clamp-4" >
Multiple badges can be minted to different addresses by the owner of a single designated key .
< / span >
< / button >
< / Tooltip >
2023-02-11 17:16:36 +00:00
< / div >
< div
className = { clsx (
'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' ,
2023-02-27 18:43:57 +00:00
mintRule !== 'by_keys' ? 'bg-stargaze/5 hover:bg-stargaze/80' : 'hover:bg-white/5' ,
2023-02-11 17:16:36 +00:00
) }
>
2023-03-07 07:33:59 +00:00
< Tooltip
2023-03-07 13:13:08 +00:00
backgroundColor = "bg-blue-500"
2023-03-07 14:53:05 +00:00
className = "m-0 w-1/3"
label = "The key pairs are intended to be saved and shared with others. Each user can claim a badge separately using the key pair that they received."
2023-03-07 07:33:59 +00:00
placement = "bottom"
2023-02-11 17:16:36 +00:00
>
2023-03-07 07:33:59 +00:00
< button
className = "p-4 w-full h-full text-left bg-transparent"
onClick = { ( ) = > {
setMintRule ( 'by_keys' )
setReadyToCreateBadge ( false )
setBadgeId ( null )
} }
type = "button"
>
< h4 className = "font-bold" > Mint Rule : By Keys < / h4 >
< span className = "text-sm text-white/80 line-clamp-4" >
Multiple key pairs are generated and designated to be only used once to mint a single badge .
< / span >
< / button >
< / Tooltip >
2023-02-11 17:16:36 +00:00
< / div >
< div
className = { clsx (
'isolate space-y-1 border-2' ,
'first-of-type:rounded-tl-md last-of-type:rounded-tr-md' ,
mintRule === 'by_minter' ? 'border-stargaze' : 'border-transparent' ,
2023-02-27 10:28:14 +00:00
mintRule !== 'by_minter' ? 'bg-stargaze/5 hover:bg-stargaze/80' : 'hover:bg-white/5' ,
2023-02-11 17:16:36 +00:00
) }
>
2023-03-07 09:12:43 +00:00
< Tooltip
2023-03-07 13:13:08 +00:00
backgroundColor = "bg-blue-500"
2023-03-07 14:53:05 +00:00
className = "m-0 w-1/3"
label = "The most basic approach. However, having just one authorized address for minting badges might limit your ability to delegate that responsibility."
2023-03-07 09:12:43 +00:00
placement = "bottom"
2023-02-11 17:16:36 +00:00
>
2023-03-07 09:12:43 +00:00
< button
className = "p-4 w-full h-full text-left bg-transparent"
onClick = { ( ) = > {
setMintRule ( 'by_minter' )
setReadyToCreateBadge ( false )
setBadgeId ( null )
} }
type = "button"
>
< h4 className = "font-bold" > Mint Rule : By Minter < / h4 >
< span className = "text-sm text-white/80 line-clamp-4" >
No key designation . Multiple badges can be minted to different addresses by a pre - determined minter
address .
< / span >
< / button >
< / Tooltip >
2023-02-11 17:16:36 +00:00
< / div >
< / div >
< / div >
< div className = "mx-10" >
< ImageUploadDetails mintRule = { mintRule } onChange = { setImageUploadDetails } / >
2023-02-27 10:28:14 +00:00
< Conditional test = { mintRule === 'by_key' } >
< 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" { ...keyState } disabled required / >
< Button className = "mt-14 ml-4" isDisabled = { creatingBadge } onClick = { handleGenerateKey } >
Generate Key
< / Button >
< / div >
< / Conditional >
2023-02-11 17:16:36 +00:00
2023-02-27 18:43:57 +00:00
< 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" >
2023-02-27 19:55:48 +00:00
< div className = "grid grid-cols-2 gap-24" >
< div className = "flex flex-col ml-4" >
< span className = "font-bold" > Number of Keys < / span >
< span className = "text-sm text-white/80" >
The number of key pairs to be whitelisted for post - creation access control
< / span >
< / div >
< input
2023-02-28 07:05:06 +00:00
className = "p-2 w-1/4 max-w-2xl bg-white/10 rounded border-2 border-white/20"
2023-02-27 19:55:48 +00:00
onChange = { ( e ) = > setNumberOfKeys ( Number ( e . target . value ) ) }
required
type = "number"
value = { numberOfKeys }
/ >
< / div >
2023-02-27 18:43:57 +00:00
< / div >
< / Conditional >
2023-02-27 10:28:14 +00:00
< 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" >
2023-02-28 14:09:06 +00:00
< TextInput className = "ml-4 w-full max-w-lg" { ...designatedMinterState } required / >
2023-02-27 10:28:14 +00:00
< / div >
< / Conditional >
2023-02-13 10:33:36 +00:00
2023-02-11 17:16:36 +00:00
< div className = "flex justify-between py-3 px-8 rounded border-2 border-white/20 grid-col-2" >
2023-02-12 17:06:16 +00:00
< BadgeDetails
2023-03-06 18:49:05 +00:00
metadataSize = {
tempBadge ? . metadata . image === undefined
? Number ( sizeof ( tempBadge ) ) + Number ( sizeof ( tempBadge ? . metadata . attributes ) ) + 150
: Number ( sizeof ( tempBadge ) ) + Number ( sizeof ( tempBadge . metadata . attributes ) )
}
2023-02-12 17:06:16 +00:00
mintRule = { mintRule }
onChange = { setBadgeDetails }
uploadMethod = { imageUploadDetails ? . uploadMethod ? imageUploadDetails . uploadMethod : 'new' }
2023-02-11 17:16:36 +00:00
/ >
< / div >
2023-02-13 16:59:16 +00:00
< Conditional test = { readyToCreateBadge } >
< BadgeConfirmationModal confirm = { createNewBadge } / >
< / Conditional >
2023-02-11 17:16:36 +00:00
< div className = "flex justify-end w-full" >
< Button
2023-02-13 14:58:28 +00:00
className = "relative justify-center p-2 mt-2 mb-6 max-h-12 text-white bg-plumbus hover:bg-plumbus-light border-0"
2023-02-11 17:16:36 +00:00
isLoading = { creatingBadge }
2023-02-13 16:59:16 +00:00
onClick = { ( ) = > performBadgeCreationChecks ( ) }
2023-02-11 17:16:36 +00:00
variant = "solid"
>
Create Badge
< / Button >
< / div >
2023-06-06 09:16:46 +00:00
< Sidetab buttonColor = "#455CF9" buttonText = "Studio Survey" height = { 600 } id = "yJnL8fXk" width = { 800 } / >
2023-02-11 17:16:36 +00:00
< / div >
< / div >
)
}
export default withMetadata ( BadgeCreationPage , { center : false } )