/* eslint-disable eslint-comments/disable-enable-pair */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ //import { coin } from '@cosmjs/proto-signing' import clsx from 'clsx' import { Alert } from 'components/Alert' import { Anchor } from 'components/Anchor' import { BadgeConfirmationModal } from 'components/BadgeConfirmationModal' import { BadgeLoadingModal } from 'components/BadgeLoadingModal' 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' import { Button } from 'components/Button' import { Conditional } from 'components/Conditional' import { TextInput } from 'components/forms/FormInput' import { useInputState } from 'components/forms/FormInput.hooks' import { Tooltip } from 'components/Tooltip' import { useContracts } from 'contexts/contracts' import { useWallet } from 'contexts/wallet' import type { DispatchExecuteArgs as BadgeHubDispatchExecuteArgs } from 'contracts/badgeHub/messages/execute' import { dispatchExecute as badgeHubDispatchExecute } from 'contracts/badgeHub/messages/execute' import * as crypto from 'crypto' import { toPng } from 'html-to-image' import type { NextPage } from 'next' import { NextSeo } from 'next-seo' import { QRCodeSVG } from 'qrcode.react' import { useEffect, useMemo, useRef, useState } from 'react' import { toast } from 'react-hot-toast' import { FaCopy, FaSave } from 'react-icons/fa' import * as secp256k1 from 'secp256k1' import { upload } from 'services/upload' import { copy } from 'utils/clipboard' import { BADGE_HUB_ADDRESS, BLOCK_EXPLORER_URL, NETWORK } from 'utils/constants' import { withMetadata } from 'utils/layout' import { links } from 'utils/links' import { truncateMiddle } from 'utils/text' const BadgeCreationPage: NextPage = () => { const wallet = useWallet() const { badgeHub: badgeHubContract } = useContracts() const scrollRef = useRef(null) const badgeHubMessages = useMemo(() => badgeHubContract?.use(BADGE_HUB_ADDRESS), [badgeHubContract, wallet.address]) const [imageUploadDetails, setImageUploadDetails] = useState(null) const [badgeDetails, setBadgeDetails] = useState(null) const [uploading, setUploading] = useState(false) const [creatingBadge, setCreatingBadge] = useState(false) const [readyToCreateBadge, setReadyToCreateBadge] = useState(false) const [mintRule, setMintRule] = useState('by_key') const [badgeId, setBadgeId] = useState(null) const [imageUrl, setImageUrl] = useState(null) const [createdBadgeKey, setCreatedBadgeKey] = useState(undefined) const [transactionHash, setTransactionHash] = useState(null) const qrRef = useRef(null) const keyState = useInputState({ id: 'key', name: 'key', title: 'Public Key', subtitle: 'Part of the key pair to be utilized for post-creation access control', }) const performBadgeCreationChecks = () => { try { setReadyToCreateBadge(false) checkImageUploadDetails() checkBadgeDetails() setTimeout(() => { setReadyToCreateBadge(true) }, 100) } catch (error: any) { toast.error(error.message, { style: { maxWidth: 'none' } }) setUploading(false) setReadyToCreateBadge(false) } } const handleImageUrl = async () => { try { setImageUrl(null) setBadgeId(null) setTransactionHash(null) if (imageUploadDetails?.uploadMethod === 'new') { setUploading(true) const coverUrl = await upload( [imageUploadDetails.assetFile] as File[], imageUploadDetails.uploadService, 'cover', imageUploadDetails.nftStorageApiKey as string, imageUploadDetails.pinataApiKey as string, imageUploadDetails.pinataSecretKey as string, ).then((imageBaseUrl) => { setUploading(false) return `ipfs://${imageBaseUrl}/${imageUploadDetails.assetFile?.name as string}` }) setImageUrl(coverUrl) return coverUrl } setImageUrl(imageUploadDetails?.imageUrl as string) return imageUploadDetails?.imageUrl as string } catch (error: any) { toast.error(error.message, { style: { maxWidth: 'none' } }) setCreatingBadge(false) setUploading(false) throw new Error("Couldn't upload the image.") } } const createNewBadge = async () => { try { if (!wallet.initialized) throw new Error('Wallet not connected') if (!badgeHubContract) throw new Error('Contract not found') setCreatingBadge(true) const coverUrl = await handleImageUrl() const badge = { manager: badgeDetails?.manager as string, metadata: { name: badgeDetails?.name || undefined, description: badgeDetails?.description || undefined, 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, animation_url: badgeDetails?.animation_url || undefined, youtube_url: badgeDetails?.youtube_url || undefined, }, transferrable: badgeDetails?.transferrable as boolean, rule: { by_key: keyState.value, }, expiry: badgeDetails?.expiry || undefined, max_supply: badgeDetails?.max_supply || undefined, } const payload: BadgeHubDispatchExecuteArgs = { contract: BADGE_HUB_ADDRESS, messages: badgeHubMessages, txSigner: wallet.address, 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' } }) setCreatingBadge(false) setUploading(false) } } const checkImageUploadDetails = () => { if (!wallet.initialized) throw new Error('Wallet not connected.') if (!imageUploadDetails) { throw new Error('Please specify the image related details.') } 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') } } const checkBadgeDetails = () => { if (!badgeDetails) throw new Error('Please fill out the required fields') if (keyState.value === '' || !createdBadgeKey) throw new Error('Please generate a public key') 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://)`) } } } 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') setBadgeId(null) keyState.onChange(publicKey) } 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() }) } // copy claim url to clipboard 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') } 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]) return (

Create Badge

Make sure you check our{' '} documentation {' '} on how to create a new badge.

Badge ID:{` ${badgeId as string}`}
Private Key:
Transaction Hash: {' '} {transactionHash} {transactionHash}
You may click{' '} here {' '} or scan the QR code to claim a badge.

You may download the QR code or copy the claim URL to share with others.

) } export default withMetadata(BadgeCreationPage, { center: false })