Build an NFT marketplace with Polygon and Flutter
As the Web3 space gains more momentum, more amazing use cases and applications are rolling out in multiple spaces such as Defi, gaming, DAOs, and more. It is important that the user experience remains great along with the complex infrastructure and protocols. Currently, most Dapps can only be built with javascript-powered libraries and frameworks as most of the web3 ecosystem is built on Javascript.
The current problem that Web3 is facing is accessibility to a wide range of devices especially mobile, because of a lack of tooling. That is where Flutter being an awesome UI framework comes to the rescue.
Although Flutter can power the mobile web3 movement, it does lack a mature ecosystem of tooling to interact with blockchain data and perform required operations. Luckily, as we wait for this ecosystem to grow, there are a few awesome tools that can get the job done.
A bit about Polygon
Polygon is an EVM-compatible layer 2 protocol for Ethereum, what this essentially means is, instead of Ethereum doing all the hard work of processing transactions, polygon uses its proof-of-stake mechanism that makes transactions faster and much cheaper and then groups clusters of transactions before sending them back to Ethereum, essentially acting as a commit chain.
Although this article won’t be going in-depth about blockchain and solidity basics, feel free to get started with some of the resources mentioned here.
A bit more about NFTs
NFTs or Non-Fungible Tokens are a blockchain standard that has a set of rules implemented that allow it has a unique identity on the blockchain which makes it atomic and non-replicable. Although ERC 721 is a golden standard there is a new popular standard called the ERC 1155 that allows the developer to create both ERC 20 and 721 tokens enabling less gas fees and managing a class of assets together.
Let’s dive into the code
Truffle init
Truffle is a tool for bootstrapping, testing, and deploying contracts. It helps us manage our contracts easily, there are of course many other popular tools such as hardhat and foundry. make sure to install truffle if you have not before using npm i -g truffle
and create an environment using truffle init
which will generate a standard set of folders and configuration files. We can start coding our contract in the /Contracts
folder by creating a new file with the .sol
extension.
NFT.sol
Let’s start by creating a contract that will implement the ERC721 token standard. this will help us mint the token itself as well as store metadata about the NFT that will resolve into a JSON, this is typically stored in decentralized storage like Filecoin and IPFS. we will go about this in the next part when we integrate everything with Flutter as most of it is done from the client-side.
The ERC721URIStorage will help us store the URI of metadata of each NFT. Counters
here, is a simple util for tracking the id of each token, which we can use to declare the _tokenIds
variable. We then create a newItemId
using the current pointer for the _tokenIds
variable.
We then have a set of methods that are accessed from the inherited contract. Within our own contract, we have only one method createToken
that will be used to min the NFT.
Finally, we call _mint
with the sender address and the newItemId
to mint the token, next _setTokenUri
is used to set the actual metadata associated with the NFT. We need call setApproveForAll
with our market address so that our market contract can have the permission to transfer ownership to users.
Market.sol
Next, let’s create a market contract where all of the NFT information, such as buyer and seller will be stored, we will need this information to enable NFT transfers and handle other functions such as displaying sold items on the frontend.
Let’s initialize two Counters
, _itemIds
and _itemsSold
to keep track of totalItems. we also define owner
and listingPrice
, this is the fee that users have to pay to the owner of the contract for listing their NFTs after minting them. The struct MarketItem
will define the shape of each NFT item in the market, and mapping
of the itemId
to the MarketItem
will help us easily fetch the item by id.
createMarketItem
is a simple function that will push a new-minted NFT into the mapping that we created earlier with all the properties that are required to carry out transactions. We add two validations as require
statements to make sure the price is greater than 0 and equal to the listing price, nonReentrarnt will help us prevent multiple calls or rate-limit the contract call.
createMarketSale
will help in transferring the ownership as well as funds between the contract and the user who bought it. we fetch and extract the required data such as price
and itemId
to initiate the transfer.
we can call transfer
directly on the seller address, as the seller is a payable address
as we defined in the struct
. The IERC721
will help up make the actual transfer of the NFT, we have to provide the nftContract
address, seller, buyer address, and the actual tokenId
. We also change the ownership of the MarketItem
to the user who bought the item.
And finally, increment the _itemsSold
and transfer the listingPrice
to the owner of the contract as a fee.
We finally have to write a bunch of methods to perform read operations for specific data that we require to show in the frontend.
fetchMarketItems
gets all the marketItems which are unsold to be displayed for sale. the logic is pretty simple, we first track the unsoldItemCount
count to create an array of the total available marketItems. we then run a for loop for all marketItems and filter it out by checking if the owner
of the item is equal to the contract address and push those items into the items
array we created earlier by creating a new MarketItem
object.
We finally return all the items.
Similarly, we have fetchMyNFTs
that will return all the items that the user has purchased. We follow a similar logic as the previous function, but instead of filtering out by the contract address, we use the address of the user who called the contract. We create a new array we the calculated itemCount and then loop over the items to push owned items with their relevant tokenId
.
Following the same pursuit as the past two, we use another function fetchItemsCreated
to get all the NFTs that the user has minted. Here we compare the seller's address for each item instead of the owner's address and then return all those items.
Deploy!!
Finally, we are ready to deploy the contracts, we will just need to seup some configuration in truffle.
create a new file under the generated/migrations
folder called 2_deploy_contracts.js
We will import the contracts into the javascript file and call deployer.delpoay(contract)
we also need to call another deploy function as our NFT contract required the market Address
to set permissions for the market to transfer items to a d different address.
be sure the install the dependency by using npm i @truffle/hdwallet-provider
. Create a new file called .secret
and paste the extracted mnemonic of your wallet which is funded. If you don’t have any funds yet, head over to the polygon faucet and drop some funds into your wallet by entering your wallet address.
We’ll then set a matic
key with the above details such as a rpc
link, networkId
and other parameters, we will be deploying this to the matic Mumbai testnet.
All you have to do now is cd
into the truffle directory and run truffle migrate --network matic
. And your contract will start deploying. it might take a few seconds for the blocks to be confirmed, make sure you grab both the contract addresses as we will require it to interact with the contract from our flutter app.
And, Viola!, your contract is now live on the matic Mumbai testnet. Stay tuned for the second part where we will hook up the contract with our Flutter app using the web3dart package.
Follow my socials and connect with me as I try to bring more examples of Flutter + Web3.