i tryed to remain item counter and price viewer.
See original GitHub issue\src\utils\candy-machine.ts
import * as anchor from "@project-serum/anchor";
import {
MintLayout,
TOKEN_PROGRAM_ID,
Token,
} from "@solana/spl-token";
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
export const CANDY_MACHINE_PROGRAM = new anchor.web3.PublicKey(
"cndyAnrLdpjq1Ssp1z8xxDsB8dxe7u4HL5Nxi2K5WXZ"
);
const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID = new anchor.web3.PublicKey(
"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
);
const TOKEN_METADATA_PROGRAM_ID = new anchor.web3.PublicKey(
"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
);
export interface CandyMachine {
id: anchor.web3.PublicKey,
connection: anchor.web3.Connection;
program: anchor.Program;
}
interface CandyMachineState {
candyMachine: CandyMachine;
itemsAvailable: number;
itemsRedeemed: number;
itemsRemaining: number;
goLiveDate: Date,
price: number,
}
export const awaitTransactionSignatureConfirmation = async (
txid: anchor.web3.TransactionSignature,
timeout: number,
connection: anchor.web3.Connection,
commitment: anchor.web3.Commitment = "recent",
queryStatus = false
): Promise<anchor.web3.SignatureStatus | null | void> => {
let done = false;
let status: anchor.web3.SignatureStatus | null | void = {
slot: 0,
confirmations: 0,
err: null,
};
let subId = 0;
status = await new Promise(async (resolve, reject) => {
setTimeout(() => {
if (done) {
return;
}
done = true;
console.log("Rejecting for timeout...");
reject({ timeout: true });
}, timeout);
try {
subId = connection.onSignature(
txid,
(result: any, context: any) => {
done = true;
status = {
err: result.err,
slot: context.slot,
confirmations: 0,
};
if (result.err) {
console.log("Rejected via websocket", result.err);
reject(status);
} else {
console.log("Resolved via websocket", result);
resolve(status);
}
},
commitment
);
} catch (e) {
done = true;
console.error("WS error in setup", txid, e);
}
while (!done && queryStatus) {
// eslint-disable-next-line no-loop-func
(async () => {
try {
const signatureStatuses = await connection.getSignatureStatuses([
txid,
]);
status = signatureStatuses && signatureStatuses.value[0];
if (!done) {
if (!status) {
console.log("REST null result for", txid, status);
} else if (status.err) {
console.log("REST error for", txid, status);
done = true;
reject(status.err);
} else if (!status.confirmations) {
console.log("REST no confirmations for", txid, status);
} else {
console.log("REST confirmation for", txid, status);
done = true;
resolve(status);
}
}
} catch (e) {
if (!done) {
console.log("REST connection error: txid", txid, e);
}
}
})();
await sleep(2000);
}
});
//@ts-ignore
if (connection._signatureSubscriptions[subId]) {
connection.removeSignatureListener(subId);
}
done = true;
console.log("Returning status", status);
return status;
}
/* export */ const createAssociatedTokenAccountInstruction = (
associatedTokenAddress: anchor.web3.PublicKey,
payer: anchor.web3.PublicKey,
walletAddress: anchor.web3.PublicKey,
splTokenMintAddress: anchor.web3.PublicKey
) => {
const keys = [
{ pubkey: payer, isSigner: true, isWritable: true },
{ pubkey: associatedTokenAddress, isSigner: false, isWritable: true },
{ pubkey: walletAddress, isSigner: false, isWritable: false },
{ pubkey: splTokenMintAddress, isSigner: false, isWritable: false },
{
pubkey: anchor.web3.SystemProgram.programId,
isSigner: false,
isWritable: false,
},
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{
pubkey: anchor.web3.SYSVAR_RENT_PUBKEY,
isSigner: false,
isWritable: false,
},
];
return new anchor.web3.TransactionInstruction({
keys,
programId: SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
data: Buffer.from([]),
});
}
export const getCandyMachineState = async (
anchorWallet: anchor.Wallet,
candyMachineId: anchor.web3.PublicKey,
connection: anchor.web3.Connection,
): Promise<CandyMachineState> => {
const provider = new anchor.Provider(connection, anchorWallet, {
preflightCommitment: "recent",
});
const idl = await anchor.Program.fetchIdl(
CANDY_MACHINE_PROGRAM,
provider
);
const program = new anchor.Program(idl, CANDY_MACHINE_PROGRAM, provider);
const candyMachine = {
id: candyMachineId,
connection,
program,
}
const state: any = await program.account.candyMachine.fetch(candyMachineId);
console.log('state.data', state.data);
const itemsAvailable = state.data.itemsAvailable.toNumber();
const itemsRedeemed = state.itemsRedeemed.toNumber();
const itemsRemaining = itemsAvailable - itemsRedeemed;
const price = state.data.price.toNumber() * 0.000000001;
let goLiveDate = state.data.goLiveDate.toNumber();
goLiveDate = new Date(goLiveDate * 1000);
return {
candyMachine,
itemsAvailable,
itemsRedeemed,
itemsRemaining,
goLiveDate,
price,
};
}
const getMasterEdition = async (
mint: anchor.web3.PublicKey
): Promise<anchor.web3.PublicKey> => {
return (
await anchor.web3.PublicKey.findProgramAddress(
[
Buffer.from("metadata"),
TOKEN_METADATA_PROGRAM_ID.toBuffer(),
mint.toBuffer(),
Buffer.from("edition"),
],
TOKEN_METADATA_PROGRAM_ID
)
)[0];
};
const getMetadata = async (
mint: anchor.web3.PublicKey
): Promise<anchor.web3.PublicKey> => {
return (
await anchor.web3.PublicKey.findProgramAddress(
[
Buffer.from("metadata"),
TOKEN_METADATA_PROGRAM_ID.toBuffer(),
mint.toBuffer(),
],
TOKEN_METADATA_PROGRAM_ID
)
)[0];
};
const getTokenWallet = async (
wallet: anchor.web3.PublicKey,
mint: anchor.web3.PublicKey
) => {
return (
await anchor.web3.PublicKey.findProgramAddress(
[wallet.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()],
SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID
)
)[0];
};
export const mintOneToken = async (
candyMachine: CandyMachine,
config: anchor.web3.PublicKey,
payer: anchor.web3.PublicKey,
treasury: anchor.web3.PublicKey,
): Promise<string> => {
const mint = anchor.web3.Keypair.generate();
const token = await getTokenWallet(payer, mint.publicKey);
const { connection, program } = candyMachine;
const metadata = await getMetadata(mint.publicKey);
const masterEdition = await getMasterEdition(mint.publicKey);
const rent = await connection.getMinimumBalanceForRentExemption(
MintLayout.span
);
return await program.rpc.mintNft({
accounts: {
config,
candyMachine: candyMachine.id,
payer: payer,
wallet: treasury,
mint: mint.publicKey,
metadata,
masterEdition,
mintAuthority: payer,
updateAuthority: payer,
tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
tokenProgram: TOKEN_PROGRAM_ID,
systemProgram: anchor.web3.SystemProgram.programId,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
},
signers: [mint],
instructions: [
anchor.web3.SystemProgram.createAccount({
fromPubkey: payer,
newAccountPubkey: mint.publicKey,
space: MintLayout.span,
lamports: rent,
programId: TOKEN_PROGRAM_ID,
}),
Token.createInitMintInstruction(
TOKEN_PROGRAM_ID,
mint.publicKey,
0,
payer,
payer
),
createAssociatedTokenAccountInstruction(
token,
payer,
payer,
mint.publicKey
),
Token.createMintToInstruction(
TOKEN_PROGRAM_ID,
mint.publicKey,
token,
payer,
[],
1
),
],
});
}
export const shortenAddress = (address: string, chars = 4): string => {
return `${address.slice(0, chars)}...${address.slice(-chars)}`;
};
const sleep = (ms: number): Promise<void> => {
return new Promise((resolve) => setTimeout(resolve, ms));
}`
`
\src\hooks\use-candy-machine.ts
import { useEffect, useState } from "react";
import * as anchor from "@project-serum/anchor";
import { awaitTransactionSignatureConfirmation, CandyMachine, getCandyMachineState, mintOneToken } from "../utils/candy-machine";
import { useWallet } from "@solana/wallet-adapter-react";
import toast from 'react-hot-toast';
import useWalletBalance from "./use-wallet-balance";
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
const treasury = new anchor.web3.PublicKey(
process.env.NEXT_PUBLIC_TREASURY_ADDRESS!
);
const config = new anchor.web3.PublicKey(
process.env.NEXT_PUBLIC_CANDY_MACHINE_CONFIG!
);
const candyMachineId = new anchor.web3.PublicKey(
process.env.NEXT_PUBLIC_CANDY_MACHINE_ID!
);
const rpcHost = process.env.NEXT_PUBLIC_SOLANA_RPC_HOST!;
const connection = new anchor.web3.Connection(rpcHost);
const txTimeout = 30000;
export default function useCandyMachine() {
const [, setBalance] = useWalletBalance()
const [candyMachine, setCandyMachine] = useState<CandyMachine>();
const wallet = useWallet();
const [isMinting, setIsMinting] = useState(false); // true when user got to press MINT
const [isSoldOut, setIsSoldOut] = useState(false); // true when items remaining is zero
const [counter, setCounter] = useState<any>({});
const [price, setPrice] = useState<number | null>(null);
const [mintStartDate, setMintStartDate] = useState(new Date(parseInt(process.env.NEXT_PUBLIC_CANDY_START_DATE!, 10)));
useEffect(() => {
(async () => {
if (
!wallet ||
!wallet.publicKey ||
!wallet.signAllTransactions ||
!wallet.signTransaction
) {
return;
}
const anchorWallet = {
publicKey: wallet.publicKey,
signAllTransactions: wallet.signAllTransactions,
signTransaction: wallet.signTransaction,
} as anchor.Wallet;
const { candyMachine, goLiveDate, itemsRemaining, itemsAvailable, price } =
await getCandyMachineState(
anchorWallet,
candyMachineId,
connection
);
setIsSoldOut(itemsRemaining === 0);
setMintStartDate(goLiveDate);
setCandyMachine(candyMachine);
setCounter({
itemsRemaining,
itemsAvailable
});
setPrice(price);
})();
}, [wallet, candyMachineId, connection]);
const onMint = async () => {
try {
setIsMinting(true);
if (wallet.connected && candyMachine?.program && wallet.publicKey) {
const mintTxId = await mintOneToken(
candyMachine,
config,
wallet.publicKey,
treasury
);
const status = await awaitTransactionSignatureConfirmation(
mintTxId,
txTimeout,
connection,
"singleGossip",
false
);
if (!status?.err) {
toast.success("Congratulations! Mint succeeded!")
} else {
toast.error("Mint failed! Please try again!")
}
}
} catch (error: any) {
let message = error.msg || "Minting failed! Please try again!";
if (!error.msg) {
if (error.message.indexOf("0x138")) {
} else if (error.message.indexOf("0x137")) {
message = `SOLD OUT!`;
} else if (error.message.indexOf("0x135")) {
message = `Insufficient funds to mint. Please fund your wallet.`;
}
} else {
if (error.code === 311) {
message = `SOLD OUT!`;
setIsSoldOut(true);
} else if (error.code === 312) {
message = `Minting period hasn't started yet.`;
}
}
toast.error(message)
} finally {
if (wallet?.publicKey) {
const balance = await connection.getBalance(wallet?.publicKey);
setBalance(balance / LAMPORTS_PER_SOL);
}
setIsMinting(false);
}
};
return { isSoldOut, mintStartDate, isMinting, onMint }
}`
`
\src\pages\mint.tsx
import Head from 'next/head'
import { Navbar } from '../components/Navbar';
import { useState } from "react";
import { Toaster } from 'react-hot-toast';
import { useWallet } from "@solana/wallet-adapter-react";
import {
shortenAddress,
} from "../utils/candy-machine";
import useCandyMachine from '../hooks/use-candy-machine';
import useWalletBalance from '../hooks/use-wallet-balance';
import Countdown from 'react-countdown';
import { WalletMultiButton } from '@solana/wallet-adapter-react-ui';
const Mint = () => {
const [balance] = useWalletBalance()
const [isActive, setIsActive] = useState(false); // true when countdown completes
const wallet = useWallet();
const [counter, setCounter] = useState<any>({});
const [price, setPrice] = useState<number | null>(null);
const { isSoldOut, mintStartDate, isMinting, onMint } = useCandyMachine()
return (
<main className="">
<Toaster />
<Head>
<title>Solana Candy Machine</title>
<meta name="description" content="Solana Candy Machine is an open-source project using NextJS,
Metaplex protocol which serve as an example app for a NFT candy machine app." />
<link rel="icon" href="/favicon.ico" />
</Head>
<Navbar />
<div>
<br />
{!!counter && (
<>
Items available: {counter.itemsRemaining} / {counter.itemsAvailable}
<br />
<br />
<br />
<br />
</>
)}
</div>
<div className="flex justify-center">
{wallet.connected &&
<button className="px-8 py-1.5 rounded bg-blue-400 text-white font-bold hover:bg-blue-600 hover:text-white"
disabled={isSoldOut || isMinting || !isActive}
onClick={onMint}
>
{isSoldOut ? (
"SOLD OUT"
) : isActive ?
<span>MINT {price} Sol {isMinting && ""}</span> :
<Countdown
date={mintStartDate}
onMount={({ completed }) => completed && setIsActive(true)}
onComplete={() => setIsActive(true)}
renderer={renderCounter}
/>
}
</button>
}
</div>
</main>
);
};
const renderCounter = ({ days, hours, minutes, seconds, completed }: any) => {
return (
<span>
{hours} hours, {minutes} minutes, {seconds} seconds
</span>
);
};
export default Mint;
`
but not work...
Issue Analytics
- State:
- Created 2 years ago
- Comments:6 (4 by maintainers)
Top Results From Across the Web
How to use Chrome's price tracking feature for the best deals
Using Chrome's price tracker · Open Chrome on your desktop or Android device. · Head to any online shop. Note: We'll use Best...
Read more >14 Retail Checkout Counter Ideas to Try in Your Store - Vend
Allocate some space in your checkout area for items that people can quickly buy on their way out. With the right approach, you...
Read more >The 6 best habit tracker apps in 2022 | Zapier
We considered more than 40 of the most popular habit tracking apps—a lot fell short, but after extensive testing, here are the best...
Read more >Python Collections Counter - DigitalOcean
Python Counter class is part of Collections module. Counter is a subclass of Dictionary and used to keep track of elements and their...
Read more >5 Negotiating Strategies When Selling Your Home
1. Counter at Your List Price · 2. Reject the Offer · 3. Try to Create a Bidding War · 4. Put an...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
i solve the problem.
return { isSoldOut, mintStartDate, isMinting, onMint, counter, price } }
haha…
sorry about my low skill english 😃
this repo duplicate use-candymachine and home.tsx very confuse. but i love tailwind ui
@Cyncra Just updated the main branch. You should be able to run it now, with the start date and everything working, by running
yarn setup-dev
beforeyarn dev
.