Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error #208

Open
wants to merge 20 commits into
base: pull-request-template
Choose a base branch
from
Open

Error #208

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
REACT_APP_CANDY_MACHINE_CONFIG=__PLACEHOLDER__
REACT_APP_CANDY_MACHINE_ID=__PLACEHOLDER__
REACT_APP_TREASURY_ADDRESS=__PLACEHOLDER__
REACT_APP_CANDY_START_DATE=__PLACEHOLDER__

REACT_APP_SOLANA_NETWORK=devnet
REACT_APP_SOLANA_RPC_HOST=__PLACEHOLDER__
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
.env
.env.*
!.env.example
70 changes: 67 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,79 @@
Docs coming soon.
# Candy-Machine-Mint

In the meantime, you might find this helpful too:
https://hackmd.io/@levicook/HJcDneEWF
The Candy-Machine-Mint project is designed to let users fork, customize, and deploy their own candy machine mint app to a custom domain, ultra fast.

A candy machine is an on-chain Solana program (or smart contract) for managing fair mint. Fair mints:
* Start and finish at the same time for everyone.
* Won't accept your funds if they're out of NFTs to sell.

The Candy-Machine-Mint project is meant to be as simple and usable as possible, accessible to everyone from long-time crypto devs to junior React devs with a vague interest in NFTs. Our goal is to empower users to create their own front ends to display, sell, and manage their NFTs as simply as possible by just updating a few styled components and following a well-documented process for setup and shipping.

## Getting Set Up

### Prerequisites

* Ensure you have recent versions of both `node` and `yarn` installed.

