Klaytn Docs Archive
Getting StartedBuild a dAppNode OperationDeveloper Hub
  • Klaytn Docs
  • -
    • Klaytn Overview
      • Why Klaytn
      • Klaytn Design
        • Consensus Mechanism
        • Accounts
        • Transactions
          • Basic
          • Fee Delegation
          • Partial Fee Delegation
          • Ethereum
        • Computation
          • Klaytn Smart Contract
          • Execution Model
          • Computation Cost
            • Computation Cost (Previous docs)
          • Klaytn Virtual Machine
            • Klaytn Virtual Machine (Previous docs)
        • Storage
          • State Migration
          • StateDB Live Pruning
        • Transaction Fees
          • Transaction Fees (Previous docs)
        • Klaytn native coin - KLAY
        • Token Economy
        • Governance
        • Multi-Channel
        • KNI
      • Scaling Solutions
    • Getting Started
      • Deploying Smart Contract Using Foundry
      • Deploying Smart Contract Using Hardhat
      • Deploying Smart Contract Using Thirdweb
      • Deploying Smart Contract Using KEN
        • Launch an Endpoint Node
        • Top up your Account
        • Install Development Tools
        • Deploy a Smart Contract
        • Check the Deployment
        • Account Management
          • Creating Accounts
          • Managing Accounts
      • Development Environment
      • Getting KLAY
    • Smart Contract
      • Solidity - Smart Contract Language
      • Precompiled Contracts
        • Precompiled Contracts (Previous docs)
      • IDE and Tools
        • Truffle
      • Sample Contracts
        • KlaytnGreeter
        • ERC-20
          • 1. Writing ERC-20 Smart Contract
          • 2. Deploying Smart Contract
          • 3. Interacting with ERC-20 token from Klaytn Wallet
        • ERC-721
          • 1. Writing ERC-721 Smart Contract
          • 2. Deploying Smart Contract
      • Testing Guide
      • Deployment Guide
      • Klaytn Compatible Tokens
      • Porting Ethereum Contract
    • Run a Node
      • Deployment
        • Endpoint Node
          • System Requirements
          • Installation Guide
            • Download
            • Installation Guide
            • Configuration
            • Startup the EN
            • Testing the Installation
          • ken CLI commands
          • JSON-RPC APIs
        • Core Cell
          • System Requirements
          • Network Configuration
          • Installation Guide
            • Download
            • Before You Install
            • Consensus Node Setup
              • Installation Guide
              • Configuration
              • Startup the CN
            • Proxy Node Setup
              • Installation Guide
              • Configuration
              • Startup the PN
            • Testing the Core Cell
          • Monitoring Setup
          • H/A Setup
        • Service Chain
          • Getting Started
            • Setting up a 4-node Service Chain
            • Connecting to Baobab
            • Cross-Chain Value Transfer
            • HA(High Availability) for ServiceChain
            • Nested ServiceChain
            • Value Transfer between Sibling ServiceChains
          • Reference Manuals
            • System Requirements
            • Download
            • SCN User Guide
              • Installation
              • Configuration
              • Starting/Stopping SCN
              • Checking Node Status
              • kscn commands
              • homi commands
            • SPN/SEN User Guide
              • Installation
              • Configuration
              • Starting/Stopping Node
              • Checking Node Status
            • Bridge Configuration
            • Anchoring
            • KAS Anchoring
            • Value Transfer
            • Configuration Files
            • Log Files
            • Genesis JSON
            • Upgrade & Hard Fork
          • How-To Guides
        • Download Node Packages
          • v1.12.0
          • v1.11.1
          • v1.11.0
          • v1.10.2
          • v1.10.1
          • v1.10.0
          • v1.9.1
          • v1.9.0
          • v1.8.4
          • v1.8.3
          • v1.8.2
          • v1.8.1
          • v1.8.0
          • v1.7.3
          • v1.7.2
          • v1.7.1
          • v1.7.0
          • v1.6.4
          • v1.6.3
          • v1.6.2
          • v1.6.1
          • v1.6.0
          • v1.5.3
          • v1.5.2
          • v1.5.1
          • v1.5.0
          • v1.4.2
          • v1.4.1
          • v1.4.0
          • v1.3.0
          • v1.2.0
          • v1.1.1
          • v1.0.0
          • v0.9.6
          • v0.8.2
    • Operation Guide
      • Configuration
      • Node Log
      • Log operation
      • Errors & Troubleshooting
      • Klaytn Command
      • Chaindata Change
      • Chaindata Migration
    • dApp Developers
      • JSON-RPC APIs
        • API references
          • eth
            • Caution
            • Account
            • Block
            • Transaction
            • Config
            • Filter
            • Gas
            • Miscellaneous
          • klay
            • Account
            • Block
            • Transaction
              • Working with Klaytn Transaction Types
            • Configuration
            • Filter
            • Gas
            • Miscellaneous
          • net
          • debug
            • Logging
            • Profiling
            • Runtime Tracing
            • Runtime Debugging
            • VM Tracing
            • VM Standard Tracing
            • Blockchain Inspection
          • admin
          • personal
          • txpool
          • governance
        • Service Chain API references
          • mainbridge
          • subbridge
        • Transaction Error Codes
      • RPC Service Providers
        • Public Endpoints
      • SDK & Libraries for interacting with Klaytn Node
        • caver-js
          • Getting Started
          • Sending a sample transaction
          • API references
            • caver.account
            • caver.wallet
              • caver.wallet.keyring
            • caver.transaction
              • Basic
              • Fee Delegation
              • Partial Fee Delegation
            • caver.rpc
              • caver.rpc.klay
              • caver.rpc.net
              • caver.rpc.governance
            • caver.contract
            • caver.abi
            • caver.kct
              • caver.kct.kip7
              • caver.kct.kip17
              • caver.kct.kip37
            • caver.validator
            • caver.utils
            • caver.ipfs
          • caver-js ~v1.4.1
            • Getting Started (~v1.4.1)
            • API references
              • caver.klay
                • Account
                • Block
                • Transaction
                  • Legacy
                  • Value Transfer
                  • Value Transfer Memo
                  • Account Update
                  • Smart Contract Deploy
                  • Smart Contract Execution
                  • Cancel
                • Configuration
                • Filter
                • Miscellaneous
              • caver.klay.net
              • caver.klay.accounts
              • caver.klay.Contract
              • caver.klay.KIP7
              • caver.klay.KIP17
              • caver.klay.abi
              • caver.utils (~v1.4.1)
            • Porting from web3.js
        • caver-java
          • Getting Started
          • API references
          • caver-java ~v1.4.0
            • Getting Started (~v1.4.0)
            • Porting from web3j
        • ethers.js
        • web3.js
      • Tutorials
        • Klaytn Online Toolkit
        • Fee Delegation Example
        • Count DApp
          • 1. Environment Setup
          • 2. Clone Count DApp
          • 3. Directory Structure
          • 4. Write Smart Contract
          • 5. Frontend Code Overview
            • 5-1. Blocknumber Component
            • 5-2. Auth Component
            • 5-3. Count Component
          • 6. Deploy Contract
          • 7. Run App
        • Klaystagram
          • 1. Environment Setup
          • 2. Clone Klaystagram DApp
          • 3. Directory Structure
          • 4. Write Klaystagram Smart Contract
          • 5. Deploy Contract
          • 6. Frontend Code Overview
          • 7. FeedPage
            • 7-1. Connect Contract to Frontend
            • 7-2. UploadPhoto Component
            • 7-3. Feed Component
            • 7-4. TransferOwnership Component
          • 8. Run App
        • Building a Buy Me a Coffee dApp
          • 1. Project Setup
          • 2. Creating a BMC Smart Contract
          • 3. Testing the contract using scripts
          • 4. Deploying BMC Smart contract
          • 5. Building the BMC Frontend with React and Web3Onboard
          • 6. Deploying Frontend code on IPFS using Fleek
          • 7. Conclusion
        • Migrating Ethereum App to Klaytn
        • Connecting MetaMask
        • Connecting Remix
        • Verifying Smart Contracts Using Block Explorers
      • Developer Tools
        • Wallets
          • Kaikas
          • Klaytn Wallet
          • Klaytn Safe
            • Klaytn Safe Design
            • Create a Safe
            • Add assets
            • Send assets
            • Contract Interaction
            • Transaction Builder
            • Points to Note
            • Frequently Asked Questions
          • SafePal S1
          • Wallet Libraries
            • Web3Auth
            • Web3Modal
            • Web3-Onboard
            • Particle Network
        • Oracles
          • Orakl Network
          • Witnet
          • SupraOracles
        • Indexers
          • SubQuery
        • Cross-chain
          • LayerZero
        • Block Explorers
          • Klaytnscope
          • Klaytnfinder
        • Klaytn Contracts Wizard
    • Glossary
  • ---
    • Klaytn Hard Fork History
    • Klaytn 2.0
      • Metaverse Package
      • Finality and Improvements
      • Ethereum Compatibility
      • Decentralizing Governance
      • Massive Eco Fund
    • FAQ
    • Open Source
    • Terms of Use
    • Languages
  • ℹ️Latest Klaytn Docs
