import { Alert, Box, Button, Collapse, Flex, Icon, Input, useDisclosure, VStack, useRadio, useRadioGroup, } from "@chakra-ui/react"; import { yupResolver } from "@hookform/resolvers/yup"; import { DataV2, Metadata } from "@metaplex-foundation/mpl-token-metadata"; import { NATIVE_MINT } from "@solana/spl-token"; import { useWallet } from "@solana/wallet-adapter-react"; import { Keypair, PublicKey, Signer, TransactionInstruction } from "@solana/web3.js"; import { FIXED_CURVE_FEES, MarketplaceSdk, } from "@strata-foundation/marketplace-sdk"; import { usePrimaryClaimedTokenRef, useProvider, } from "@strata-foundation/react"; import { getMintInfo, sendMultipleInstructions, } from "@strata-foundation/spl-utils"; import { useRouter } from "next/router"; import React from "react"; import { useAsyncCallback } from "react-async-hook"; import { FormProvider, useForm } from "react-hook-form"; import { BsChevronDown } from "react-icons/bs"; import * as yup from "yup"; import { useMarketplaceSdk } from "../../contexts/marketplaceSdkContext"; import { NFT_STORAGE_API_KEY } from "../../constants"; import { route, routes } from "../../utils/routes"; import { Disclosures, disclosuresSchema, IDisclosures } from "./Disclosures"; import { FormControlWithError } from "./FormControlWithError"; import { MintSelect } from "./MintSelect"; import { IMetadataFormProps, TokenMetadataInputs } from "./TokenMetadataInputs"; import { IUseExistingMintProps, UseExistingMintInputs, } from "./UseExistingMintInputs"; import { useWalletModal } from "@solana/wallet-adapter-react-ui"; import { TokenMintDecimalsInputs } from "./TokenMintDecimalsInputs"; interface IMarketplaceFormProps extends Omit, IUseExistingMintProps { mint: string; quantity: number; price: number; curve: string; decimals?: number; disclosures: IDisclosures; goLiveDate: Date; } const validationSchema = yup.object({ useExistingMint: yup.boolean(), existingMint: yup.string().when("useExistingMint", { is: true, then: yup.string().required(), }), decimals: yup .number() .nullable() .transform((v) => { return v === "" || isNaN(v) ? null : v; }) .when("useExistingMint", { is: false, then: yup.number().min(0).required(), }), mint: yup.string().required(), image: yup.mixed(), name: yup.string().when("useExistingMint", { is: false, then: yup.string().required().min(2), }), description: yup.string().when("useExistingMint", { is: false, then: yup.string().required().min(2), }), quantity: yup.number().required().min(1).integer(), price: yup.number().required().min(0), curve: yup.string(), disclosures: disclosuresSchema, goLiveDate: yup.date().required(), }); async function createMarket( marketplaceSdk: MarketplaceSdk, values: IMarketplaceFormProps ): Promise { const mint = new PublicKey(values.mint); const targetMintKeypair = Keypair.generate(); let metadata; if (values.useExistingMint) { const existingMint = new PublicKey(values.existingMint!); values.decimals = ( await getMintInfo(marketplaceSdk.provider, existingMint) ).decimals; metadata = new DataV2({ name: values.name || "", symbol: values.symbol || "", uri: values.uri || "", sellerFeeBasisPoints: 0, creators: null, collection: null, uses: null, }); } else { const uri = await marketplaceSdk.tokenMetadataSdk.uploadMetadata({ provider: values.provider, name: values.name!, symbol: "", description: values.description, image: values.image, mint: targetMintKeypair.publicKey, attributes: [ { trait_type: "is_strata_sale", display_type: "Strata Sale", value: "true", }, ], }); metadata = new DataV2({ // Max name len 32 name: values.name!.substring(0, 32), symbol: "", uri, sellerFeeBasisPoints: 0, creators: null, collection: null, uses: null, }); } const instructions: TransactionInstruction[][] = []; const signers: Signer[][] = []; const marketItemInstrs = await marketplaceSdk.createMarketItemInstructions({ targetMintKeypair, metadata, quantity: values.quantity, price: values.price, baseMint: mint, bondingArgs: { curve: values.curve ? new PublicKey(values.curve) : undefined, targetMintDecimals: values.decimals, goLiveDate: values.goLiveDate, }, }); instructions.push(...marketItemInstrs.instructions); signers.push(...marketItemInstrs.signers); let id = targetMintKeypair.publicKey; if (values.useExistingMint) { const retrievalInstrs = await marketplaceSdk.fungibleEntanglerSdk.createFungibleEntanglerInstructions( { authority: marketplaceSdk.provider.wallet.publicKey, dynamicSeed: Keypair.generate().publicKey.toBuffer(), amount: values.quantity, parentMint: new PublicKey(values.existingMint!), // swaps from childMint to parentMint childMint: targetMintKeypair.publicKey, } ); instructions.push(retrievalInstrs.instructions); signers.push(retrievalInstrs.signers); id = retrievalInstrs.output.childEntangler; } await sendMultipleInstructions( marketplaceSdk.tokenBondingSdk.errors || new Map(), marketplaceSdk.provider, instructions, signers ); return id; } export const FixedPriceForm: React.FC = () => { const formProps = useForm({ resolver: yupResolver(validationSchema), defaultValues: { useExistingMint: true, }, }); const { register, handleSubmit, setValue, formState: { errors, isSubmitting }, watch, } = formProps; const { publicKey, connected } = useWallet(); const { visible, setVisible } = useWalletModal(); const { info: tokenRef } = usePrimaryClaimedTokenRef(publicKey); const { awaitingApproval } = useProvider(); const { execute, loading, error } = useAsyncCallback(createMarket); const { marketplaceSdk } = useMarketplaceSdk(); const router = useRouter(); const { isOpen, onToggle } = useDisclosure(); const onSubmit = async (values: IMarketplaceFormProps) => { const id = await execute(marketplaceSdk!, values); router.push( route(routes.fixedPriceAdmin, { id: id.toBase58(), }), undefined, { shallow: true } ); }; const useExistingMint = watch("useExistingMint"); return ( {!connected && ( )}
{tokenRef && ( )} setValue("mint", s)} />{" "} {error && ( {error.toString()} )}
); };