Purple Dash

9 minutes

Building a Decentralized Marketplace on Flow Blockchain: An Example of a Smart Contract for Digital Asset Exchange

In this article, we will explore an example of a smart contract for a digital asset exchange marketplace on Flow.

Purple Dash
27/09/2023 8:38 AM

Table of Contents

The advent of blockchain technology has paved the way for decentralized marketplaces, where users can buy, sell, and trade digital assets without relying on intermediaries. One such blockchain that offers robust smart contract capabilities for building marketplaces is Flow. In this article, we will explore an example of a smart contract for a digital asset exchange marketplace on Flow.

Smart Contract Overview

The smart contract for the digital asset exchange marketplace on Flow can be implemented using the Cadence programming language, which is specifically designed for writing smart contracts on Flow. The smart contract will have various functions to handle different aspects of the marketplace, such as listing assets, managing orders, and handling transactions.

Here’s an overview of the main components of the smart contract:

1. NFT Listings: This smart contract allows people to list their digital collectibles, called NFTs (Non-Fungible Tokens), for sale. Each NFT has a unique identifier (ID) and a price associated with it.

2. Marketplace Vault: Think of this as a secure place where people can store their money or tokens. In our case, it’s used to hold the money that buyers pay for NFTs until the seller receives it.

3. User Roles: There are two types of users in this marketplace: regular users and administrators. Administrators have special powers, like allowing users to list their NFTs for sale.

4. Listing NFTs: To list an NFT for sale, a user needs to have a special listNftToMarketplace permission. They set the price for their NFT and make it available for others to buy.

5. Changing Prices: If a user wants to change the price of their NFT, they can do that too. They need to own the NFT and can adjust the price whenever they want.

6. Purchasing NFTs: Other users can buy the listed NFTs by paying the specified price. The money goes from the buyer to the seller, and the NFT ownership is transferred to the buyer.

7. Canceling Listings: If a user changes their mind or wants to keep their NFT, they can remove it from the marketplace.

8. Events: Whenever something important happens, like a new NFT being listed, a purchase, a price change, or a listing cancellation, the contract creates a record of it called an “event.” These events help everyone see what’s happening in the marketplace.

Example Code

This contract serves as a simplified illustration of an NFT marketplace on the Flow blockchain. It provides a fundamental framework for users to engage in the buying and selling of NFTs while incorporating essential features such as user roles, listing capabilities, and secure transaction management. It’s important to note that while this contract demonstrates key concepts, Flow recommends employing the NFT Storefront Contract standard for the development of more comprehensive and versatile marketplace solutions. The NFT Storefront Contract offers enhanced functionality and flexibility, making it the preferred choice for building generic NFT marketplaces that cater to a wide range of use cases and requirements.

Here’s an example of how the smart contract code for a simple NFT marketplace on Flow could look like in Cadence:

import FungibleToken from 0x01
import NonFungibleToken from 0x02

pub contract NFTMarketplace {

    // Resource to represent an NFT listing
    pub resource NFTListing {
        pub let owner: Address
        pub var price: UFix64

        init(owner: Address, price: UFix64) {
            self.owner = owner
            self.price = price
        }
    }

    // Event emitted when a new NFT is put up for sale
    pub event ForSale(id: UInt64, price: UFix64, seller: Address)

    // Event emitted when NFT price change detected
    pub event PriceChanged(id: UInt64, newPrice: UFix64, seller: Address)

    // Event that is emitted when an NFT is purchased
    pub event TokenPurchased(id: UInt64, price: UFix64, seller: Address, buyer: Address)

    // Mapping to store NFT listings
    pub var listings: {UInt64: NFTListing}

    // Capability to list NFTs on the marketplace
    pub var listNftToMarketplace: Capability<&{NonFungibleToken.CollectionPublic}>

    // Fungible token vault of the marketplace
    pub let marketplaceVault: @FungibleToken.Vault

    init(marketplaceVault: @FungibleToken.Vault, listNftCapability: Capability<&{NonFungibleToken.CollectionPublic}>) {
        self.listings = {}
        self.marketplaceVault = marketplaceVault
        self.listNftToMarketplace = listNftCapability
    }

    // Function to list an NFT for sale
    pub fun listForSale(tokenID: UInt64, price: UFix64) {
        let sender = getAccount(self)

        pre {
            self.listings[tokenID] == nil: "NFT is already listed for sale."
            price > 0.0: "Price must be greater than 0."
            self.listNftToMarketplace.borrow()?.exists(tokenID: tokenID) ?? false:
                "You do not have permission to list this NFT."
        }

        let listing = NFTListing(owner: sender.address, price: price)
        self.listings[tokenID] <-! listing
        emit ForSale(id: tokenID, price: price, seller: sender.address)
    }

    // Function to change the price of a listed NFT
    pub fun changePrice(tokenID: UInt64, newPrice: UFix64) {
        let sender = getAccount(self)
        let listing = self.listings[tokenID]

        pre {
            listing != nil: "NFT is not listed for sale."
            listing!.owner == sender.address: "You can only change the price of your own NFTs."
            newPrice > 0.0: "Price must be greater than 0."
        }

        listing!.price = newPrice
        emit PriceChanged(id: tokenID, newPrice: newPrice, seller: sender.address)
    }

    // Function to purchase an NFT
    pub fun purchase(tokenID: UInt64) {
        let sender = getAccount(self)
        let listing = self.listings[tokenID]

        pre {
            listing != nil: "NFT is not listed for sale."
            sender.address != listing!.owner: "You cannot buy your own NFT."
            self.marketplaceVault.balance >= listing!.price: "Insufficient funds to purchase."
        }

        // Transfer funds to the seller
        let payment = listing!.price
        self.marketplaceVault.withdraw(amount: payment)
        sender.borrow<&FungibleToken.Vault{FungibleToken.Receiver}>(from: FungibleToken.ReceiverStoragePath)
            .deposit(from: <-self.marketplaceVault)

        // Transfer ownership of the NFT
        let collection = sender.borrow<&NonFungibleToken.Collection{NonFungibleToken.Receiver}>(
            from: NonFungibleToken.CollectionStoragePath
        )
        collection.withdraw(withdrawID: tokenID)

        // Remove the listing
        destroy listing
        emit TokenPurchased(id: tokenID, price: payment, seller: listing!.owner, buyer: sender.address)
    }

    // Function to cancel a listing
    pub fun cancelListing(tokenID: UInt64) {
        let sender = getAccount(self)
        let listing = self.listings[tokenID]

        pre {
            sender.address == listing!.owner: "You can only cancel your own listings."
        }

        // Remove the listing
        destroy listing
        emit ForSale(id: tokenID, price: 0.0, seller: sender.address)
    }
}