Powered by GitBook
On this page
  • Introduction
  • Pre-requisites
  • Setting Up Your Development Environment
  • Creating SBT Smart Contract
  • Testing SBT Smart Contract
  • Deploying the smart contract
  • Hardhat Forking
  • Forking Mainnet
  • Forking at a Block
  1. -
  2. Getting Started

Deploying Smart Contract Using Hardhat

PreviousDeploying Smart Contract Using FoundryNextDeploying Smart Contract Using Thirdweb

Last updated 1 year ago

Introduction

This section will guide you through deploying a Soulbound Token to the Klaytn Baobab Network using .

Hardhat is a smart-contract development environment that will help you:

  • Develop and compile smart contracts.

  • Debug, test, and deploy smart contracts and dApps.

By the end of this guide you will be able to:

  • Set up a Hardhat project on Klaytn.

  • Create a simple soul-bound token.

  • Compile your smart contract using Hardhat.

  • Test, deploy, and interact with your smart contract using Hardhat.

  • Explore Hardhat forking feature.

Pre-requisites

To follow this tutorial, the following are the prerequisites:

Setting Up Your Development Environment

To make use of hardhat, we need to set up our development environment and get hardhat installed. Let's do this in the following steps:

Step 1: Create a project directory

mkdir soulbound-tokens
cd soulbound-tokens

