For Devcon 24' workshop, built by Shiv Bhonde & Eda Akturk
In this workshop, we’ll build a gasless NFT minting app using Scaffold-ETH 2. By the end, you'll have a NFT minting app that allows users to mint NFTs without paying for gas, using the Coinbase Smart Wallet.
npx create-eth@latest
Note: When creating the app, do not add extra spaces in the title.
Choose foundry as your smart contract development environment
? What solidity framework do you want to use? foundry"
Once downloaded, run your app:
yarn chain // start your local anvil env
yarn deploy // deploys a test smart contract to the local network
yarn start // start your NextJS app
Visit: http://localhost:3000
Features to explore:
Go to "https://wizard.openzeppelin.com/#erc721" to get a template for an ERC721 contract.
Create a new file called NFT.sol
in the packages/foundry/contracts
folder. Add you contract here.
Some changes to the wizard generated contract:
_nextTokenId
public so that we count remaining supplyuint256 public maxSupply = 10;
to limit the number of NFTs that can be minted.mapping(address => bool) public alreadyMinted;
function safeMint(address to) public {
require(_nextTokenId < maxSupply, "Max supply reached");
require(!alreadyMinted[to], "Already minted");
alreadyMinted[to] = true; // add this
uint256 tokenId = _nextTokenId++;
_safeMint(to, tokenId);
}
Create a new deployment script in the packages/foundry/script
folder. Check the contents of the DeployYourContract.s
and make the adjusments for the new NFT contract.
NFT nft = new NFT()
(we had removed the deployer from constructor)Add the new deployment script to Deploy.s.sol
. And deploy the contracts by running:
yarn deploy --reset
/nft
in packages/nextjs/app
and a page.tsx file inside it.We'll use DaisyUI components to build a pretty NFT minting interface. Check them out here: https://daisyui.com/components/card/
Steps:
Get max supply for the NFT collection and tokenID with the useScaffoldReadContract Hooks.
const { data: supply } = useScaffoldReadContract({
contractName: "NFT",
functionName: "maxSupply",
});
const { data: tokenID } = useScaffoldReadContract({
contractName: "NFT",
functionName: "_nextTokenId",
});
Get the NFT's left to mint.
const [remaining, setRemaining] = useState<number | null>(null);
useEffect(() => {
if (supply !== undefined) {
setRemaining(Number(supply) - (tokenID ? Number(tokenID) : 0));
}
}, [tokenID, supply]);
Create a mint button with the useScaffoldWriteContract hooks.
const { writeContractAsync: writeScaffoldContractAsync } = useScaffoldWriteContract("NFT");
<button
className="btn btn-primary"
onClick={async () => {
try {
await writeScaffoldContractAsync({
functionName: "safeMint",
args: [connectedAddress],
});
} catch (e) {
console.error("Error minting NFT:", e);
}
}}
>
Mint NFT
</button>
Create a new component to display the NFT's that the user has. Here is how the page should be: app/nft/_components/MyHoldings.tsx
. We're using the same component from the simple NFT challange of speedrunethereum. Import this into app/nft/page.tsx
Run your app to make sure all is working.
Create a link in Header.tsx
to navigate to the NFT minting page.
{
label: "NFT",
href: "/nft",
},
wagmiConnectors.ts
remove other wallet settings, and add the following:coinbaseWallet.preference = "smartWalletOnly";
const wallets = [coinbaseWallet];
useWriteContracts
, and add it to our page.tsx
// wagmi hook to batch write to multiple contracts (EIP-5792 specific)
const { writeContractsAsync } = useWriteContracts();
// add this later to read address and contract abi
const { data: NFT } = useDeployedContractInfo("NFT");
Gasless mint button
. This will use a paymaster to cover the transaction cost.<button
className="btn btn-primary w-45"
onClick={async () => {
try {
if (!NFT) return;
// TODO: update the ENV
const paymasterURL = process.env.NEXT_PUBLIC_PAYMASTER_URL;
await writeContractsAsync({
contracts: [
{
address: NFT.address,
abi: NFT.abi,
functionName: "safeMint",
args: [connectedAddress],
},
],
capabilities: {
paymasterService: {
url: paymasterURL,
},
},
});
notification.success("NFT minted");
} catch (e) {
console.error("Error minting NFT:", e);
}
}}
>
Gasless Mint
</button>
NEXT_PUBLIC_PAYMASTER_URL
.env.local
and add NEXT_PUBLIC_PAYMASTER_URL
yarn generate
to create an account for deployment..env
inside foundry with scaffold-eth-custom
yarn account
yarn deploy --network baseSepolia
scaffold.config.ts
:targetNetworks: [chains.baseSepolia],
pollingInterval: 3000,
yarn vercel:yolo
NEXT_PUBLIC_PAYMASTER_URL
and then run yarn vercel:yolo
againThere you go, a gassless nft minting app! 🚀