* Follow the instructions [here](https://docs.solana.com/cli/install-solana-cli-tools) to install the Solana Command Line Toolkit.

* Follow the instructions [here](https://hackmd.io/@levicook/HJcDneEWF) to install the Metaplex Command Line Utility.
* Installing the Command Line Package is currently an advanced task that will be simplified eventually.

### Installation

1. Fork the project, then clone down. Example:
```
git clone [email protected]:exiled-apes/candy-machine-mint.git
```

2. Build the project. Example:
```
cd candy-machine-mint
yarn install
yarn build
```

3. Define your environment variables using the instructions below, and start up the server with `npm start`.

#### Environment Variables

To run the project, first rename the `.env.example` file at the root directory to `.env` and update the following variables:

```
REACT_APP_CANDY_MACHINE_CONFIG=__PLACEHOLDER__
```

This is a Solana account address. You can get the value for this from the `.cache/temp` file. This file is created when you run the `metaplex upload` command in terminal.

```
REACT_APP_CANDY_MACHINE_ID=__PLACEHOLDER__
```

Same as above; this is a Solana account address. You can get the value for this from the `./cache/temp` file. This file is created when you run the `metaplex upload` command in terminal.

```
REACT_APP_TREASURY_ADDRESS=__PLACEHOLDER__
```

This the Solana address that receives the funds gathered during the minting process. More docs coming as we can test this.

```
REACT_APP_CANDY_START_DATE=__PLACEHOLDER__
```

This is a unix time stamp that configures when your mint will be open.

```
REACT_APP_SOLANA_NETWORK=devnet
```

This identifies the Solana network you want to connect to. Options are `devnet`, `testnet`, and `mainnet`.

```
REACT_APP_SOLANA_RPC_HOST=https://explorer-api.devnet.solana.com
```

This identifies the RPC server your web app will access the Solana network through.

# Getting Started with Create React App

Expand Down
9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@
"@project-serum/anchor": "^0.14.0",
"@solana/spl-token": "^0.1.8",
"@solana/wallet-adapter-base": "^0.5.2",
"@solana/wallet-adapter-material-ui": "^0.8.3",
"@solana/wallet-adapter-react": "^0.9.1",
"@solana/wallet-adapter-react-ui": "^0.1.0",
"@solana/wallet-adapter-wallets": "^0.7.5",
"@solana/web3.js": "^1.24.1",
"@solana/wallet-adapter-material-ui": "^0.11.0",
"@solana/wallet-adapter-react": "^0.11.0",
"@solana/wallet-adapter-wallets": "^0.9.0",
"@solana/web3.js": "^1.27.0",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
Expand Down
66 changes: 51 additions & 15 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import { clusterApiUrl } from "@solana/web3.js";
import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";
import {
getPhantomWallet,
getSlopeWallet,
getSolflareWallet,
getSolletWallet,
getSolletExtensionWallet,
} from "@solana/wallet-adapter-wallets";

import {
Expand All @@ -18,6 +20,7 @@ import {
} from "@solana/wallet-adapter-react";

import { WalletDialogProvider } from "@solana/wallet-adapter-material-ui";
import { createTheme, ThemeProvider } from "@material-ui/core";

const treasury = new anchor.web3.PublicKey(
process.env.REACT_APP_TREASURY_ADDRESS!
Expand All @@ -40,29 +43,62 @@ const startDateSeed = parseInt(process.env.REACT_APP_CANDY_START_DATE!, 10);

const txTimeout = 30000; // milliseconds (confirm this works for your project)

const theme = createTheme({
palette: {
type: 'dark',
},
overrides: {
MuiButtonBase: {
root: {
justifyContent: 'flex-start',
},
},
MuiButton: {
root: {
textTransform: undefined,
padding: '12px 16px',
},
startIcon: {
marginRight: 8,
},
endIcon: {
marginLeft: 8,
},
},
},
});

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

const wallets = useMemo(
() => [getPhantomWallet(), getSolflareWallet(), getSolletWallet()],
() => [
getPhantomWallet(),
getSlopeWallet(),
getSolflareWallet(),
getSolletWallet({ network }),
getSolletExtensionWallet({ network })
],
[]
);

return (
<ConnectionProvider endpoint={endpoint}>
<WalletProvider wallets={wallets} autoConnect>
<WalletDialogProvider>
<Home
candyMachineId={candyMachineId}
config={config}
connection={connection}
startDate={startDateSeed}
treasury={treasury}
txTimeout={txTimeout}
/>
</WalletDialogProvider>
</WalletProvider>
</ConnectionProvider>
<ThemeProvider theme={theme}>
<ConnectionProvider endpoint={endpoint}>
<WalletProvider wallets={wallets} autoConnect={true}>
<WalletDialogProvider>
<Home
candyMachineId={candyMachineId}
config={config}
connection={connection}
startDate={startDateSeed}
treasury={treasury}
txTimeout={txTimeout}
/>
</WalletDialogProvider>
</WalletProvider>
</ConnectionProvider>
</ThemeProvider>
);
};

Expand Down
95 changes: 53 additions & 42 deletions src/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as anchor from "@project-serum/anchor";

import { LAMPORTS_PER_SOL } from "@solana/web3.js";

import { useWallet } from "@solana/wallet-adapter-react";
import { useAnchorWallet } from "@solana/wallet-adapter-react";
import { WalletDialogButton } from "@solana/wallet-adapter-material-ui";

import {
Expand Down Expand Up @@ -42,6 +42,10 @@ const Home = (props: HomeProps) => {
const [isSoldOut, setIsSoldOut] = useState(false); // true when items remaining is zero
const [isMinting, setIsMinting] = useState(false); // true when user got to press MINT

const [itemsAvailable, setItemsAvailable] = useState(0);
const [itemsRedeemed, setItemsRedeemed] = useState(0);
const [itemsRemaining, setItemsRemaining] = useState(0);

const [alertState, setAlertState] = useState<AlertState>({
open: false,
message: "",
Expand All @@ -50,13 +54,39 @@ const Home = (props: HomeProps) => {

const [startDate, setStartDate] = useState(new Date(props.startDate));

const wallet = useWallet();
const wallet = useAnchorWallet();
const [candyMachine, setCandyMachine] = useState<CandyMachine>();

const refreshCandyMachineState = () => {
(async () => {
if (!wallet) return;

const {
candyMachine,
goLiveDate,
itemsAvailable,
itemsRemaining,
itemsRedeemed,
} = await getCandyMachineState(
wallet as anchor.Wallet,
props.candyMachineId,
props.connection
);

setItemsAvailable(itemsAvailable);
setItemsRemaining(itemsRemaining);
setItemsRedeemed(itemsRedeemed);

setIsSoldOut(itemsRemaining === 0);
setStartDate(goLiveDate);
setCandyMachine(candyMachine);
})();
};

const onMint = async () => {
try {
setIsMinting(true);
if (wallet.connected && candyMachine?.program && wallet.publicKey) {
if (wallet && candyMachine?.program) {
const mintTxId = await mintOneToken(
candyMachine,
props.config,
Expand Down Expand Up @@ -111,65 +141,46 @@ const Home = (props: HomeProps) => {
severity: "error",
});
} finally {
if (wallet?.publicKey) {
const balance = await props.connection.getBalance(wallet?.publicKey);
if (wallet) {
const balance = await props.connection.getBalance(wallet.publicKey);
setBalance(balance / LAMPORTS_PER_SOL);
}
setIsMinting(false);
refreshCandyMachineState();
}
};

useEffect(() => {
(async () => {
if (wallet?.publicKey) {
if (wallet) {
const balance = await props.connection.getBalance(wallet.publicKey);
setBalance(balance / LAMPORTS_PER_SOL);
}
})();
}, [wallet, props.connection]);

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,
props.candyMachineId,
props.connection
);

setIsSoldOut(itemsRemaining === 0);
setStartDate(goLiveDate);
setCandyMachine(candyMachine);
})();
}, [wallet, props.candyMachineId, props.connection]);
useEffect(refreshCandyMachineState, [
wallet,
props.candyMachineId,
props.connection,
]);

return (
<main>
{wallet.connected && (
<p>Address: {shortenAddress(wallet.publicKey?.toBase58() || "")}</p>
{wallet && (
<p>Wallet {shortenAddress(wallet.publicKey.toBase58() || "")}</p>
)}

{wallet.connected && (
<p>Balance: {(balance || 0).toLocaleString()} SOL</p>
)}
{wallet && <p>Balance: {(balance || 0).toLocaleString()} SOL</p>}

{wallet && <p>Total Available: {itemsAvailable}</p>}

{wallet && <p>Redeemed: {itemsRedeemed}</p>}

{wallet && <p>Remaining: {itemsRemaining}</p>}

<MintContainer>
{!wallet.connected ? (
{!wallet ? (
<ConnectButton>Connect Wallet</ConnectButton>
) : (
<MintButton
Expand Down Expand Up @@ -222,7 +233,7 @@ interface AlertState {
const renderCounter = ({ days, hours, minutes, seconds, completed }: any) => {
return (
<CounterText>
{hours} hours, {minutes} minutes, {seconds} seconds
{hours + (days || 0) * 24} hours, {minutes} minutes, {seconds} seconds
</CounterText>
);
};
Expand Down
8 changes: 8 additions & 0 deletions src/candy-machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,21 @@ export const getCandyMachineState = async (
}

const state: any = await program.account.candyMachine.fetch(candyMachineId);

const itemsAvailable = state.data.itemsAvailable.toNumber();
const itemsRedeemed = state.itemsRedeemed.toNumber();
const itemsRemaining = itemsAvailable - itemsRedeemed;

let goLiveDate = state.data.goLiveDate.toNumber();
goLiveDate = new Date(goLiveDate * 1000);

console.log({
itemsAvailable,
itemsRedeemed,
itemsRemaining,
goLiveDate,
})

return {
candyMachine,
itemsAvailable,
Expand Down
2 changes: 2 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ body {
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: #303030;
color: #FFFFFF;
}

code {
Expand Down
Loading