Step 2: Initialize an npm project

Paste this command in your terminal to create a package.json file

npm init -y

Step 3: Install hardhat and other dependencies:

  • Paste the code below in your terminal to install hardhat

npm install --save-dev hardhat
  • Paste the code below to install other dependencies

npm install dotenv @klaytn/contracts

Note: This installs other dependencies needed for this project ranging from hardhat, klaytn/contract, dotenv et al.

Step 4: Initialise a hardhat project:

Run the command below to initiate an hardhat project

npx hardhat

For this guide, you'll be selecting a typescript project as seen below:

Note: While initializing the project, you will get a prompt to install hardhat-toolbox plugin. The plugin bundles all the commonly used packages and Hardhat plugins recommended to start developing with Hardhat.

After initializing a hardhat project, your current directory should include:

contracts/ – this folder contains smart contract code.

scripts/ – this folder contains code that deploys your contracts on the blockchain network.

test/ – this folder contains all unit tests that test your smart contract.

hardhat.config.js – this file contains configurations important for the work of Hardhat and the deployment of the soul-bound token.

Step 5: Create a .env file

Now create your .env file in the project folder. This file helps us load environment variables from an .env file into process.env.

  • Paste this command in your terminal to create a .env file

touch .env
  • After creating our file, let's configure our .env file to look like this:

 KLAYTN_BAOBAB_URL= "Your Baobab RPC link"
 PRIVATE_KEY= "your private key copied from MetaMask wallet"

Step 6: Setup Hardhat Configs

Modify your hardhat.config.js with the following configurations:

require("@nomicfoundation/hardhat-toolbox");
require('dotenv').config()


module.exports = {
  solidity: "0.8.17",
  networks: {
    baobab: {
      url: process.env.KLAYTN_BAOBAB_URL || "",
      gasPrice: 250000000000,
      accounts:
        process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [],
    }
  }
};

Now that we have our development environment all set, let's get into writing our soul-bound token smart contract.

Creating SBT Smart Contract

Note: You already installed this library in step 3 of the Setting Development Environment section.

Step 1: Select the contracts folder in the Explorer pane, click the New File button and create a new file named SBT.sol

Step 2: Open the file and paste the following code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import "@klaytn/contracts/KIP/token/KIP17/KIP17.sol";
import "@klaytn/contracts/utils/Counters.sol";
import "@klaytn/contracts/access/Ownable.sol";