This contract serves as a foundational structure for an NFT marketplace on the Flow blockchain. Let’s break down its functionality:

  • NFT Listing Resource (NFTListing): This resource defines the core structure of an NFT listing. It holds two critical pieces of information: the owner’s address (the person who owns the NFT) and the price at which the NFT is listed for sale.
  • Events (ForSale, PriceChanged, TokenPurchased): These events are emitted by the contract to keep track of important actions within the marketplace. They include events for when a new NFT is listed for sale, when the price of an NFT changes, when a token (NFT) is purchased, and when a listing is canceled.
  • Data Storage (listings, listNftToMarketplace, marketplaceVault): The contract uses data storage to maintain critical information. The listings mapping keeps track of all the NFT listings, associating each listing with a unique token ID. The listNftToMarketplace capability is used to control who can list NFTs on the marketplace, and the marketplaceVault holds the fungible tokens (e.g., cryptocurrency) used in transactions.
  • Initialization (init): During contract initialization, it receives essential parameters: the marketplace vault and the capability required to list NFTs on the marketplace.
  • List NFT for Sale (listForSale): This function allows users to list their NFTs for sale. Users must provide the token ID and the sale price. Before listing, the contract performs checks to ensure that the NFT is not already listed, the price is valid, and the user has the necessary permissions.
  • Change NFT Price (changePrice): Users can modify the price of their listed NFTs using this function. However, they can only change the price of NFTs they own, and the new price must be greater than zero.
  • Purchase NFT (purchase): This function enables users to buy NFTs that are listed for sale. It verifies that the NFT is indeed listed, the buyer is not trying to purchase their own NFT, and they have sufficient funds to complete the purchase. Funds are transferred from the buyer to the seller, and the NFT ownership is transferred to the buyer.
  • Cancel Listing (cancelListing): Users can cancel their own listings using this function. It removes the NFT listing from the marketplace.

Users can list their NFTs for sale, change the sale price, purchase listed NFTs, and cancel their own listings. The contract implements capability-based access control to ensure that only authorized users can perform specific actions such as listing on the marketplace, and it maintains an event log to track marketplace activities.

Flow NFT-Storefront Contract Standard

The NFT Storefront contract is a standard contract on the Flow blockchain that facilitates both the direct sales and peer-to-peer trading of Non-Fungible Tokens (NFTs). It is useful for decentralized applications that want to provide an NFT marketplace experience.

