Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
maxwellfortney committed Oct 6, 2021
0 parents commit e92a127
Show file tree
Hide file tree
Showing 23 changed files with 6,333 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
NEXT_PUBLIC_CANDY_MACHINE_CONFIG=
NEXT_PUBLIC_CANDY_MACHINE_ID=
NEXT_PUBLIC_TREASURY_ADDRESS=
NEXT_PUBLIC_CANDY_START_DATE=

NEXT_PUBLIC_SOLANA_NETWORK=
NEXT_PUBLIC_SOLANA_RPC_HOST=
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
34 changes: 34 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local

# vercel
.vercel
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.

[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.

The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
52 changes: 52 additions & 0 deletions components/WalletConnection/WalletConnectionProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";
import {
ConnectionProvider,
WalletProvider,
} from "@solana/wallet-adapter-react";
import { WalletModalProvider } from "@solana/wallet-adapter-react-ui";
import {
getLedgerWallet,
getPhantomWallet,
getSlopeWallet,
getSolflareWallet,
getSolletWallet,
getSolletExtensionWallet,
getTorusWallet,
} from "@solana/wallet-adapter-wallets";
import { clusterApiUrl } from "@solana/web3.js";
import { FC, useMemo } from "react";

const WalletConnectionProvider: FC = ({ children }) => {
// Can be set to 'devnet', 'testnet', or 'mainnet-beta'
const network = process.env
.NEXT_PUBLIC_SOLANA_NETWORK as WalletAdapterNetwork;

const endpoint = useMemo(() => clusterApiUrl(network), [network]);

const wallets = useMemo(
() => [
getPhantomWallet(),
getSlopeWallet(),
getSolflareWallet(),
getTorusWallet({
options: {
clientId: "CLIENT_ID",
},
}),
getLedgerWallet(),
getSolletWallet({ network }),
getSolletExtensionWallet({ network }),
],
[network]
);

return (
<ConnectionProvider endpoint={endpoint}>
<WalletProvider wallets={wallets} autoConnect>
<WalletModalProvider>{children}</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
);
};

export default WalletConnectionProvider;
251 changes: 251 additions & 0 deletions hooks/useCandyMachine.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
import { useEffect, useState } from "react";
import * as anchor from "@project-serum/anchor";
import {
awaitTransactionSignatureConfirmation,
CandyMachine,
getCandyMachineState,
mintMultipleToken,
mintOneToken,
} from "../utils/candyMachine";
import toast from "react-hot-toast";
import { useWallet } from "@solana/wallet-adapter-react";
import useWalletBalance from "./useWalletBalance";
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
import { sleep } from "../utils";

const MINT_PRICE_SOL = 1;

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 [nftsData, setNftsData] = useState<any>(
({} = {
itemsRemaining: 0,
itemsRedeemed: 0,
itemsAvailable: 0,
} as any)
);
const [isMinting, setIsMinting] = useState(false);
const [isSoldOut, setIsSoldOut] = useState(false);
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 } =
await getCandyMachineState(anchorWallet, candyMachineId, connection);

setIsSoldOut(itemsRemaining === 0);
setMintStartDate(goLiveDate);
setCandyMachine(candyMachine);
})();
}, [wallet, candyMachineId, connection]);

useEffect(() => {
(async () => {
if (!isMinting) {
const anchorWallet = {
publicKey: wallet.publicKey,
signAllTransactions: wallet.signAllTransactions,
signTransaction: wallet.signTransaction,
} as anchor.Wallet;

const { itemsRemaining, itemsRedeemed, itemsAvailable } =
await getCandyMachineState(anchorWallet, candyMachineId, connection);

setNftsData({ itemsRemaining, itemsRedeemed, itemsAvailable });
}
})();
}, [wallet, candyMachineId, connection, isMinting]);

const startMint = 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! Check on your wallet :)"
);
} 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);
}
};

const startMintMultiple = async (quantity: number) => {
try {
setIsMinting(true);
if (wallet.connected && candyMachine?.program && wallet.publicKey) {
const oldBalance =
(await connection.getBalance(wallet?.publicKey)) / LAMPORTS_PER_SOL;
const futureBalance = oldBalance - MINT_PRICE_SOL * quantity;

const signedTransactions: any = await mintMultipleToken(
candyMachine,
config,
wallet.publicKey,
treasury,
quantity
);

const promiseArray = [];

for (let index = 0; index < signedTransactions.length; index++) {
const tx = signedTransactions[index];
promiseArray.push(
awaitTransactionSignatureConfirmation(
tx,
txTimeout,
connection,
"singleGossip",
true
)
);
}

const allTransactionsResult = await Promise.all(promiseArray);
let totalSuccess = 0;
let totalFailure = 0;

for (let index = 0; index < allTransactionsResult.length; index++) {
const transactionStatus = allTransactionsResult[index];
if (!transactionStatus?.err) {
totalSuccess += 1;
} else {
totalFailure += 1;
}
}

let newBalance =
(await connection.getBalance(wallet?.publicKey)) / LAMPORTS_PER_SOL;

while (newBalance > futureBalance) {
await sleep(1000);
newBalance =
(await connection.getBalance(wallet?.publicKey)) / LAMPORTS_PER_SOL;
}

if (totalSuccess) {
toast.success(
`Congratulations! ${totalSuccess} mints succeeded! Your NFT's should appear in your wallet soon :)`,
{ duration: 6000, position: "bottom-center" }
);
}

if (totalFailure) {
toast.error(
`Some mints failed! ${totalFailure} mints failed! Check on your wallet :(`,
{ duration: 6000, position: "bottom-center" }
);
}
}
} 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,
nftsData,
startMint,
startMintMultiple,
};
}
Loading

0 comments on commit e92a127

Please sign in to comment.