contract SoulBoundToken is KIP17, Ownable {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;

    constructor() KIP17("SoulBoundToken", "SBT") {}

    function safeMint(address to) public onlyOwner {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
    }


    function _beforeTokenTransfer(address from, address to, uint256) pure override internal {
        require(from == address(0) || to == address(0), "This a Soulbound token. It cannot be transferred.");
    }

    function _burn(uint256 tokenId) internal override(KIP17) {
        super._burn(tokenId);
    }
}

Code Walkthrough

This is your smart contract. line 1 shows that Hardhat uses the Solidity version 0.8.7 or greater. Other than that, it imports KIP17.sol and other supporting contracts. From lines 6-12, a smart contract that inherits KIP17 is been created. Also, the token name and symbol was passed in the constructor.

As you can see in the code above, the token name and symbol have been set to SoulBoundToken and SBT respectively. You can change the token name and symbol to anything you desire.

One major thing in this contract is that it prohibits token transfer, which makes the issued tokens soulbond.

Testing SBT Smart Contract

In this section, we would be testing some of our contract functionalities.

Step 1: In the Explorer pane, select the test folder and click the New File button to create a new file named sbtTest.js

Step 2: Copy the code below in the sbtTest.js file.

// This is an example test file. Hardhat will run every *.ts file in `test/`,
// so feel free to add new ones.

// Hardhat tests are normally written with Mocha and Chai.

// We import Chai to use its asserting functions here.
const { expect } = require("chai");

// We use `loadFixture` to share common setups (or fixtures) between tests.
// Using this simplifies your tests and makes them run faster, by taking
// advantage of Hardhat Network's snapshot functionality.
const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers");

// `describe` is a Mocha function that allows you to organize your tests.
// Having your tests organized makes debugging them easier. All Mocha
// functions are available in the global scope.
//
// `describe` receives the name of a section of your test suite, and a
// callback. The callback must define the tests of that section. This callback
// can't be an async function.
describe("Token contract", function () {
  // We define a fixture to reuse the same setup in every test. We use
  // loadFixture to run this setup once, snapshot that state, and reset Hardhat
  // Network to that snapshot in every test.
  async function deployTokenFixture() {
    // Get the ContractFactory and Signers here.
    const [owner, addr1, addr2] = await ethers.getSigners();

    // To deploy our contract, we just have to call ethers.deployContract() and call the 
    // waitForDeployment() method, which happens onces its transaction has been
    // mined.

    const sbtContract = await ethers.deployContract("SoulBoundToken");

    await sbtContract.waitForDeployment();

    // Fixtures can return anything you consider useful for your tests
    return { sbtContract, owner, addr1, addr2 };
  }

  // You can nest describe calls to create subsections.
  describe("Deployment", function () {
    // `it` is another Mocha function. This is the one you use to define each
    // of your tests. It receives the test name, and a callback function.
    //
    // If the callback function is async, Mocha will `await` it.
    it("Should mint SBT to owner", async function () {
      const { sbtContract, owner } = await loadFixture(deployTokenFixture);
      const safemint = await sbtContract.safeMint(owner.address);
      expect(await sbtContract.ownerOf(0)).to.equal(owner.address);
    });
  });

  describe("Transactions", function () {
    it("Should prohibit token transfer using transferFrom", async function () {
      const { sbtContract, owner, addr1 } = await loadFixture(
        deployTokenFixture
      );

      const safemintTx = await sbtContract.safeMint(owner.address);

      // prohibit token transfer of token id (0) from owner to addr1
      await expect(
        sbtContract.transferFrom(owner.address, addr1.address, 0)
      ).to.be.reverted;
  });

  it("Should prohibit token transfer using safeTransferFrom", async function () {
    const { sbtContract, owner, addr1 } = await loadFixture(
      deployTokenFixture
    );

    const safemintTx = await sbtContract.safeMint(owner.address);

    // prohibit token transfer of token id (0) from owner to addr1
    await expect(sbtContract['safeTransferFrom(address,address,uint256)'](
      owner.address,
      addr1.address,
      0 
  )).to.be.reverted;
});


});

})

The tests above check the following:

  • Is the owner of a particular token id the same as who it was minted to?

  • Did it prohibit transfer of tokens between accounts?

Step 3: To run your test, run the command below:

npx hardhat test test/sbtTest.ts 

Deploying the smart contract

