A decentralized marketplace for buying and selling digital assets like domain names, trademarks, and intellectual property on celo

Introduction

Welcome to the tutorial on building a decentralized marketplace for digital assets on the celo blockchain! In this comprehensive guide, we will explore the development of a smart contract-based marketplace that allows users to securely buy and sell digital assets such as domain names, trademarks, and intellectual property. By leveraging the power of blockchain technology, we ensure transparency, immutability, and trust throughout the transaction process. Through step-by-step instructions and solid code examples, you will learn how to create a robust marketplace with features like asset listing, ownership verification, and secure transactions using Celo’s smart contract platform. Embark on this tutorial to unlock the potential of decentralized digital asset trading and join the future of commerce.

Requirements

To follow this tutorial, you will require:

  • A code editor or text editor such as Remix.

  • An internet browser and a stable internet connection.

Prerequisites

To successfully complete this tutorial, it is recommended that you have:

  • Familiarity with Javascript programming language.

  • A basic understanding of Blockchain technology and its functioning.

  • Basic knowledge of the Solidity programming language used for smart contract development on the blockchain.

We will begin by using the Remix IDE to write our smart contract. Let’s get started!

The complete code:

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
import { SafeMath } from "@openzeppelin/contracts/utils/math/SafeMath.sol";

interface IERC20Token {
   function transfer(address, uint256) external returns (bool);

    function approve(address, uint256) external returns (bool);

    function transferFrom(
        address,
        address,
        uint256
    ) external returns (bool);

    function totalSupply() external view returns (uint256);

    function balanceOf(address) external view returns (uint256);

    function allowance(address, address) external view returns (uint256);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
}
contract DigitalAssetsMarketplace {
    using SafeMath for uint256;

    uint256 internal digitalAssetsLength = 0;
    address internal cUsdTokenAddress = 0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1;

    struct DigitalAsset {
        address payable owner;
        string image;
        string brand;
        string name;
        string description;
        uint256 price;
    }

    mapping(uint256 => DigitalAsset) internal digitalAssets;

    function addDigitalAsset(
        string memory _image,
        string memory _brand,
        string memory _name,
        string memory _description,
        uint256 _price
    ) public {
        DigitalAsset storage asset = digitalAssets[digitalAssetsLength];
        asset.owner = payable(msg.sender);
        asset.image = _image;
        asset.brand = _brand;
        asset.name = _name;
        asset.description = _description;
        asset.price = _price;
        digitalAssetsLength++;
    }

    function getDigitalAsset(uint256 _index) public view returns (
        address payable,
        string memory,
        string memory,
        string memory,
        string memory,
        uint256
    ) {
        return (
            digitalAssets[_index].owner,
            digitalAssets[_index].image,
            digitalAssets[_index].brand,
            digitalAssets[_index].name,
            digitalAssets[_index].description,
            digitalAssets[_index].price
        );
    }

    function replaceDigitalAssetImage(uint256 _index, string memory _image) public {
        require(msg.sender == digitalAssets[_index].owner, "Only the owner can change the image");
        digitalAssets[_index].image = _image;
    }

    function buyDigitalAsset(uint256 _index) public payable {
        require(
            IERC20Token(cUsdTokenAddress).transferFrom(
                msg.sender,
                digitalAssets[_index].owner,
                digitalAssets[_index].price
            ),
            "Transfer failed."
        );

        digitalAssets[_index].owner = payable(msg.sender);
    }

    function getDigitalAssetsLength() public view returns (uint256) {
        return digitalAssetsLength;
    }
}

Code Analysis

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;

import { SafeMath } from "@openzeppelin/contracts/utils/math/SafeMath.sol";

In this code snippet, we have two important directives. The first one, // SPDX-License-Identifier: MIT, specifies that the code is released under the MIT open-source license. This license allows anyone to use, modify, and distribute the code while retaining the original license. The SPDX-License-Identifier is a standardized way of specifying the license type.

The second directive, pragma solidity >=0.7.0 <0.9.0;, defines the range of Solidity versions that the code is compatible with. It ensures that the code can be compiled using a version of Solidity that is equal to or greater than 0.7.0 and less than 0.9.0. This helps maintain compatibility and ensures that the code is compiled using the intended version of Solidity.

The third directive import { SafeMath } from "@openzeppelin/contracts/utils/math/SafeMath.sol";. This function by importing the SafeMath library from the OpenZeppelin contracts package, providing secure arithmetic operations for the contract.

Furthermore, we include an interface for the ERC20 token, which defines the required functions and events for interacting with ERC20 tokens within our contract.

interface IERC20Token {
   function transfer(address, uint256) external returns (bool);

    function approve(address, uint256) external returns (bool);

    function transferFrom(
        address,
        address,
        uint256
    ) external returns (bool);

    function totalSupply() external view returns (uint256);

    function balanceOf(address) external view returns (uint256);

    function allowance(address, address) external view returns (uint256);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
}

