From 6553b42c20124886f98f11d996f4bb15ee847f32 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 15 Sep 2021 14:41:08 -0400 Subject: [PATCH] add support for custom spl mints --- src/Home.tsx | 63 +++++++++++++++++++++++++++++++++++--------- src/candy-machine.ts | 14 +++++++++- 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/src/Home.tsx b/src/Home.tsx index 3e7407b93..c66392c9a 100644 --- a/src/Home.tsx +++ b/src/Home.tsx @@ -6,7 +6,8 @@ import Alert from "@material-ui/lab/Alert"; import * as anchor from "@project-serum/anchor"; -import { LAMPORTS_PER_SOL } from "@solana/web3.js"; +import {LAMPORTS_PER_SOL, PublicKey} from "@solana/web3.js"; +import {TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, Token } from "@solana/spl-token"; import { useWallet } from "@solana/wallet-adapter-react"; import { WalletDialogButton } from "@solana/wallet-adapter-material-ui"; @@ -41,6 +42,8 @@ const Home = (props: HomeProps) => { const [isActive, setIsActive] = useState(false); // true when countdown completes const [isSoldOut, setIsSoldOut] = useState(false); // true when items remaining is zero const [isMinting, setIsMinting] = useState(false); // true when user got to press MINT + const [tokenMint, setTokenMint] = useState(undefined); // Custom spl token for mint price. Typically undefined + const [associatedTokenAccountAddress, setAssociatedTokenAccountAddress] = useState(undefined); // Associated token for custom spl mint const [alertState, setAlertState] = useState({ open: false, @@ -53,6 +56,31 @@ const Home = (props: HomeProps) => { const wallet = useWallet(); const [candyMachine, setCandyMachine] = useState(); + const updateBalance = async () => { + if (wallet?.publicKey) { + if (tokenMint && associatedTokenAccountAddress) { + const token = new Token( + props.connection, + tokenMint, + TOKEN_PROGRAM_ID, + // @ts-ignore + wallet + ) + const mintInfo = await token.getMintInfo(); + try { + const associatedTokenAccountInfo = await token.getAccountInfo(associatedTokenAccountAddress); + setBalance(associatedTokenAccountInfo.amount.toNumber() / 10 ** mintInfo.decimals); + } catch (e) { + // if we cant fatch associated address, assume balance is 0 + setBalance(0); + } + return; + } + const balance = await props.connection.getBalance(wallet.publicKey); + setBalance(balance / LAMPORTS_PER_SOL); + } + } + const onMint = async () => { try { setIsMinting(true); @@ -61,7 +89,8 @@ const Home = (props: HomeProps) => { candyMachine, props.config, wallet.publicKey, - props.treasury + props.treasury, + associatedTokenAccountAddress ); const status = await awaitTransactionSignatureConfirmation( @@ -111,22 +140,16 @@ const Home = (props: HomeProps) => { severity: "error", }); } finally { - if (wallet?.publicKey) { - const balance = await props.connection.getBalance(wallet?.publicKey); - setBalance(balance / LAMPORTS_PER_SOL); - } + await updateBalance(); setIsMinting(false); } }; useEffect(() => { (async () => { - if (wallet?.publicKey) { - const balance = await props.connection.getBalance(wallet.publicKey); - setBalance(balance / LAMPORTS_PER_SOL); - } + await updateBalance(); })(); - }, [wallet, props.connection]); + }, [wallet, props.connection, tokenMint]); useEffect(() => { (async () => { @@ -145,15 +168,29 @@ const Home = (props: HomeProps) => { signTransaction: wallet.signTransaction, } as anchor.Wallet; - const { candyMachine, goLiveDate, itemsRemaining } = + const { candyMachine, goLiveDate, itemsRemaining, tokenMint } = await getCandyMachineState( anchorWallet, props.candyMachineId, props.connection ); + + setIsSoldOut(itemsRemaining === 0); setStartDate(goLiveDate); + + if (tokenMint) { + const associatedTokenAccountAddress = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + tokenMint, + wallet.publicKey + ); + setTokenMint(tokenMint); + setAssociatedTokenAccountAddress(associatedTokenAccountAddress); + } + setCandyMachine(candyMachine); })(); }, [wallet, props.candyMachineId, props.connection]); @@ -165,7 +202,7 @@ const Home = (props: HomeProps) => { )} {wallet.connected && ( -

Balance: {(balance || 0).toLocaleString()} SOL

+

Balance: {(balance || 0).toLocaleString()} {tokenMint ? "" : "SOL"}

)} diff --git a/src/candy-machine.ts b/src/candy-machine.ts index c8af65dee..a4702b3ce 100644 --- a/src/candy-machine.ts +++ b/src/candy-machine.ts @@ -29,7 +29,8 @@ interface CandyMachineState { itemsAvailable: number; itemsRedeemed: number; itemsRemaining: number; - goLiveDate: Date, + goLiveDate: Date; + tokenMint?: anchor.web3.PublicKey; } export const awaitTransactionSignatureConfirmation = async ( @@ -176,6 +177,7 @@ export const getCandyMachineState = async ( const itemsAvailable = state.data.itemsAvailable.toNumber(); const itemsRedeemed = state.itemsRedeemed.toNumber(); const itemsRemaining = itemsAvailable - itemsRedeemed; + const tokenMint = state.tokenMint; let goLiveDate = state.data.goLiveDate.toNumber(); goLiveDate = new Date(goLiveDate * 1000); @@ -186,6 +188,7 @@ export const getCandyMachineState = async ( itemsRedeemed, itemsRemaining, goLiveDate, + tokenMint }; } @@ -237,6 +240,7 @@ export const mintOneToken = async ( config: anchor.web3.PublicKey, // feels like this should be part of candyMachine? payer: anchor.web3.PublicKey, treasury: anchor.web3.PublicKey, + associatedTokenAccountAddress?: anchor.web3.PublicKey, ): Promise => { const mint = anchor.web3.Keypair.generate(); const token = await getTokenWallet(payer, mint.publicKey); @@ -248,6 +252,13 @@ export const mintOneToken = async ( MintLayout.span ); + const remainingAccounts = []; + if (associatedTokenAccountAddress) { + console.log("here", associatedTokenAccountAddress); + remainingAccounts.push({ pubkey: associatedTokenAccountAddress, isWritable: true, isSigner: false }); + remainingAccounts.push({ pubkey: payer, isWritable: false, isSigner: true }); + } + return await program.rpc.mintNft({ accounts: { config, @@ -266,6 +277,7 @@ export const mintOneToken = async ( clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, }, signers: [mint], + remainingAccounts, instructions: [ anchor.web3.SystemProgram.createAccount({ fromPubkey: payer,