Develop NFT marketplace on Flow
Flow is a decentralized and high-performing Blockchain that has been specifically designed to support the next generation of games, applications, and other kinds of digital assets. As a developer-centric Blockchain, Flow utilizes multi-role architecture models that makes the platform ideal for extensive scaling without sharding. Flow is highly popular among people interacting with NFTs like crypto-infused games, in-game items, and collectibles.
The history of Flow can be traced back to 2017 when Dapper Labs launched the Blockchain game CryptoKitties on Ethereum. CryptoKitties was a viral digital trading game that allowed users to buy, collect and breed digital cats. The sudden surge and popularity of Cryptokitties slowed Ethereum transactions while increasing gas fees to resolve such challenges. It was then that Dapper Labs decided to launch a dedicated Blockchain for crypto games and collectibles that helped overcome challenges of network congestion, slow speeds, and costly transactions. Cyrptokitties set the stage for the development of Flow that facilitated the trading of gaming collectibles and other kinds of crypto games. With NFTs gradually reaching their popularity and maturity levels, Flow serves as a successful Blockchain to create Web3 assets, NFTs, and even Decentralized Autonomous Organizations (DAOs).
Flow facilitates the trade of gaming assets and is primarily designed for a highly-scalable gaming experience. The Blockchain is fast with proof-of-stake consensus, eliminating sharding techniques and facilitating low-cost transactions. In this article, we will look into the steps required to create an NFT marketplace on Flow in detail. Right from setting up the Flow Playground to testing the first NFT transaction, the article will cover essential topics to help you create your marketplace from scratch.
- Smart Contracts on Flow
- How to develop the NFT Marketplace on Flow?
- Build an NFT Marketplace with LeewayHertz
Smart Contracts and Account State on Flow
Smart contract programming in Flow blockchain is done using Cadence. Cadence helps developers ensure that the codes used are safe, easy and secure. As a resource-oriented programming language, Cadence helps assert digital ownership by ensuring that resources and associated assets are not replicated, copied, or lost/deleted. Cadence also sets pre-conditions and post-conditions necessary for current and future Blockchain transactions.
Developers use the Flow Playground to write and deploy smart contracts. The Flow Developer Playground comprises an in-browser editor and an emulator for developers to experiment with on Flow. Before setting up your NFT marketplace on Flow, the Flow Playground comes with several features that make it easier for developers to create and execute smart contracts along with familiarizing them with Cadence on the Playground. It is important for developers to first get accustomed to the Flow Playground to understand the deployment of Cadence smart contracts without errors.
How to develop the NFT Marketplace on Flow?
To build a marketplace on Flow, it is important to integrate both fungible and non-fungible tokens into a single contract, known as ‘composable smart contract’. For developing the NFT marketplace, we have to create a composable smart contract. But before that, you have to set up the playground for marketplace development. Ensure that your fungible tokens and non-fungible token contracts are deployed and set up correctly, then follow the steps below to prepare the playground for NFT marketplace development.
Set up the Playground for Marketplace Development
After understanding the creation and deployment of smart contracts on Cadence, the following starter code in Flow Playground can help developers through the further stages of building an NFT marketplace.
// FungibleToken.cdc // The FungibleToken contract is a sample implementation of a fungible token on Flow. // Fungible tokens behave like everyday currencies -- they can be minted, transferred or // traded for digital goods. // Follow the fungible tokens tutorial to learn more: https://docs.onflow.org/docs/fungible-tokens pub contract FungibleToken { // Total supply of all tokens in existence. pub var totalSupply: UFix64 // Provider // Interface that enforces the requirements for withdrawing // tokens from the implementing type. // We don't enforce requirements on self.balance here because // it leaves open the possibility of creating custom providers // that don't necessarily need their own balance. pub resource interface Provider { // withdraw // Function that subtracts tokens from the owner's Vault // and returns a Vault resource (@Vault) with the removed tokens. // The function's access level is public, but this isn't a problem // because even the public functions are not fully public at first. // anyone in the network can call them, but only if the owner grants // them access by publishing a resource that exposes the withdraw // function. pub fun withdraw(amount: UFix64): @Vault { post { // `result` refers to the return value of the function result.balance == UFix64(amount): "Withdrawal amount must be the same as the balance of the withdrawn Vault" } } } // Receiver // Interface that enforces the requirements for depositing // tokens into the implementing type. // We don't include a condition that checks the balance because // we want to give users the ability to make custom Receivers that // can do custom things with the tokens, like split them up and // send them to different places. pub resource interface Receiver { // deposit // Function that can be called to deposit tokens // into the implementing resource type pub fun deposit(from: @Vault) } // Balance // Interface that specifies a public `balance` field for the vault pub resource interface Balance { pub var balance: UFix64 } // Vault // Each user stores an instance of only the Vault in their storage // The functions in the Vault and governed by the pre and post conditions // in the interfaces when they are called. // The checks happen at runtime whenever a function is called. // Resources can only be created in the context of the contract that they // are defined in, so there is no way for a malicious user to create Vaults // out of thin air. A special Minter resource needs to be defined to mint // new tokens. pub resource Vault: Provider, Receiver, Balance { // keeps track of the total balance of the account's tokens pub var balance: UFix64 // initialize the balance at resource creation time init(balance: UFix64) { self.balance = balance } // withdraw // Function that takes an integer amount as an argument // and withdraws that amount from the Vault. // It creates a new temporary Vault that is used to hold // the money that is being transferred. It returns the newly // created Vault to the context that called so it can be deposited // elsewhere. pub fun withdraw(amount: UFix64): @Vault { self.balance = self.balance - amount return <-create Vault(balance: amount) } // deposit // Function that takes a Vault object as an argument and adds // its balance to the balance of the owners Vault. // It is allowed to destroy the sent Vault because the Vault // was a temporary holder of the tokens. The Vault's balance has // been consumed and therefore can be destroyed. pub fun deposit(from: @Vault) { self.balance = self.balance + from.balance destroy from } } // createEmptyVault // Function that creates a new Vault with a balance of zero // and returns it to the calling context. A user must call this function // and store the returned Vault in their storage in order to allow their // account to be able to receive deposits of this token type. pub fun createEmptyVault(): @Vault { return <-create Vault(balance: 0.0) } // VaultMinter // Resource object that an admin can control to mint new tokens pub resource VaultMinter { // Function that mints new tokens and deposits into an account's vault // using their `Receiver` reference. // We say `&AnyResource{Receiver}` to say that the recipient can be any resource // as long as it implements the Receiver interface pub fun mintTokens(amount: UFix64, recipient: &AnyResource{Receiver}) { FungibleToken.totalSupply = FungibleToken.totalSupply + amount recipient.deposit(from: <-create Vault(balance: amount)) } } // The init function for the contract. All fields in the contract must // be initialized at deployment. This is just an example of what // an implementation could do in the init function. The numbers are arbitrary. init() { self.totalSupply = 30.0 // create the Vault with the initial balance and put it in storage // account.save saves an object to the specified `to` path // The path is a literal path that consists of a domain and identifier // The domain must be `storage`, `private`, or `public` // the identifier can be any name let vault <- create Vault(balance: self.totalSupply) self.account.save(<-vault, to: /storage/MainVault) // Create a new MintAndBurn resource and store it in account storage self.account.save(<-create VaultMinter(), to: /storage/MainMinter) // Create a private capability link for the Minter // Capabilities can be used to create temporary references to an object // so that callers can use the reference to access fields and functions // of the objet. // // The capability is stored in the /private/ domain, which is only // accesible by the owner of the account self.account.link<&VaultMinter>(/private/Minter, target: /storage/MainMinter) } }
With the above starter code, you can prepare the Flow Playground for the state required to build a marketplace for NFTs.
Setting up the NFT Marketplace
After having the starter code and NFT token contracts in place, the following steps will help you set up the marketplace on Flow-
- Open account 0x01. The Fungible Token definitions in ‘FungibleToken.cdc’ need to be included in this account. You can refer to the Fungible Token tutorial to ensure the same.
- The Fungible Token code needs to be deployed to account 0x01
- Select and switch to account 0x02 from the account selection menu.
- Follow the Non-fungible token tutorial to make sure you have all NFT definitions from account 0x02 in NFTv2.cdc
- Deploy the NFT code to account 0x02
Run the transaction in Transaction 3, which is the SetupAccount1Transaction.cdc file. You can use account 0x01 as the only signer to set up account 0x01’s account.
import FungibleToken from 0x01 import NonFungibleToken from 0x02 // This transaction sets up account 0x01 for the marketplace tutorial // by publishing a Vault reference and creating an empty NFT Collection. transaction { prepare(acct: AuthAccount) { // Create a public Receiver capability to the Vault acct.link<&FungibleToken.Vault{FungibleToken.Receiver, FungibleToken.Balance}> (/public/MainReceiver, target: /storage/MainVault) log("Created Vault references") // store an empty NFT Collection in account storage acct.save<@NonFungibleToken.Collection>(<-NonFungibleToken.createEmptyCollection(), to: /storage/NFTCollection) // publish a capability to the Collection in storage acct.link<&{NonFungibleToken.NFTReceiver}>(/public/NFTReceiver, target: /storage/NFTCollection) log("Created a new empty collection and published a reference") } }
Run the transaction in Transaction 4, which is the SetupAccount2Transaction.cdc file. You need to use account 0x02 as the only signer to set up account 0x02’s account.
import FungibleToken from 0x01 import NonFungibleToken from 0x02 // This transaction adds an empty Vault to account 0x02 // and mints an NFT with id=1 that is deposited into // the NFT collection on account 0x01. transaction { // Private reference to this account's minter resource let minterRef: &NonFungibleToken.NFTMinter prepare(acct: AuthAccount) { // create a new vault instance with an initial balance of 0 let vaultA <- FungibleToken.createEmptyVault() // Store the vault in the account storage acct.save<@FungibleToken.Vault>(<-vaultA, to: /storage/MainVault) // Create a public Receiver capability to the Vault let ReceiverRef = acct.link<&FungibleToken.Vault{FungibleToken.Receiver, FungibleToken.Balance}>(/public/MainReceiver, target: /storage/MainVault) log("Created a Vault and published a reference") // Borrow a reference for the NFTMinter in storage self.minterRef = acct.borrow<&NonFungibleToken.NFTMinter>(from: /storage/NFTMinter) ?? panic("Could not borrow an nft minter reference") } execute { // Get the recipient's public account object let recipient = getAccount(0x01) // Get the Collection reference for the receiver // getting the public capability and borrowing a reference from it let receiverRef = recipient.getCapability<&{NonFungibleToken.NFTReceiver}>(/public/NFTReceiver) .borrow() ?? panic("Could not borrow an nft receiver reference") // Mint an NFT and deposit it into account 0x01's collection self.minterRef.mintNFT(recipient: receiverRef) log("New NFT minted for account 1") } }
Run the transaction in Transaction 5, which is the SetupAccount1TransactionMinting.cdc file. Use account 0x01 as the only signer to mint fungible tokens for accounts 1 and 2.
import FungibleToken from 0x01 import NonFungibleToken from 0x02 // This transaction mints tokens for both accounts using // the minter stored on account 0x01. transaction { // Public Vault Receiver References for both accounts let acct1Ref: &AnyResource{FungibleToken.Receiver} let acct2Ref: &AnyResource{FungibleToken.Receiver} // Private minter references for this account to mint tokens let minterRef: &FungibleToken.VaultMinter prepare(acct: AuthAccount) { // Get the public object for account 0x02 let account2 = getAccount(0x02) // Retrieve public Vault Receiver references for both accounts self.acct1Ref = acct.getCapability<&FungibleToken.Vault{FungibleToken.Receiver}>(/public/MainReceiver) .borrow() ?? panic("Could not borrow acct1 vault receiver reference") self.acct2Ref = account2.getCapability<&FungibleToken.Vault{FungibleToken.Receiver}>(/public/MainReceiver) .borrow() ?? panic("Could not borrow acct2 vault receiver reference") // Get the stored Minter reference for account 0x01 self.minterRef = acct.borrow<&FungibleToken.VaultMinter>(from: /storage/MainMinter) ?? panic("Could not borrow vault minter reference") } execute { // Mint tokens for both accounts self.minterRef.mintTokens(amount: 20.0, recipient: self.acct2Ref) self.minterRef.mintTokens(amount: 10.0, recipient: self.acct1Ref) log("Minted new fungible tokens for account 1 and 2") } }
To ensure everything is set up properly, run the script CheckSetupScript.cdc file in Script 1.
import FungibleToken from 0x01 import NonFungibleToken from 0x02 // This script checks that the accounts are set up correctly for the marketplace tutorial. // Account 0x01: Vault Balance = 40, NFT.id = 1 // Account 0x02: Vault Balance = 20, No NFTs pub fun main() { // Get the accounts' public account objects let acct1 = getAccount(0x01) let acct2 = getAccount(0x02) // Get references to the account's receivers // by getting their public capability // and borrowing a reference from the capability let acct1ReceiverRef = acct1.getCapability<&FungibleToken.Vault{FungibleToken.Balance}>(/public/MainReceiver) .borrow<&FungibleToken.Vault{FungibleToken.Balance}>() ?? panic("Could not borrow acct1 vault receiver reference") let acct2ReceiverRef = acct2.getCapability<&FungibleToken.Vault{FungibleToken.Balance}>(/public/MainReceiver) .borrow() ?? panic("Could not borrow acct2 vault receiver reference") // Log the Vault balance of both accounts and ensure they are // the correct numbers. // Account 0x01 should have 40. // Account 0x02 should have 20. log("Account 1 Balance") log(acct1ReceiverRef.balance) log("Account 2 Balance") log(acct2ReceiverRef.balance) // verify that the balances are correct if acct1ReceiverRef.balance != 40.0 || acct2ReceiverRef.balance != 20.0 { panic("Wrong balances!") } // Find the public Receiver capability for their Collections let acct1Capability = acct1.getCapability<&{NonFungibleToken.NFTReceiver}>(/public/NFTReceiver) let acct2Capability = acct2.getCapability<&{NonFungibleToken.NFTReceiver}>(/public/NFTReceiver) // borrow references from the capabilities let nft1Ref = acct1Capability.borrow() ?? panic("Could not borrow acct1 nft receiver reference") let nft2Ref = acct2Capability.borrow() ?? panic("Could not borrow acct2 nft receiver reference") // Print both collections as arrays of IDs log("Account 1 NFTs") log(nft1Ref.getIDs()) log("Account 2 NFTs") log(nft2Ref.getIDs()) // verify that the collections are correct if nft1Ref.getIDs()[0] != 1 as UInt64 || nft2Ref.getIDs().length != 0 { panic("Wrong Collections!") } }
The following should be the output —
"Account 1 Vault Balance" 40 "Account 2 Vault Balance" 20 "Account 1 NFTs" [1] "Account 2 NFTs" []
With the above steps, you can ensure that the Flow Playground is in the correct state to create your NFT marketplace to enable the sale of NFTs between accounts.
Selling NFT on the Marketplace
Users trying to sell an NFT will store an instance of a ‘SaleCollection’ resource in the account storage. Switch to account 0x03 and open Marketplace.cdc. Select the Deploy button that appears at the bottom right of the editor within the Marketplace.cdc file. The file should then contain the following contraction-
import FungibleToken from 0x01 import NonFungibleToken from 0x02 // The Marketplace contract is a sample implementation of an NFT Marketplace on Flow. // This contract allows users to put their NFTs up for sale. Other users // can purchase these NFTs with fungible tokens. pub contract Marketplace { // Event that is emitted when a new NFT is put up for sale pub event ForSale(id: UInt64, price: UFix64) // Event that is emitted when the price of an NFT changes pub event PriceChanged(id: UInt64, newPrice: UFix64) // Event that is emitted when a token is purchased pub event TokenPurchased(id: UInt64, price: UFix64) // Event that is emitted when a seller withdraws their NFT from the sale pub event SaleWithdrawn(id: UInt64) // Interface that users will publish for their Sale collection // that only exposes the methods that are supposed to be public pub resource interface SalePublic { pub fun purchase(tokenID: UInt64, recipient: &AnyResource{NonFungibleToken.NFTReceiver}, buyTokens: @FungibleToken.Vault) pub fun idPrice(tokenID: UInt64): UFix64? pub fun getIDs(): [UInt64] } // SaleCollection // NFT Collection object that allows a user to put their NFT up for sale // where others can send fungible tokens to purchase it pub resource SaleCollection: SalePublic { // Dictionary of the NFTs that the user is putting up for sale pub var forSale: @{UInt64: NonFungibleToken.NFT} // Dictionary of the prices for each NFT by ID pub var prices: {UInt64: UFix64} // The fungible token vault of the owner of this sale. // When someone buys a token, this resource can deposit // tokens into their account. pub let ownerVault: Capability<&AnyResource{FungibleToken.Receiver}> init (vault: Capability<&AnyResource{FungibleToken.Receiver}>) { self.forSale <- {} self.ownerVault = vault self.prices = {} } // withdraw gives the owner the opportunity to remove a sale from the collection pub fun withdraw(tokenID: UInt64): @NonFungibleToken.NFT { // remove the price self.prices.remove(key: tokenID) // remove and return the token let token <- self.forSale.remove(key: tokenID) ?? panic("missing NFT") return <-token } // listForSale lists an NFT for sale in this collection pub fun listForSale(token: @NonFungibleToken.NFT, price: UFix64) { let id = Token ID // store the price in the price array self.prices[id] = price // put the NFT into the the forSale dictionary let oldToken <- self.forSale[id] <- token destroy oldToken emit ForSale(id: id, price: price) } // changePrice changes the price of a token that is currently for sale pub fun changePrice(tokenID: UInt64, newPrice: UFix64) { self.prices[tokenID] = newPrice emit PriceChanged(id: tokenID, newPrice: newPrice) } // purchase lets a user send tokens to purchase an NFT that is for sale pub fun purchase(tokenID: UInt64, recipient: &AnyResource{NonFungibleToken.NFTReceiver}, buyTokens: @FungibleToken.Vault) { pre { self.forSale[tokenID] != nil && self.prices[tokenID] != nil: "No token matching this ID for sale!" buyTokens.balance >= (self.prices[tokenID] ?? 0.0): "Not enough tokens to by the NFT!" } // get the value out of the optional let price = self.prices[tokenID]! self.prices[tokenID] = nil let vaultRef = self.ownerVault.borrow() ?? panic("Could not borrow reference to owner token vault") // deposit the purchasing tokens into the owners vault vaultRef.deposit(from: <-buyTokens) // deposit the NFT into the buyers collection recipient.deposit(token: <-self.withdraw(tokenID: tokenID)) emit TokenPurchased(id: tokenID, price: price) } // idPrice returns the price of a specific token in the sale pub fun idPrice(tokenID: UInt64): UFix64? { return self.prices[tokenID] } // getIDs returns an array of token IDs that are for sale pub fun getIDs(): [UInt64] { return self.forSale.keys } destroy() { destroy self.forSale } } // createCollection returns a new collection resource to the caller pub fun createSaleCollection(ownerVault: Capability<&AnyResource{FungibleToken.Receiver}>): @SaleCollection { return <- create SaleCollection(vault: ownerVault) } }
The marketplace contract allows users to add and remove NFTs, as well as set and remove a price. A user can put up their NFT on sale after depositing it into the collection with the listForSale function. Another user can buy the NFT by sending their Vault containing currency to make the purchase. The buyer will also include a reference to their NFT collection for the purchased token to get deposited and reflected into their collection immediately after making the purchase.
The capability that the marketplace contract stores is:
pub let ownerVault: Capability<&AnyResource{FungibleToken.Receiver}>.
The seller (owner of the sale) saves a capability to their Fungible Token Receiver within the sale, which helps deposit the currency into the owner’s vault once the NFT purchase is made.
The marketplace contract also includes events that are supported by Cadence. Cadence can be used to add or remove events within contracts when important actions/events take place.
pub event ForSale(id: UInt64, price: UFix64) pub event PriceChanged(id: UInt64, newPrice: UFix64) pub event TokenPurchased(id: UInt64, price: UFix64) pub event SaleWithdrawn(id: UInt64)
Testing and Verifying Transactions
By now, you should have a fungible token Vault and an NFT Collection in both accounts. Account 0x01 should have an NFT in their collection. Now you can create a SaleCollection and list account 0x01’s token for sale by following the steps below-
Step 1:
Open Transaction1.cdc. Select account 0x01 as the only signer and click on Send to submit the transaction
import FungibleToken from 0x01 import NonFungibleToken from 0x02 import Marketplace from 0x03 // This transaction creates a new Sale Collection object, // lists an NFT for sale, puts it in account storage, // and creates a public capability to the sale so that others can buy the token. transaction { prepare(acct: AuthAccount) { // Borrow a reference to the stored Vault let receiver = acct.getCapability<&{FungibleToken.Receiver}>(/public/MainReceiver) // Create a new Sale object, // initializing it with the reference to the owner's vault let sale <- Marketplace.createSaleCollection(ownerVault: receiver) // borrow a reference to the NFTCollection in storage let collectionRef = acct.borrow<&NonFungibleToken.Collection>(from: /storage/NFTCollection) ?? panic("Could not borrow a reference to the owner's nft collection") // Withdraw the NFT from the collection that you want to sell // and move it into the transaction's context let token <- collectionRef.withdraw(withdrawID: 1) // List the token for sale by moving it into the sale object sale.listForSale(token: <-token, price: 10.0) // Store the sale object in the account storage acct.save<@Marketplace.SaleCollection>(<-sale, to: /storage/NFTSale) // Create a public capability to the sale so that others can call its methods acct.link<&Marketplace.SaleCollection{Marketplace.SalePublic}>(/public/NFTSale, target: /storage/NFTSale) log("Sale Created for account 1. Selling NFT 1 for 10 tokens") } }
This sale transaction ensures the following-
- Creates the SalesCollection in the owner’s vault, storing their vault reference
- Gets a capability for the owner’s vault
- Withdraws the owner’s token from their collection
- Sets the price and lists the token
- Stores the sale in their account storage
- Enables other users to purchase any NFTs for sale by establishing a capability
Step 2:
You need to run a script to check if the sale has been created correctly. For this, open script2.cdc. Click on theExecutebutton to print the ID and price of thw NFT that has been put up on sale by account 0x01
import FungibleToken from 0x01 import NonFungibleToken from 0x02 import Marketplace from 0x03 // This script prints the NFTs that account 0x01 has for sale. pub fun main() { // Get the public account object for account 0x01 let account1 = getAccount(0x01) // Find the public Sale reference to their Collection let acct1saleRef = account1.getCapability<&AnyResource{Marketplace.SalePublic}>(/public/NFTSale) .borrow() ?? panic("Could not borrow a reference to the sale") // Los the NFTs that are for sale log("Account 1 NFTs for sale") log(acct1saleRef.getIDs()) log("Price") log(acct1saleRef.idPrice(tokenID: 1)) }
The Print output should look something like this:
"Account 1 NFTs for sale" [1] "Price" 10
Purchasing the NFT – Buyers
The transaction in Transaction2.cdc can be used by the buyer to purchase the seller’s NFT. Open Transaction2.cdc file and select account 0x02 as the only signer. Click the Send button.
import FungibleToken from 0x01 import NonFungibleToken from 0x02 import Marketplace from 0x03 // This transaction uses the signer's Vault tokens to purchase an NFT // from the Sale collection of account 0x01. transaction { // reference to the buyer's NFT collection where they // will store the bought NFT let collectionRef: &AnyResource{NonFungibleToken.NFTReceiver} // Vault that will hold the tokens that will be used to // but the NFT let temporaryVault: @FungibleToken.Vault prepare(acct: AuthAccount) { // get the references to the buyer's fungible token Vault // and NFT Collection Receiver self.collectionRef = acct.borrow<&AnyResource{NonFungibleToken.NFTReceiver}>(from: /storage/NFTCollection) ?? panic("Could not borrow reference to the signer's nft collection") let vaultRef = acct.borrow<&FungibleToken.Vault>(from: /storage/MainVault) ?? panic("Could not borrow reference to the signer's vault") // withdraw tokens from the buyers Vault self.temporaryVault <- vaultRef.withdraw(amount: 10.0) } execute { // get the read-only account storage of the seller let seller = getAccount(0x01) // get the reference to the seller's sale let saleRef = seller.getCapability<&AnyResource{Marketplace.SalePublic}>(/public/NFTSale) .borrow() ?? panic("could not borrow reference to the seller's sale") // purchase the NFT the the seller is selling, giving them the reference // to your NFT collection and giving them the tokens to buy it saleRef.purchase(tokenID: 1, recipient: self.collectionRef, buyTokens: <-self.temporaryVault) log("Token 1 has been bought by account 2!") } }
What the above transaction can enable-
- Gets the public account object for account 0x01
- Acquires relevant references to the buyer’s stored resources
- Withdraws buyer’s tokens to be used for NFT purchase
- Gets the reference to seller’s public sale
- Calls and executes the purchase function, passing in the tokens and Collection reference
- The Purchase function deposits the purchased NFTs directly into the buyer’s collection.
Running a Script to Verify NFT Purchase
There are two steps to verify that the NFT was purchased correctly and verify the nature of the NFT transaction.
- Open the Script3.cdc file
- Click ‘Execute’ button with ‘Script3.cdc’ containing the following code-
import FungibleToken from 0x01 import NonFungibleToken from 0x02 import Marketplace from 0x03 // This script checks that the Vault balances and NFT collections are correct // for both accounts. // Account 1: Vault balance = 50, No NFTs // Account 2: Vault balance = 10, NFT ID=1 pub fun main() { // Get the accounts' public account objects let acct1 = getAccount(0x01) let acct2 = getAccount(0x02) // Get references to the account's receivers // by getting their public capability // and borrowing a reference from the capability let acct1ReceiverRef = acct1.getCapability<&FungibleToken.Vault{FungibleToken.Balance}>(/public/MainReceiver) .borrow() ?? panic("Could not borrow reference to acct1 vault") let acct2ReceiverRef = acct2.getCapability<&FungibleToken.Vault{FungibleToken.Balance}>(/public/MainReceiver) .borrow() ?? panic("Could not borrow reference to acct2 vault") // Log the Vault balance of both accounts and ensure they are // the correct numbers. // Account 0x01 should have 50. // Account 0x02 should have 10. log("Account 1 Balance") log(acct1ReceiverRef.balance) log("Account 2 Balance") log(acct2ReceiverRef.balance) // verify that the balances are correct if acct1ReceiverRef.balance != 50.0 || acct2ReceiverRef.balance != 10.0 { panic("Wrong balances!") } // Find the public Receiver capability for their Collections let acct1Capability = acct1.getCapability<&{NonFungibleToken.NFTReceiver}>(/public/NFTReceiver) let acct2Capability = acct2.getCapability<&{NonFungibleToken.NFTReceiver}>(/public/NFTReceiver) // borrow references from the capabilities let nft1Ref = acct1Capability.borrow() ?? panic("Could not borrow reference to acct1 nft collection") let nft2Ref = acct2Capability.borrow() ?? panic("Could not borrow reference to acct2 nft collection") // Print both collections as arrays of IDs log("Account 1 NFTs") log(nft1Ref.getIDs()) log("Account 2 NFTs") log(nft2Ref.getIDs()) // verify that the collections are correct if nft2Ref.getIDs()[0] != 1 as UInt64 || nft1Ref.getIDs().length != 0 { panic("Wrong Collections!") } // Get the public sale reference for Account 0x01 let acct1SaleRef = acct1.getCapability<&AnyResource{Marketplace.SalePublic}>(/public/NFTSale) .borrow() ?? panic("Could not borrow a reference to the sale") // Print the NFTs that account 0x01 has for sale log("Account 1 NFTs for sale") log(acct1SaleRef.getIDs()) if acct1SaleRef.getIDs().length != 0 { panic("Sale should be empty!") } }
After completing the verification process, you can ensure the success of the transaction when you observe a print similar to the following-
"Account 1 Vault Balance" 50 "Account 2 Vault Balance" 10 "Account 1 NFTs" [] "Account 2 NFTs" [1] "Account 1 NFTs for Sale" []
The codes for each of the steps, i.e. setting up the marketplace, designing the marketplace, and facilitating transactions from one account to another, can help you create a simple marketplace on Cadence. You can also scale your marketplace further by using the CentralMarketplace.cdc contract.
Build an NFT Marketplace with LeewayHertz
Developing an NFT marketplace on a platform like Flow requires businesses to consider several things for their development. A good way to develop scalable marketplaces would be to partner with Blockchain experts with adequate experience and knowledge in NFT development. Building a custom NFT marketplace becomes quicker and easier with an experienced team of Blockchain experts. LeewayHertz offers NFT marketplace development services, enabling businesses to build dynamic marketplaces on Flow and other platforms and help them scale their business.
Start a conversation by filling the form
All information will be kept confidential.
Insights
How NFTs disrupt the music industry
NFTs in music provide promising opportunities for the industry and a wide range of benefits for artists and music enthusiasts. Musicians receive fair compensation for their work, and fans can directly interact with them.
How to Create a Multifunctional Fractionalized NFT?
A fractionalized NFT is an NFT broken down into smaller fractions to be sold individually. Each fraction represents a portion of the NFT’s ownership, enabling multiple people to own a single token.
What Not to Do while Building NFT Marketplaces?
NFT marketplaces are undoubtedly a profitable business. NFT marketplaces will invariably deliver desired results when developed with certain dos and don’ts in mind.