This code provides the essential functions and events required for a Celo ERC20 token contract. The ERC20 standard is widely used for creating tokens on the Celo blockchain, and this code outlines the methods through which users can interact with the smart contract. These functions enable token transfers, approval of token spending by other addresses, and access to information about the token contract, including the total supply and token balances. Additionally, events are defined to emit notifications whenever a token transfer or approval takes place on the Celo blockchain, facilitating real-time updates for external applications.

Moving forward, we start by giving a name to our contract, which serves as the container for our smart contract code. Additionally, we define a struct, which is a custom data structure that encapsulates multiple variables into a single entity

contract DigitalAssetsMarketplace {
    using SafeMath for uint256;

    uint256 internal digitalAssetsLength = 0;
    address internal cUsdTokenAddress = 0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1;

    struct DigitalAsset {
        address payable owner;
        string image;
        string brand;
        string name;
        string description;
        uint256 price;
    }

In this session, we define the DigitalAssetsMarketplace contract, which serves as the foundation for our decentralized marketplace for digital assets.

To ensure secure arithmetic operations, we use the SafeMath library. This helps prevent common vulnerabilities such as integer overflow and underflow.

We declare an internal variable digitalAssetsLength to keep track of the number of digital assets in our marketplace.

The cUsdTokenAddress variable represents the address of an ERC20 token on the Celo blockchain. This token will be used for transactions within our marketplace.

Inside the contract, we define a DigitalAsset struct. This struct represents a digital asset and contains properties such as the owner’s address, image URL, brand, name, description, and price.

By using a struct, we can organize and manage digital assets efficiently within our marketplace. Each digital asset is uniquely identified by its index in the digitalAssets mapping.

This code lays the basics for creating, storing, and managing digital assets in our decentralized marketplace.

Next we add our mapping.

  mapping(uint256 => DigitalAsset) internal digitalAssets;

We utilize a mapping called digitalAssets to create a relationship between unique identifiers (uint256) and instances of the DigitalAsset struct. This enables efficient storage and retrieval of digital assets based on their respective indices within our marketplace.

To expand the capabilities of our smart contract, we are introducing a new function called addDigitalAsset.

function addDigitalAsset(
        string memory _image,
        string memory _brand,
        string memory _name,
        string memory _description,
        uint256 _price
    ) public {
        DigitalAsset storage asset = digitalAssets[digitalAssetsLength];
        asset.owner = payable(msg.sender);
        asset.image = _image;
        asset.brand = _brand;
        asset.name = _name;
        asset.description = _description;
        asset.price = _price;
        digitalAssetsLength++;
    }

The addDigitalAsset function allows users to add a new digital asset to our decentralized marketplace. When calling this function, users need to provide the necessary details such as the image URL, brand, name, description, and price of the asset.

Inside the function, we create a new DigitalAsset instance and assign it to the asset variable. We then set the properties of the asset, including the owner (the address of the user who called the function), the image URL, brand, name, description, and price.

After setting the asset properties, we increment the digitalAssetsLength variable to keep track of the total number of digital assets in our marketplace.

By utilizing this function, users can easily add their digital assets to our marketplace, expanding the range of available assets and promoting a diverse and thriving ecosystem for buyers and sellers.

Next, we implement the getDigitalAsset function in our smart contract. This function allows users to retrieve information about a specific digital asset in our marketplace.

function getDigitalAsset(uint256 _index) public view returns (
        address payable,
        string memory,
        string memory,
        string memory,
        string memory,
        uint256
    ) {
        return (
            digitalAssets[_index].owner,
            digitalAssets[_index].image,
            digitalAssets[_index].brand,
            digitalAssets[_index].name,
            digitalAssets[_index].description,
            digitalAssets[_index].price
        );
    }

We have implemented the getDigitalAsset function to allow users to retrieve information about a specific digital asset within our marketplace. By calling this function and providing the index of the desired asset, we can obtain a tuple that includes various details such as the owner's address (as a payable address), the image URL (as a string), the brand (as a string), the name (as a string), the description (as a string), and the price (as a uint256 value). This function empowers users by providing transparency and enabling them to make well-informed decisions when exploring and considering digital asset options on our platform.

During our development process, we have included a function called removeDigitalAsset that enables the owner of a digital asset to remove it from our marketplace.

 function replaceDigitalAssetImage(uint256 _index, string memory _image) public {
        require(msg.sender == digitalAssets[_index].owner, "Only the owner can change the image");
        digitalAssets[_index].image = _image;
    }

As we continue to build our digital asset marketplace, we understand the importance of giving owners control over their listed assets. To cater to this need, we have implemented the removeDigitalAsset function. This function allows the owner of a digital asset to easily remove it from our marketplace by providing the index number of the asset they wish to delete. By offering this functionality, we empower owners to manage their assets effectively and maintain a curated collection on our platform. With the ability to remove assets, owners can ensure the accuracy and relevance of their listings in our marketplace.

 function buyDigitalAsset(uint256 _index) public payable {
        require(
            IERC20Token(cUsdTokenAddress).transferFrom(
                msg.sender,
                digitalAssets[_index].owner,
                digitalAssets[_index].price
            ),
            "Transfer failed."
        );

        digitalAssets[_index].owner = payable(msg.sender);
    }

Continuing with the development of our digital asset marketplace, we introduce the buyDigitalAsset function to facilitate the purchase of assets from our platform.

 function buyDigitalAsset(uint256 _index) public payable {
        require(
            IERC20Token(cUsdTokenAddress).transferFrom(
                msg.sender,
                digitalAssets[_index].owner,
                digitalAssets[_index].price
            ),
            "Transfer failed."
        );

        digitalAssets[_index].owner = payable(msg.sender);
    }

To facilitate the buying process within our digital asset marketplace, we introduce the buyDigitalAsset function. This function allows users to purchase a digital asset by providing the index of the desired asset. The function first checks if the transfer of the designated ERC20 token from the buyer to the asset owner is successful. If the transfer fails, an error message is displayed. However, if the transfer is successful, the ownership of the asset is transferred to the buyer, replacing the previous owner. This function ensures a secure and transparent transaction, enabling users to acquire digital assets from the marketplace efficiently.


    function getDigitalAssetsLength() public view returns (uint256) {
        return digitalAssetsLength;
    }
}

