# NFT Contract Deployment Tutorial

By [4EVERLAND](https://paragraph.com/@4everland-3) · 2023-09-13

---

_This article mainly introduces how to deploy your NFT (ERC-721 Token) to a decentralized network using smart contracts._

**Initialize the Project**
==========================

    // Create an NFT for the ocean
    mkdir  nft-ocean
    
    // Enter the directory
    cd nft-ocean
    
    // Initialize the project, fill in as prompted. You can fill in the package name and description.
    npm init
    
    // Add hardhat dependency
    npm install --save-dev hardhat
    
    
    /* Using scaffolding to set up the project, we choose "Create a basic sample project."
    This will help us create a demo project and install the necessary dependencies. */
    npx hardhat
    

**The code structure will look like this after initialization:**

![](https://storage.googleapis.com/papyrus_images/d527e46deb0f37a9a3394761d4b753eceeb47073523cd6a63f4bab0c52b29e02.webp)

*   `contracts` directory is for storing smart contract code.
    
*   `scripts` directory is for storing scripts, such as those for contract deployment.
    
*   `test` directory is for storing test code for smart contracts.
    
*   `hardhat.config.js` contains some configurations about the hardhat framework, such as the Solidity version, etc.
    
*   `package.json` contains npm related configurations.
    

**Writing the Smart Contract**
==============================

You can now start writing the contract code. This tutorial mainly introduces NFT issuance, so let’s implement a simple NFT smart contract.

    // Install the openzeppelin/contracts dependency, which has built-in 
    implementations for many contract protocols and utility code.
    npm install @openzeppelin/contracts
    

Create a file named NFT\_OCEAN.sol in the contracts directory

    pragma solidity ^0.8.0;
    
    import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
    
    contract NFT_OCEAN is ERC721, ERC721Enumerable, Ownable {
        string private _baseURIextended;
        //Set mint limit
        uint256 public constant MAX_SUPPLY = 5;
        //0.01eth per mint
        uint256 public constant PRICE_PER_TOKEN = 0.01 ether;
    
        constructor() ERC721("nft_ocean", "NFT_OCEAN") {
        }
    
        function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal override(ERC721, ERC721Enumerable) {
            super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
        }
    
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC721Enumerable) returns (bool) {
            return super.supportsInterface(interfaceId);
        }
    
        function setBaseURI(string memory baseURI_) external onlyOwner() {
            _baseURIextended = baseURI_;
        }
    
        function _baseURI() internal view virtual override returns (string memory) {
            return _baseURIextended;
        }
    
        function mint(uint numberOfTokens) public payable {
            uint256 ts = totalSupply();
            require(ts + numberOfTokens <= MAX_SUPPLY, "Purchase would exceed max tokens");
            require(PRICE_PER_TOKEN * numberOfTokens <= msg.value, "Ether value sent is not correct");
    
            for (uint256 i = 0; i < numberOfTokens; i++) {
                _safeMint(msg.sender, ts + i);
            }
        }
    
        function withdraw() public onlyOwner {
            uint balance = address(this).balance;
            payable(msg.sender).transfer(balance);
        }
    }
    

This code does not meet production standards as it lacks features like whitelisting and limits on how many tokens each address can mint. It’s just for demonstration purposes. It’s recommended to design your contract carefully before issuing NFTs.

**Compile the Contract**
------------------------

    // Compile the contract
    npx hardhat compile
    

**Deploy the Contract**
=======================

Modify the auto-generated script in the `scripts` directory, rename it to `deploy.js`, and change its internal code.

    const hre = require("hardhat");
    
    async function main() {
      // Hardhat always runs the compile task when running scripts with its command
      // line interface.
      //
      // If this script is run directly using `node` you may want to call compile
      // manually to make sure everything is compiled
      // await hre.run('compile');
    
      // We get the contract to deploy
      const Nft_ocean = await hre.ethers.getContractFactory("NFT_OCEAN");
      const nft_ocean = await Nft_ocean.deploy();
    
      await nft_ocean.deployed();
    
      console.log("NFT_OCEAN deployed to:", nft_ocean.address);
    }
    
    // We recommend this pattern to be able to use async/await everywhere
    // and properly handle errors.
    main()
      .then(() => process.exit(0))
      .catch((error) => {
        console.error(error);
        process.exit(1);
      });
    

**Select node service proxy**
-----------------------------

To interact with the Ethereum blockchain, you have various options. To keep things simple, we will use a free account on Alchemy, a blockchain developer platform and API that allows us to interact with the Ethereum chain without running our own node. Refer to the [documentation](https://docs.alchemy.com/docs/choosing-a-web3-network) to create an Ethereum Mainnet and an Ethereum Sepolia App.

![](https://storage.googleapis.com/papyrus_images/d9608a644d148ef956b235dcf4d84e12541134362ca6c49a894f4807b69e2e13.webp)

**Configuring the Testnet Environment**
---------------------------------------

First off, let’s get our contract onto a test network to see it in action. For this tutorial, we’re picking Sepolia as our go-to testnet.

Now, a quick heads-up: when we’re executing our code, we’re going to be dealing with sensitive stuff like public and private keys, along with API data. It’s a no-go to hard-code these directly into our codebase for obvious security reasons. So, we’ll use the `dotenv` package to securely manage all the key data we'll need for both deploying and interacting with our contract.

    // Add dotenv dependency
    npm install dotenv
    
    // Create a file to store variables at the root of the project.
    touch .env
    

Add content to .env:

    PRIVATE_KEY= <Your MetaMask private key here>
    API= <Alchemy APP HTTPS here>
    PUBLIC_KEY= <MetaMask address here>
    NETWORK=sepolia
    API_KEY=<Alchemy API KEY here>
    

*   Exporting MetaMask Private Key: Open wallet -> Account Details -> Export Private Key.
    
*   API, API\_KEY: Open Alchemy's Ethereum Sepolia API KEY, copy both the HTTPS and API\_KEY.
    

![](https://storage.googleapis.com/papyrus_images/ee76b950895395ded9ab3f0ddd1d0b2f565b7356394e37d5108cf05512252a7f.webp)

Modify `hardhat.config.js`

    /**
    * @type import('hardhat/config').HardhatUserConfig
    */
    
    require("@nomicfoundation/hardhat-toolbox");
    require("@nomiclabs/hardhat-ethers");
    //require("@nomiclabs/hardhat-waffle");
    
    // This is a sample Hardhat task. To learn how to create your own go to
    // https://hardhat.org/guides/create-task.html
    require('dotenv').config();
    const { API, PRIVATE_KEY } = process.env;
    
    // You need to export an object to set up your config
    // Go to https://hardhat.org/config/ to learn more
    
    /** @type import('hardhat/config').HardhatUserConfig */
    module.exports = {
      solidity: "0.8.19",
      defaultNetwork: "sepolia",
      networks: {
        hardhat: {},
        sepolia: {
          url: API,
          accounts: [`0x${PRIVATE_KEY}`]
        }
      },
    };
    

*   To perform contract tests, ETH is required. Obtain test ETH by visiting [https://sepoliafaucet.com/](https://sepoliafaucet.com/) and entering the appropriate wallet address.
    
*   After switching to the Sepolia test network, it’s possible to observe that the wallet now contains some ETH.
    

**Execute Contract Deployment**
===============================

First, install ETHERS.JS.

    npm install --save-dev @nomiclabs/hardhat-ethers ethers@^5.7.2
    

Deploy the contract.

    npx hardhat --network sepolia run scripts/deploy.js
    // After execution is complete, a prompt will appear, indicating that the contract has been successfully deployed
    NFT_WEB3_EXPOLRER deployed to: {Contract Address}
    

At this point, the contract information can be found by searching for the contract address on [https://sepolia.etherscan.io/](https://sepolia.etherscan.io/).

**Setting Resources for NFTs**
------------------------------

The current NFTs only contain a `tokenID`, and resources need to be set for them. In the implementation of ERC721, we can see the implementation of the `tokenURI` method. Various marketplaces use this method to read NFT resource information.

    /**
         * @dev See {IERC721Metadata-tokenURI}.
         */
        function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
            require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
    
            string memory baseURI = _baseURI();
            return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
        }
    

As we can see, the method concatenates `BaseURI` and `tokenID` to read the `tokenURI`. Resources should be prepared in this format.

**Preparing Resources**
-----------------------

    // Create a resources folder
    mkdir res
    cd res
    // Create a folder for storing images
    mkdir  nfts
    // Create a folder for storing metadata information
    mkdir  metadata
    

Since this is a sample project and no specialized designer has been hired, five images were sourced online. Place these five images in the `img` directory and name them sequentially from `1.jpeg` to `5.jpeg`.

Similar to the Ethereum network, resource files are uploaded to IPFS. There’s no need to run a local IPFS node; instead, a node service provider can be used for uploading. For this tutorial, use [4everland’s storage dashboard](https://dashboard.4everland.org/bucket/storage/) to upload the `nfts` folder.

![](https://storage.googleapis.com/papyrus_images/d10fc12ef4a81aa949b6cf57a9ba382db865d024022b978c27fb7aa6b123ebf0.webp)

Select the image folder and perform snapshots to generate a root CID. The result when accessed through a gateway is shown below:

![](https://storage.googleapis.com/papyrus_images/4f8cc7d110863041d05a1f8a432e966bead776b6ce3db6e7a7b656dddd63ae09.webp)

Next, prepare the metadata files. Create five new files in the `metadata` directory and name them sequentially from `0` to `4`. Below is the information for file `0`:

    {
        "name": "nft-ocean",
        "attributes": [
            {
                "trait_type": "tokenID",
                "value": "0"
            }
        ],
        "description": "nft-ocean image",
        // Include the recently uploaded image URL here
        "image": "ipfs://bafybeief75v57gu3khb5ftjkgldyzyea4x3r6sesekcia6lk2pijgl5idm/01.jpeg"
    }
    

After uploading the `metadata` folder and taking snapshots, publish it once you have obtained the folder's CID.

![](https://storage.googleapis.com/papyrus_images/3e7d5bd63d515c04c9a76280c6807cf79cde799eb8f250d109cad691e394b3a2.webp)

**Setting the BaseURI**
-----------------------

It’s necessary to set the contract’s baseURI to the metadata file address that was just uploaded. This way, various platforms can access resource information using the `tokenURI` method. Create a new file under the `scripts` directory called `setBaseURI.js`.

setBaseURI.js

    require("dotenv").config()
    const hre = require("hardhat");
    const PRIVATE_KEY = process.env.PRIVATE_KEY
    const NETWORK = process.env.NETWORK
    const API_KEY = process.env.API_KEY
    
    
    const provider = new hre.ethers.providers.AlchemyProvider("sepolia",API_KEY);
    // The contract will be automatically generated upon compilation
    const abi = require("../artifacts/contracts/NFT_OCEAN.sol/NFT_OCEAN.json").abi
    const contractAddress = "the actual contract address" 
    const contract = new hre.ethers.Contract(contractAddress, abi, provider)
    const wallet = new hre.ethers.Wallet(PRIVATE_KEY, provider)
    const baseURI = "ipfs://{ the actual IPFS root CID where your metadata is stored}/"
    
    async function main() {
      const contractWithSigner = contract.connect(wallet);
      // Call the setBaseURI method
      const tx = await contractWithSigner.setBaseURI(baseURI)
      console.log(tx.hash);
      await tx.wait();
      console.log("setBaseURL success");
    }
    
    main()
      .then(() => process.exit(0))
      .catch((error) => {
        console.error(error);
        process.exit(1);
      });
    

**Execute the setBaseURI Script**
---------------------------------

    npx hardhat --network sepolia run scripts/setBaseURI.js
    

**Mint Testing**
----------------

Only minted `tokenID`s will appear on the marketplace, so a minting test is needed. This is usually done via a frontend interface in real scenarios.

mint.js

    require("dotenv").config()
    const hre = require("hardhat");
    const PRIVATE_KEY = process.env.PRIVATE_KEY
    const NETWORK = process.env.NETWORK
    const API_KEY = process.env.API_KEY
    
    
    const provider = new hre.ethers.providers.AlchemyProvider("sepolia",API_KEY);
    const abi = require("../artifacts/contracts/NFT_OCEAN.sol/NFT_OCEAN.json").abi
    const contractAddress = "0x531D6B8fBe5FAC349D05642E17F5E998A4DfEd68"
    const contract = new hre.ethers.Contract(contractAddress, abi, provider)
    const wallet = new hre.ethers.Wallet(PRIVATE_KEY, provider)
    
    async function main() {
        const contractWithSigner = contract.connect(wallet);
        /// Determine how much ETH is needed to mint
        const price = await contract.PRICE_PER_TOKEN();
        console.log("price is" + price);
        // Call the mint method and pay the minting fee
        const tx = await contractWithSigner.mint(1, { value: price});
        console.log(tx.hash);
        await tx.wait();
        console.log("mint success");
    }
    
    main()
        .then(() => process.exit(0))
        .catch((error) => {
            console.error(error);
            process.exit(1);
        });
    

**Execute the Mint Script**
---------------------------

    npx hardhat --network sepolia run scripts/mint.js
    

**Mint Successful**
-------------------

![](https://storage.googleapis.com/papyrus_images/80ac81b45d94c282688c662bb0dffd5b4a79f44ecb3e0f2b5f8dd10a8bdbcd27.webp)

**Viewing the NFT**
===================

At this point, you can go to the OpenSea testnet at [https://testnets.opensea.io/](https://testnets.opensea.io/) to view the NFT you have minted.

![](https://storage.googleapis.com/papyrus_images/f129fabe0a9d3a27fe9dfc67fef50226d91c2a4e2cc956da3473e91b89203c11.webp)

**About 4EVERLAND**
===================

4EVERLAND is a Web3 infrastructure powered by blockchain technology that provides developers with convenient, efficient, and low-cost storage, network, and computing capabilities. It is committed to helping developers smoothly transition from Web2.0 to Web3.0 and building a Web3.0 cloud computing platform friendly to Web2.0.

[Website](https://www.4everland.org/) | [Twitter](https://twitter.com/4everland_org) | [Telegram](https://t.me/org_4everland) | [Discord](https://discord.com/invite/Cun2VpsdjF) | [Reddit](https://www.reddit.com/r/4everland/) | [Medium](https://4everland.medium.com/) | Email

---

*Originally published on [4EVERLAND](https://paragraph.com/@4everland-3/nft-contract-deployment-tutorial)*
