import { Alert, Button, Flex, Input, Text, 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 { useWalletModal } from "@solana/wallet-adapter-react-ui"; import { PublicKey, Signer, TransactionInstruction } from "@solana/web3.js"; import { useMint, useProvider, usePublicKey, useStrataSdks, useTokenMetadata } from "@strata-foundation/react"; import { SplTokenMetadata } from "@strata-foundation/spl-utils"; import { useRouter } from "next/router"; import React, { useEffect } from "react"; import { useAsyncCallback } from "react-async-hook"; import { DefaultValues, FormProvider, useForm } from "react-hook-form"; import * as yup from "yup"; import { FormControlWithError } from "./FormControlWithError"; import { MintSelect } from "./MintSelect"; import { IMetadataFormProps, TokenMetadataInputs } from "./TokenMetadataInputs"; interface IEditMetadataFormProps extends IMetadataFormProps { symbol: string; mint: string; externalUrl: string; } const validationSchema = yup.object({ mint: yup.string().required(), image: yup.mixed(), name: yup.string().required().min(2), description: yup.string(), symbol: yup.string().min(2).max(10), externalUrl: yup.string(), }); async function editMetadata( tokenMetadataSdk: SplTokenMetadata, values: IEditMetadataFormProps, ): Promise { const mintKey = new PublicKey(values.mint); const uri = await tokenMetadataSdk.uploadMetadata({ provider: values.provider, name: values.name, symbol: values.symbol, description: values.description, image: values.image, mint: mintKey, externalUrl: values.externalUrl, }); const instructions: TransactionInstruction[] = []; const signers: Signer[] = []; const metadata = await Metadata.getPDA(mintKey); const data = new DataV2({ // Max name len 32 name: values.name.substring(0, 32), symbol: values.symbol.substring(0, 10), uri, sellerFeeBasisPoints: 0, creators: null, collection: null, uses: null, }); if (await tokenMetadataSdk.provider.connection.getAccountInfo(metadata)) { const { instructions: metaInstrs, signers: metaSigners } = await tokenMetadataSdk.updateMetadataInstructions({ metadata, data, }); instructions.push(...metaInstrs); signers.push(...metaSigners); } else { const { instructions: metaInstrs, signers: metaSigners } = await tokenMetadataSdk.createMetadataInstructions({ mint: mintKey, data, }); instructions.push(...metaInstrs); signers.push(...metaSigners); } await tokenMetadataSdk.sendInstructions(instructions, signers); return mintKey; } export const EditMetadataForm = ({ values, onComplete, }: { values: DefaultValues; onComplete?: (mintKey: PublicKey) => void; }) => { const formProps = useForm({ resolver: yupResolver(validationSchema), defaultValues: values, }); const { register, handleSubmit, watch, setValue, formState: { errors, isSubmitting }, } = formProps; const { awaitingApproval } = useProvider(); const { execute, loading, error } = useAsyncCallback(editMetadata); const { tokenMetadataSdk } = useStrataSdks(); const { visible, setVisible } = useWalletModal(); const { connected, publicKey } = useWallet(); const router = useRouter(); const { mint, name, symbol } = watch(); const mintKey = usePublicKey(mint as string | undefined); const mintAcc = useMint(mintKey); const { metadata, data, image } = useTokenMetadata(mintKey); const insufficientAuthority = metadata?.updateAuthority ? metadata.updateAuthority != publicKey?.toBase58() : false; useEffect(() => { if (mint) { setValue("mint", mint as string); } }, [mint, router, setValue]); useEffect(() => { if (metadata && data && image && !(isSubmitting || loading)) { setValue("name", metadata?.data.name); setValue("symbol", metadata?.data.symbol); setValue("description", data?.description || ""); setValue("externalUrl", data?.external_url || ""); (async () => { const imageFile = await getFileFromUrl(image, "image"); if (imageFile) setValue("image", imageFile); })(); } }, [setValue, metadata, data, image]); const onSubmit = async (values: IEditMetadataFormProps) => { await execute(tokenMetadataSdk!, values); onComplete && onComplete(new PublicKey(values.mint)); }; return ( {!connected && ( )}
{name && ( {name} ({symbol}) )} setValue("mint", s)} /> { mintAcc && mintAcc.decimals == 0 && ( )} {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(); if (!blob.type.includes("image")) return undefined const fileName = `${name}${blob.type === defaultType ? ".jpeg" : ".png"}`; const file = new File([blob], fileName, { type: blob.type || defaultType }); return file; };