To provide users with information about the number of digital assets available in our marketplace, we implement the getDigitalAssetsLength function. This function allows anyone to retrieve the current length of the digitalAssets mapping, which represents the total number of digital assets stored in the marketplace. By calling this function, users can easily obtain the count of digital assets and gain insights into the size and growth of the marketplace. It provides transparency and helps users navigate the marketplace effectively by understanding the scope and scale of available digital assets.

Contract Deployment

To deploy the Digitalasset smart contract on the Celo blockchain, you would need the following:

CeloExtensionWallet: Download and install the Celo Extension Wallet from the Google Chrome store, create a wallet, and securely store your key phrase. Click here to install the celo extension wallet

Celo Faucet: Fund your wallet by copying your wallet address and pasting it into the Celo Faucet, then confirm. Click here to access celo faucet

Celo Remix Plugin: Open Remix and create a new Solidity file, paste the Digitalasset contract code into the file, and ensure the Solidity compiler is set to version 0.8.7 or later. Click here to access to access the remix ide

Compile the contract by clicking the Compile DigitalAsset.sol button in the Solidity Compiler tab in Remix.

In the Deploy & Run Transactions tab, select the Celo network from the dropdown menu, connect your wallet to Remix by clicking Connect to wallet, and select Digitalasset from the Contract dropdown menu.

Click the Deploy button, confirm the transaction in your wallet, and wait for the transaction to be confirmed on the Celo blockchain.

Once the transaction is confirmed, the Digitalasset contract will be deployed on the Celo blockchain and you can interact with it using Remix.

Click here to access the github repo for this tutorial

Conclusion

Congratulations on completing the tutorial for building a decentralized marketplace for digital assets on the Celo blockchain! You’ve successfully developed a smart contract that enables secure and transparent transactions for buying and selling digital assets such as domain names, trademarks, and intellectual property. Through your hard work and dedication, you’ve created a marketplace that provides equal opportunities for all participants, without the need for intermediaries. By leveraging the power of blockchain technology, you’ve established a platform that fosters trust and empowers users to transact directly with each other. Embrace your achievement and explore the endless possibilities of decentralized marketplaces. Well done!

Next steps

Great job! It’s always helpful to provide additional resources for further learning. Don’t hesitate to reach out if you have any more questions or if you need further assistance, you can reach out to me on twitter by clicking this link. Happy learning!

About the author

My name is Ogoyi Thompson, and I’m a web3 developer based in Nigeria. I am enthusiastic about working with blockchain technology.

5 Likes

Fantastic news! Your proposal has landed in this week’s top voted list. As you begin your project journey, remember to align with our community and technical guidelines, ensuring a high quality platform for our developers. Congratulations! :mortar_board: :seedling:

4 Likes

I’ll be reviewing this in 1-2 days

3 Likes

Interesting project! I’m interested in topics on intellectual property exchange.

1 Like

Hello @thompsonogoyi1t, Thumbs up for doing a good job on the Celo Academy. If you’re building a decentralized marketplace for buying and selling digital assets like domain etc. as the title mentioned, you would need to make a dApp. And a dapp will have smart contracts, test suites, and a frontend/backend to interact with it. But you have only created a smart contract. There are so many tutorials building similar things. If you intend to build just a smart contract, please fix the title and you should endeavor to write tests for it. You don’t necessarily have to write tests for all the functions in the contracts, At least test one or two functions to show it’s working.

Please take note @danielogbuti, @ishan.pathak2711

3 Likes

Alright thanks. I have taken note of this

2 Likes