16 min read
Before you start this guideβ
This guide will give you an in-depth walkthrough of how to deploy an NFT collection on Solana using Sugar (Candy Machine) and Umi (a Metaplex JS protocol for Solana). If youβd like to accomplish this task quicker and leave the heavy lifting to us, we recommend the Crossmint NFT Mint API [mainnet] add-on. By using the Crossmint NFT Mint API [mainnet], you can avoid having to create a Candy Machine. Use the NFT Mint API to create collections, reduce up-front costs, and mint NFTs easily!
Try the Crossmint NFT Mint API [mainnet].
Overviewβ
Are you ready to launch your NFT collection on Solana? Metaplex's Candy Machine CLI, Sugar, is the tool for you to launch in no time. If you have used previous versions of Candy Machine, you will notice that Sugar has a few significant improvements:
- improved upload performance
- simplified UX
- improved error handling
What You Will Doβ
In this guide, you will create a new wallet using Solana CLI, airdrop some SOL, and deploy a Candy Machine on Solana's devnet using Sugar. You'll then deploy your Candy Machine to the web using the Solana dApp Scaffold and Metaplex's new modular JS framework, Umi.
What You Will Needβ
- Node.js installed (version 16.15 or higher)
- Solana CLI installed
- Phantom Wallet or Solflare extension installed
- Metaplex Sugar installed (Latest Version, V2.x+ ) (installation instructions below)
- A text editor
- npm or yarn installed (We will use yarn to initialize our project and install the necessary packages. Feel free to use npm instead if that's your preferred package manager.)
Set Up Your Projectβ
Create a new project directory in your terminal with the following:
mkdir sugar-demo
cd sugar-demo
Install Sugarβ
During installation, you will be asked which version you want to use. V1.x is for Candy Machine v2, V2.x is for Candy Machine v3. This guide assumes you are using V2.x. (Candy Machine v3). If you are using an older version, check out our guide here.
Mac Installationβ
In your terminal, enter:
bash <(curl -sSf https://sugar.metaplex.com/install.sh)
Note: You may need to restart your terminal after installation
Windows Installationβ
- Download the Windows installer here.
- Run the installer as an Administrator (Right Click-->Run as Administrator). If you get a warning about an untrusted binary, try clicking More Info and then Run Anyway. If you do not have this option, follow these steps:
- Right-click on the executable file and go to Properties.
- If you trust the Metaplex developer team, check the Unblock button as shown in the image below. This will allow you to run this binary on your computer since Microsoft does not trust it automatically.
- Click Apply and Ok.
- Run the installer again
If all works correctly, you should see something like this:
You should be able to verify your sugar installation by entering sugar --version
in your terminal. You should see something like this:
QuickNode % sugar --version
sugar-cli 2.5.0
Set Up a New Walletβ
One of the cool new features of Sugar is that it will allow you to set your wallet and RPC configs using Solana CLI so that you don't need to re-enter them in each of your Sugar commands.
We first need to create a new wallet for specifically devnet testing with the following command:
solana-keygen new --no-bip39-passphrase --outfile ./wallet.json
We can confirm that the wallet we just generated is the wallet that the Solana CLI will use by running the following:
solana config set --keypair ./wallet.json
Note: we are using a relative path to our wallet in this example as we will not be changing directories, but you may include a full path if you prefer.
Establish a Connection to Your QuickNode RPCβ
Connect to a Solana Cluster with Your QuickNode Endpoint
To build on Solana, you'll need an API endpoint to connect with the network. You're welcome to use public nodes or deploy and manage your own infrastructure; however, if you'd like 8x faster response times, you can leave the heavy lifting to us.
QuickNode Now Accepts Solana Payments π
You can now pay for a QuickNode plan using USDC on Solana. As the first multi-chain provider to accept Solana payments, we're streamlining the process for developers β whether you're creating a new account or managing an existing one. Learn more about paying with Solana here.
See why over 50% of projects on Solana choose QuickNode and sign up for a free account here. We're going to use a Solana Devnet endpoint.
Copy the HTTP Provider link:
solana config set --url YOUR_QUICKNODE_URL
Now to fund your wallet, you can run the command:
solana airdrop 1
If the command is successful, you should see something like this:
You can also run solana balance
in your terminal and see that the 1 SOL is there.
Prepare NFT Assetsβ
If you have used Candy Machine before, this process will be familiar. We must create a .json file corresponding to each digital asset using a simple number format starting with 0 and increasing sequentially, never skipping a number (e.g., 0.json maps to 0.png, then 1.json maps to 1.png). We can also create an optional collection.json and collection.png to enable Sugar to create an on-chain collection automatically.
Metaplex has a sample set of assets that we can download and work with to be consistent. You can edit the json to include whichever values you would like, as long as it is compliant with the URI json schema.
Download the sample set and extract the contents to ./assets/
in your project directory (the default directory that Sugar will look for your files).
Configure Candy Machineβ
Create a new file, config.json
, in your root project folder:
echo > config.json
Open the file and paste this config:
{
"number": 10,
"symbol": "NB",
"sellerFeeBasisPoints": 500,
"isMutable": true,
"isSequential": false,
"ruleSet": null,
"creators": [
{
"address": "YOUR_WALLET_ADDRESS",
"share": 100
}
],
"uploadMethod": "bundlr",
"awsS3Bucket": null,
"retainAuthority": true,
"awsConfig": null,
"nftStorageAuthToken": null,
"shdwStorageAccount": null,
"pinataConfig": null,
"hiddenSettings": null,
"guards": {
"default": {
"solPayment": {
"value": 0.01,
"destination": "YOUR_WALLET_ADDRESS"
},
"startDate": {
"date": "2022-10-23T20:00:00Z"
}
}
}
}
Make sure to replace YOUR_WALLET_ADDRESS
with the wallet address you created earlier.
NOTE: you can run solana address in your terminal to obtain the wallet address that you just created.
Now, we should be all set. If you're following along to this point, you should have a directory structure like this:
sugar-demo/
βββ wallet.json
βββ config.json
βββ assets/
βββ [0-9].png
βββ [0-9].json
βββ collection.png
βββ collection.json
Sugar has a built-in validation tool that will let us check for errors before proceeding. In the terminal, run:
sugar validate
Note: You may see a warning, "missing properties.category
". This is fine as the attribute is not included in the Metaplex sample files.
We're in business! Nice job. Let's build our Candy Machine.
Create a Candy Machineβ
Because we have set our RPC and wallet using Solana CLI and saved our assets
and config.json
to Sugar's default directories, our commands will be pretty simple!
Upload Your Assetsβ
In your terminal, enter:
sugar upload
You should see something like this:
Deploy Candy Machineβ
In your terminal, enter:
sugar deploy
You should see something like this:
If you get a "Blockhash not found" error, try rerunning the command.
Make sure to store the Candy Machine ID provided in your terminal locally. We will need this later.
Verify Candy Machineβ
Let's make sure everything has worked as we'd expected. In your terminal, enter:
sugar verify
You should see something like this:
Great work!
Test Your Candy Machineβ
Try minting an NFT using sugar. In your terminal, enter:
sugar mint
You should see something like this:
Add Your Candy Guardsβ
By default, when you deploy your Candy Machine, only you can mint NFTs. You must implement candy guards to define the criteria by which others can mint your NFTs (e.g., start time, payment amount, payment token, whitelist tokens, etc.). Since we already defined our guards in our config.json
, we can run the following:
sugar guard add
Excellent! Let's create a mint page to share this project with the world!
Set Up a Minting Siteβ
For a speedy deployment, we will be using the Solana dApp Scaffold, a handy tool that includes a Next.JS app with Solana Wallet Adapter already integrated.
Clone the dApp Scaffoldβ
From your project directory, in your terminal, enter:
git clone https://github.com/solana-labs/dapp-scaffold ./candy-machine-ui/
cd candy-machine-ui
Install Dependenciesβ
Install the included dependencies. In your terminal, enter:
yarn
We will need a few additional metaplex packages to get our minting site up and running. In your terminal, enter:
yarn add @metaplex-foundation/mpl-candy-machine@alpha @metaplex-foundation/mpl-token-metadata@alpha @metaplex-foundation/mpl-toolbox @metaplex-foundation/umi @metaplex-foundation/umi-bundle-defaults @metaplex-foundation/umi-signer-wallet-adapters
Create a .env Fileβ
Create a file, .env
in the candy-machine-ui folder:
echo > .env
Add the following values in the new .env
file (replacing the values with your own):
NEXT_PUBLIC_RPC=<YOUR_QUICKNODE_URL>
NEXT_PUBLIC_CANDY_MACHINE_ID=<YOUR_CANDY_MACHINE_PUBKEY>
NEXT_PUBLIC_TREASURY=<YOUR_WALLET_ADDRESS>
If you don't remember your Candy Machine ID, you should be able to find it in cache.json
in the program.candyMachine
field.
With all that information plugged in, you can save the file.
Add Mint NFT Buttonβ
We must add a button to our minting site to allow users to mint an NFT. First, duplicate the RequestAirdrop
component and rename it to CandyMint
. In your terminal, copy the file by entering the following:
cp ./src/components/RequestAirdrop.tsx ./src/components/CandyMint.tsx
Open CandyMint.tsx
and change the exported component name from RequestAirdrop
to CandyMint
. You should also change the text in the button from <span>Airdrop 1 </span>
to <span>Mint NFT </span>
.
Finally, let's update our constant declarations and clear the contents of the onClick
function, so we have a clean slate to work with. Your file should look like this:
//...Default Imports (we will replace these later)
export const CandyMint: FC = () => {
// π Update these constant declarations
const { connection } = useConnection();
const wallet = useWallet();
const { getUserSOLBalance } = useUserSOLBalanceStore();
// TODO - Create an Umi instance
// π Update this onClick function
const onClick = useCallback(async () => {
if (!publicKey) {
console.log('error', 'Wallet not connected!');
notify({ type: 'error', message: 'error', description: 'Wallet not connected!' });
return;
}
// TODO - Add minting logic here
}, []);
return (
<div className="flex flex-row justify-center">
<div className="relative group items-center">
<div className="m-1 absolute -inset-0.5 bg-gradient-to-r from-indigo-500 to-fuchsia-500
rounded-lg blur opacity-20 group-hover:opacity-100 transition duration-1000 group-hover:duration-200 animate-tilt"></div>
<button
className="px-8 m-2 btn animate-pulse bg-gradient-to-br from-indigo-500 to-fuchsia-500 hover:from-white hover:to-purple-300 text-black"
onClick={onClick}
>
<span>Mint NFT </span>
</button>
</div>
</div>
);
};
We need to add the CandyMint
component to our Home View. Open ./sugar-demo/candy-machine-ui/src/views/home/index.tsx
and add the following import statement:
// Components
import { RequestAirdrop } from '../../components/RequestAirdrop';
// π Add this line
import { CandyMint } from '../../components/CandyMint';
And add the CandyMint
component to the HomeView
component:
<RequestAirdrop />
{/* π Add this line */}
<CandyMint />
Great job! You should now have a new button, but before we test it, we need to add mint functionality. Let's do that.
Add Mint Functionalityβ
Return to ./sugar-demo/candy-machine-ui/src/components/CandyMint.tsx
and replace the default imports with the following:
import { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { FC, useCallback, useMemo } from 'react';
import { notify } from "../utils/notifications";
import useUserSOLBalanceStore from '../stores/useUserSOLBalanceStore';
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
import { generateSigner, transactionBuilder, publicKey, some } from '@metaplex-foundation/umi';
import { fetchCandyMachine, mintV2, mplCandyMachine, safeFetchCandyGuard } from "@metaplex-foundation/mpl-candy-machine";
import { walletAdapterIdentity } from '@metaplex-foundation/umi-signer-wallet-adapters';
import { mplTokenMetadata } from '@metaplex-foundation/mpl-token-metadata';
import { setComputeUnitLimit } from '@metaplex-foundation/mpl-toolbox';
import { clusterApiUrl } from '@solana/web3.js';
import * as bs58 from 'bs58';
// These access the environment variables we defined in the .env file
const quicknodeEndpoint = process.env.NEXT_PUBLIC_RPC || clusterApiUrl('devnet');
const candyMachineAddress = publicKey(process.env.NEXT_PUBLIC_CANDY_MACHINE_ID);
const treasury = publicKey(process.env.NEXT_PUBLIC_TREASURY);
Create a Umi Instanceβ
Let's create a memoized Umi instance using our QuickNode Endpoint and the mplCandyMachine and mplTokenMetadata plugins. We will use this to mint our NFTs. Replace // TODO - Create Umi instance
with:
const umi = useMemo(() =>
createUmi(quicknodeEndpoint)
.use(walletAdapterIdentity(wallet))
.use(mplCandyMachine())
.use(mplTokenMetadata()),
[wallet, mplCandyMachine, walletAdapterIdentity, mplTokenMetadata, quicknodeEndpoint, createUmi]
);
This will create a Umi instance that uses our wallet connected via the wallet adapter, our QuickNode endpoint, and a couple of Metaplex plugins.
Add Minting Logicβ
Let's update our onClick
function to mint an NFT using Umi's transaction builder. Replace the onClick
function with the following code, and then we will walk through what is happening:
const onClick = useCallback(async () => {
if (!wallet.publicKey) {
console.log('error', 'Wallet not connected!');
notify({ type: 'error', message: 'error', description: 'Wallet not connected!' });
return;
}
// Fetch the Candy Machine.
const candyMachine = await fetchCandyMachine(
umi,
candyMachineAddress,
);
// Fetch the Candy Guard.
const candyGuard = await safeFetchCandyGuard(
umi,
candyMachine.mintAuthority,
);
try {
// Mint from the Candy Machine.
const nftMint = generateSigner(umi);
const transaction = await transactionBuilder()
.add(setComputeUnitLimit(umi, { units: 800_000 }))
.add(
mintV2(umi, {
candyMachine: candyMachine.publicKey,
candyGuard: candyGuard?.publicKey,
nftMint,
collectionMint: candyMachine.collectionMint,
collectionUpdateAuthority: candyMachine.authority,
mintArgs: {
solPayment: some({ destination: treasury }),
},
})
);
const { signature } = await transaction.sendAndConfirm(umi, {
confirm: { commitment: "confirmed" },
});
const txid = bs58.encode(signature);
console.log('success', `Mint successful! ${txid}`)
notify({ type: 'success', message: 'Mint successful!', txid });
getUserSOLBalance(wallet.publicKey, connection);
} catch (error: any) {
notify({ type: 'error', message: `Error minting!`, description: error?.message });
console.log('error', `Mint failed! ${error?.message}`);
}
}, [wallet, connection, getUserSOLBalance, umi, candyMachineAddress, treasury]);
Let's walk through what is happening here:
- First, we check to make sure the wallet is connected. If it is not, we notify the user and return.
- Next, we fetch the Candy Machine and Candy Guard using the
fetchCandyMachine
andsafeFetchCandyGuard
functions from the mpl-candy-machine package. We need these for our minting transaction. - Next, we generate a signer using Umi's
generateSigner
function. This will be used to mint our NFT. - Next, we create a transaction using Umi's
transactionBuilder
function. This will be used to mint our NFT:
- We add a
setComputeUnitLimit
instruction to the transaction. This safety measure prevents the transaction from failing due to exceeding the compute unit limit. - We add a
mintV2
instruction to the transaction. This is the instruction that will mint our NFT. We pass our umi instance and an object with the following properties:candyMachine
: The candy machine's public key.candyGuard
: The candy guard's public key.nftMint
: The signer we generated earlier.collectionMint
: The collection mint's public key.collectionUpdateAuthority
: The collection update authority's public key.mintArgs
: An object containing the mint arguments associated with your Candy Guard (this may be different depending on your Candy Guard). For thesolPayment
guard, we must pass thedestination
of the payment (the treasury's public key).
- We send the transaction and notify the user of the result. Note that umi returns transaction signatures as a Unit8Array, so we must encode it to base58 before displaying it to the user.
- Finally, we call
getUserSOLBalance
(a store action built into the scaffold) to update the user's SOL balance.
Nice job! Our site should now be able to mint NFTs.
Mint an NFTβ
Now that we have added the minting logic, let's start and test the site. In your terminal, run the following:
yarn dev
This will open up a browser at localhost:3000, where you can connect your wallet and have the ability to mint an NFT. Confirm that your Phantom Wallet is set to devnet and not mainnet before proceeding.
You'll need devnet SOL in your Phantom wallet to mint an NFT. You can use Solana CLI to airdrop to your Phantom wallet by entering this command in your terminal:
solana airdrop 1 YOUR_PHANTOM_WALLET_ADDRESS
Once you're ready, click "Mint". If done successfully, you should see a website like this:
If you see an error that says βMint Failed,β you might not have enough funds. Try again once you have added funds. You can view the NFT in your wallet after purchasing. Phantom may take a moment before rendering the NFT in your wallet. Ours looks like this:
That's some sweet sugar! Nice job.
Conclusionβ
Congrats! You created a Candy Machine using Metaplex Sugar and Umi. You now have all the tools needed to run your own NFT mint. We're excited to see what NFTs you're creating! Join us on Discord or reach out to us via Twitter to share your NFT project.
We β€οΈ Feedback!
Let us know if you have any feedback or requests for new topics. We'd love to hear from you.