NFT Frame


View on Warpcast
The NFT Frame

Frames launched on Jan 26th and brought parabolic daily active user growth to the Farcaster protocol. Ever since then, people have done all sorts of unique things with frames, including minting NFTs. I was curious how it worked. I had seen links to platforms like Manifold and Zora, and I had also seen mint buttons that didn't take you to an external site but just let you mint an NFT right from the feed. I was trying to wrap my head around this so I decided to build one.

The mfer NFTs

I bought my first mfer in the summer of 2022 and have accumulated a few since then. I always loved that they were cc0. I would use them to make silly memes of random crypto concepts I was thinking about. I would post them on Twitter and they helped grow my account there. When I was thinking about it, I realized I could use these memes as the art for a frame NFT project.

The Contract

Now that I had the art, I needed the contract. All I needed was a simple ERC721 contract that had a mint function, and a function to get the remaining NFT's left to mint. These contracts are readily available from Open Zepellin, or you can ask Chat GPT to write a contract for you.

The Frame

I had a total of 15 NFTs I wanted to mint. I wanted the frame to let the user click a mint button directly in the timeline, and I would mint an NFT to their Farcaster connected wallet. There were a few steps to make this happen. First, I made a gif that cycled through the 15 NFTs that would display above the mint button on the initial frame. Then, I needed to create the functionality in my API to actually mint the NFT to the correct address. When the user clicks the button from a client, a payload is sent to my API which includes the user's fid (Farcaster id). I parsed the payload for the fid, and then made a GET request to get the user data from Farcaster, example. So now you have the address where the NFT needs to go. How do I know which NFT to send?

Managing NFTs from the API

First thing to do is deploy the contract to a network. I chose Base for the low gas fees. I compiled my contract in the Remix web IDE and deployed it to Base after connecting my Metamask wallet. In my API, I used the ethers package to interact with my contract. To do this, I needed the contract ABI. This can be copied from Remix after you compile your contract. It basically is an interface that outlines the methods available on your contract. Then I needed a JSON RPC Provider URL, which I got from my free Alchemy account. And to be able to mint the NFTs from my contract, I needed to do that from the owner of the contract. So I needed to create a wallet instance using ethers with my Metamask private key. Lastly, I needed the contract address, which I also got from Remix after I deployed it. Then you can use ethers to create an instance of the contract from the owners wallet. You'll end up with something like this:

const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(
const contract = new ethers.Contract(
Now you have your contract and you can interact with it, calling any function you want. Now I'm ready to mint something, but what? I uploaded my NFTs to Pinata, and a corresponding json file with their title and description. Then I created an array of all 15 ipfs hashes from each json file. In my API, I called a function on my contract to see how many are remaining to mint of the 15.
const remaining = await contract.getRemainingNFTs();
const remainingInt = parseInt(remaining);
Once I knew how many were left, I could then do what I needed to. If there were zero left, I displayed a minted out graphic, which I got from mferGPT.
The button I returned was a link to the collection in this case. Another case I wanted to consider was if the user had already minted 2, to not let them mint again. The ERC721 standard makes a method available to check the balance of tokens by address. So in my Javascript I did this:
const balance = await contract.balanceOf(ethAddress);
If this number was greater than or equal to 2, I displayed a 2 per wallet graphic.
If the user had not minted 2, I called the mint NFT function and minted to their address.
const nftToMint = NFT_HASHES[remainingInt - 1];
const tokenURI = `ipfs://${nftToMint}`;
const mintTx = await contract.mintNFT(ethAddress, tokenURI);
You can see here, that I first get the hash of the NFT I want to mint based on the remaining number of NFTs left. Then I create the tokenURI, which is the ipfs link to the json file that describes the NFT. Then I call the mintNFT function on my contract, passing the user's address and the tokenURI. If successful, I would return a success screen.
The last scenario was if there was an error. I wrapped all of the functionality around a try catch block, and if there was an error, I returned an error screen.

© 2024