import { Alert, Button, Center, Input, Spinner, VStack, } from "@chakra-ui/react"; import { yupResolver } from "@hookform/resolvers/yup"; import { DataV2, Metadata } from "@metaplex-foundation/mpl-token-metadata"; import { useWallet } from "@solana/wallet-adapter-react"; import { PublicKey, Signer, TransactionInstruction } from "@solana/web3.js"; import { MarketplaceSdk } from "@strata-foundation/marketplace-sdk"; import { truthy, useProvider, useStrataSdks, useTokenBondingFromMint, useTokenMetadata, } from "@strata-foundation/react"; import { SplTokenBonding } from "@strata-foundation/spl-token-bonding"; import { SplTokenMetadata } from "@strata-foundation/spl-utils"; import { useRouter } from "next/router"; import React from "react"; import { useAsync, useAsyncCallback } from "react-async-hook"; import { DefaultValues, FormProvider, useForm } from "react-hook-form"; import * as yup from "yup"; import { FormControlWithError } from "./FormControlWithError"; import { Recipient } from "./Recipient"; import { IMetadataFormProps, TokenMetadataInputs } from "./TokenMetadataInputs"; import { NFT_STORAGE_API_KEY } from "../../constants"; interface IEditBountyFormProps extends IMetadataFormProps { shortName: string; contact: string; discussion: string; authority: string; } const validationSchema = yup.object({ image: yup.mixed(), name: yup.string().required().min(2), description: yup.string(), shortName: yup.string().min(2).max(10), contact: yup.string(), discussion: yup.string(), authority: yup.string().required(), }); async function editBounty( tokenMetadataSdk: SplTokenMetadata, tokenBondingSdk: SplTokenBonding, values: IEditBountyFormProps, mintKey: PublicKey ): Promise { const uri = await tokenMetadataSdk.uploadMetadata({ provider: values.provider, name: values.name, symbol: values.shortName, description: values.description, image: values.image, mint: mintKey, attributes: MarketplaceSdk.bountyAttributes({ mint: mintKey, discussion: values.discussion, contact: values.contact, }), }); const instructions: TransactionInstruction[] = []; const signers: Signer[] = []; const { instructions: metaInstrs, signers: metaSigners } = await tokenMetadataSdk.updateMetadataInstructions({ metadata: await Metadata.getPDA(mintKey), data: new DataV2({ // Max name len 32 name: values.name.substring(0, 32), symbol: values.shortName.substring(0, 10), uri, sellerFeeBasisPoints: 0, creators: null, collection: null, uses: null, }), }); instructions.push(...metaInstrs); signers.push(...metaSigners); const tokenBondingKey = (await SplTokenBonding.tokenBondingKey(mintKey))[0]; const tokenBondingAcct = await tokenBondingSdk.getTokenBonding( tokenBondingKey ); if ( values.authority && tokenBondingSdk.wallet.publicKey.equals( tokenBondingAcct!.reserveAuthority as PublicKey ) ) { const authority = new PublicKey(values.authority); const { instructions: bondInstrs, signers: bondSigners } = await tokenBondingSdk.updateTokenBondingInstructions({ tokenBonding: tokenBondingKey, generalAuthority: authority, reserveAuthority: authority, }); instructions.push(...bondInstrs); signers.push(...bondSigners); } await tokenBondingSdk.sendInstructions(instructions, signers); return mintKey; } export const EditBountyFormRaw = ({ mintKey, values, onComplete, hide = new Set(), }: { mintKey: PublicKey; values: DefaultValues; onComplete?: () => void; hide?: Set; }) => { const formProps = useForm({ resolver: yupResolver(validationSchema), defaultValues: values, }); const { register, handleSubmit, watch, setValue, formState: { errors, isSubmitting }, } = formProps; const { publicKey } = useWallet(); const { awaitingApproval } = useProvider(); const { execute, loading, error } = useAsyncCallback(editBounty); const { tokenMetadataSdk, tokenBondingSdk } = useStrataSdks(); const router = useRouter(); const authority = watch("authority"); const onSubmit = async (values: IEditBountyFormProps) => { await execute(tokenMetadataSdk!, tokenBondingSdk!, values, mintKey); onComplete && onComplete(); }; const authorityRegister = register("authority"); return (
{!hide.has("shortName") && ( )} {!hide.has("authority") && ( {publicKey && ( )} )} {!hide.has("contact") && ( )} {!hide.has("discussion") && ( )} {error && {error.toString()}}
); }; const getFileFromUrl = async ( url: string | undefined, name: string, defaultType: string = "image/jpeg" ): Promise => { if (!url) { return undefined; } const data = await fetch(url, { cache: "no-cache" }); const blob = await data.blob(); const fileName = `${name}${blob.type === defaultType ? ".jpeg" : "png"}`; const file = new File([blob], fileName, { type: blob.type || defaultType }); return file; }; export const EditBountyForm = ({ mintKey, onComplete, hide = new Set(), }: { mintKey: PublicKey; onComplete?: () => void; hide?: Set; }) => { const { info: tokenBonding, loading: loadingBonding } = useTokenBondingFromMint(mintKey); const { displayName, image, metadata, data, loading: loadingMetadata, } = useTokenMetadata(mintKey); const { result: file, loading } = useAsync(getFileFromUrl, [image, "image"]); const attributes = React.useMemo( () => SplTokenMetadata.attributesToRecord(data?.attributes), [data] ); if ( loadingBonding || loadingMetadata || loading || !tokenBonding || !metadata ) { return (
); } return ( ); };