Scripts are JavaScript/Typescript files that help you deploy contracts to the blockchain network. In this section, you will create a script for the smart contract.

Step 1: In the Explorer pane, select the "scripts" folder and click the New File button to create a new file named sbtDeploy.js.

Step 2: Copy and paste the following code inside the file.

Note: input your MetaMask wallet address in the deployerAddr variable.

const { ethers } = require("hardhat");

async function main() {

  const deployerAddr = "Your Metamask wallet address";
  const deployer = await ethers.getSigner(deployerAddr);

  console.log(`Deploying contracts with the account: ${deployer.address}`);
  console.log(`Account balance: ${(await deployer.provider.getBalance(deployerAddr)).toString()}`);


  const sbtContract = await ethers.deployContract("SoulBoundToken");
  await sbtContract.waitForDeployment();

console.log(`Congratulations! You have just successfully deployed your soul bound tokens.`);
console.log(`SBT contract address is ${sbtContract.target}. You can verify on https://baobab.scope.klaytn.com/account/${sbtContract.target}`);
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Step 3: In the terminal, run the following command which tells Hardhat to deploy your SBT token on the Klaytn Test Network (Baobab)

npx hardhat run scripts/sbtDeploy.js --network baobab

Step 5: Copy and paste the deployed contract address in the search field and press Enter. You should see the recently deployed contract.

Hardhat Forking

Hardhat provides developers the functionality of simulating the mainnet (at any given block) to a local development network. One of the major benefit of this feature is that it enables developers to interact with deployed contract and also write test for complex cases.

Forking Mainnet

Now that we have our Hardhat project set up let’s fork the Klaytn Mainnet using Hardhat. Open your terminal and run this command

npx hardhat node --fork <YOUR ARCHIVE NODE URL>

npx hardhat node --fork https://archive-en.cypress.klaytn.net

You can also configure hardhat.config.js - Hardhat Network to always do this:

networks: {
  hardhat: {
    forking: {
      url: "<YOUR ARCHIVE NODE URL>",
    }
  }
}

Output

After successfully running this command, your terminal looks like the above image. You'll have 20 development accounts that are pre-funded with 10,000 test tokens.

The forked chain's RPC server is listening at http://127.0.0.1:8545/. You can verify the forked network by querying the latest block number. Let's try to make a cURL to the RPC to get the block number. Open a new terminal window and use the following command:

curl --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST localhost:8545 

Output

Forking at a Block

With hardhat, you can fork the mainnet at a particular block. In that case, let’s fork the chain at block number 105701850.

npx hardhat node --fork <YOUR ARCHIVE NODE URL> --fork-block-number 105701850

npx hardhat node --fork https://archive-en.cypress.klaytn.net --fork-block-number 105701850

To confirm the forked chain at the stated block, open a new terminal window and use the following command:

curl --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST localhost:8545 

Soul-bound tokens(SBTs) are non-transferable NFTs. Meaning once acquired, they cannot be sold or transferred to another user. To learn more about SBTs, how it works and their use case, you can check out this published by Vitalik Buterin.

Code editor: a source-code editor such .

: used to deploy the contracts, sign transactions and interact with the contracts.

RPC Endpoint: you can get this from one of the supported .

Test KLAY from : fund your account with sufficient KLAY.

Note: You can also choose to use the functionality provided by hardhat to configure variables that shouldn't be included in the code repository.

In this section, you will use the : a library for secure smart contract development built on a solid foundation of community-vetted code. It is a fork of open zeppelin contracts.

In the code you just copied, line 7 & 12 shows you imported expect from and from hardhat-network-helpers.

For more in-depth guide on testing, please check .

Step 4: Open to check if the SBT token has been deployed successfully.

For this feature to work effectively, you need to connect to an archive node. You can read more about this feature

The output is an hexadecimal as seen above. To get the block number from the hex, convert the hex to a decimal using this . You should get the latest block number from the time you forked the network. You can confirm the block number on .

The output returns hexadecimal which when converted using this should be equal to 105701850.

For more in-depth guide on Hardhat, please refer to . Also, you can find the full implementation of the code for this guide on

reference article
VS-Code
Metamask
Endpoint Providers
Faucet
NodeJS and NPM
configuration variable
Klaytn Contracts
Chai
loadFixture
Hardhat testing
Klaytnscope
here
tool
klaytnscope
tool
Hardhat Docs
GitHub
Hardhat