The NFT Storefront contract offers a generic process for creating a listing for an NFT and provides all the essential APIs to manage those listings independently. This contract simplifies the process of listing NFTs for sale in dApp-specific marketplaces, enabling developers to manage listings and execute NFT trades seamlessly. Some key features and functionalities of the contract standard include:

  • Flexible NFT Listings: Sellers can easily create listings for their NFTs, specifying various details like the type of NFT, sale price, and commission structure. These listings are stored within a storefront resource associated with the seller’s account.
  • Multi-Currency Support: While the contract does not directly support selling NFTs for multiple currencies within a single listing, sellers can create separate listings for the same NFT, each with a different accepted currency. This enables sellers to list their NFTs for sale in various cryptocurrencies.
  • Peer-to-Peer (p2p) Listings: Sellers have the option to create p2p listings that are not tied to any specific marketplace. These listings can be fulfilled directly between the seller and buyer, providing flexibility in how NFTs are sold.
  • Cross-Marketplace Listings: NFT listings created through a specific dApp storefront can be simultaneously listed on third-party marketplaces. Well-known marketplaces monitor compatible NFT listing events, allowing automated integration of these listings into their platforms.
  • Commission and Royalties: Sellers can define commission structures for their listings, specifying which addresses receive a portion of the sale price. This enables creators to earn royalties on secondary sales of their NFTs. Additionally, the contract supports marketplace commissions, rewarding marketplaces that facilitate sales.
  • Ghost Listings and Expiry: The contract addresses potential issues like ghost listings (listings without the underlying NFT) by providing safety measures. Sellers can specify an expiry period for listings to prevent purchases after a certain time.
  • Purchase Process: Buyers can easily purchase NFTs through the contract by providing the required payment vault and commission recipient, if applicable. The contract automates the payment of sale cuts and royalties during the purchase process.
  • APIs and Events: The contract provides a set of APIs for managing listings, including creating, removing, and querying them. Events are emitted to notify interested parties about storefront creation, destruction, listing availability, and completed listings.

Implementing NFTStorefront In Your Application

An account that wants to list NFTs for sale creates a Storefront resource and stores it in their account. This Storefront resource acts as a kind of shop or display case where the account can list individual NFTs for sale. Each of these individual sales is known as a Listing.

Typically, there is one Storefront per account, and it is stored at a specific location in the account’s storage, which is /storage/NFTStorefrontV2.

The NFTStorefrontV2 contract supports all tokens that use the NonFungibleToken standard. This setup allows for a decentralized marketplace where each account can list their unique NFTs for sale, and these listings can be accessed and purchased by other accounts on the Flow blockchain.

To implement the NFTStorefrontV2 contract standard in your application, follow the below sequence of steps:

  1. Initiate the creation and storage of the Storefront resource within the user’s account. This is accomplished through the setup_account transaction, and you can access the transaction code in the NFT Storefront repository.
  2. Proceed to generate a listing under the recently created storefront resource. If the user is already in possession of the storefront resource, they can simply utilize the existing one. Sellers have the flexibility to specify various requirements when listing their NFTs.
  3. Effectively manage listings and facilitate NFT transactions using the contract’s APIs. DApp developers can harness these APIs to oversee listings offered for sale and conduct NFT trades.
  4. Multiple sale listings for the same NFT are supported. NFT owners can create numerous sale listings on the same marketplace or even across different marketplaces.
  5. Furthermore, NFTs can be listed for sale in various fungible tokens. NFT owners can establish distinct sale listings for the same NFT, each specifying different fungible tokens as accepted payment methods. For instance, they can list the same NFT for sale using both FLOW and USDC tokens.
  6. Additionally, the contract accommodates royalty payments. Each sale listing can include multiple sale cuts, facilitating royalty disbursements to multiple entities upon a sale.

It’s crucial to note that NFTs listed for sale through the NFT Storefront contract remain under the complete control of the NFT owner until a sale occurs, and the owner can transfer the NFT at any point prior to the sale.

Conclusion

In conclusion, decentralized marketplaces represent a promising frontier for blockchain technology, and smart contracts stand as the linchpin enabling trust and automation within these ecosystems. The Flow blockchain, with its innovative approach to user-defined smart contracts and its unwavering commitment to scalability, emerges as an ideal platform for crafting decentralized marketplaces characterized by high-performance and user-friendly interactions. In this article, we’ve explored the fundamentals of smart contracts, delved into the distinctive features of the Flow blockchain, and examined a practical example of a marketplace smart contract tailored to the Flow environment.

While we’ve covered the creation, modification, and cancellation of assets, as well as order placement and fulfillment, it’s crucial to acknowledge that this is a simplified demonstration. Real-world decentralized marketplaces would invariably demand greater complexity and robustness, aligning with their specific operational needs.

When embarking on the development of smart contracts for decentralized marketplaces, meticulous attention to security protocols becomes paramount. Rigorous measures like input validation, precise access control, and comprehensive handling of edge cases are indispensable to safeguard the integrity and trustworthiness of the marketplace. Furthermore, the diligent processes of testing, auditing, and continuous monitoring of smart contract code serve as vital mechanisms to uncover and rectify any vulnerabilities or anomalies.

Tags:
Flow
Web3
Smart Contract Development
Smart Contracts
NFT Marketplaces
NFTs
DApp development
Blockchain Technology
Blockchain Networks
Cryptocurrencies
Blockchain Development
Blockchain

Purple Dash

We are a team of seasoned developers, blockchain architects, and financial experts, passionate about building cutting-edge solutions that revolutionize how businesses operate in the digital economy.


Latest Articles

Stay up-to-date with the latest industry trends and insights by reading articles from our technical experts, providing expertise on cutting-edge technologies in the crypto and fintech space.

View All