Ontology Developer Center
DISCOVERCOMMUNITYSUPPORT
  • Introduction
  • Discover Ontology
  • Getting Started
  • Glossary
  • Decentralized Identity and Data
    • ONT ID
      • Decentralized Identifiers
        • Method Specification for Ontology
        • Method Specification for Ethereum
        • Method Specification for BSC
      • Verifiable Credentials
        • Anonymous Credentials
      • ONT Login
        • Scenarios
        • Protocol Specification
        • Front-end JavaScript SDK
          • Integration and Usage
          • API Reference
        • Front-end UI SDK
          • Integration and Usage
          • API Reference
        • Back-end Go SDK
          • Integration and Usage
          • API Reference
        • Back-end Java SDK
          • Integration and Usage
          • API Reference
      • ONT TAG
        • Workflow
        • API Reference
      • Mercury
      • OScore
    • DDXF
      • Components and Interfaces
      • GREP
      • Overall Scheme
      • Solutions
        • Marketplace
          • Deployment
          • Scenarios
          • SaaS Tenant
          • Java SDK
        • Data Storage
          • Deployment
          • Java SDK
        • Resource Auditor
        • Offline Judge
      • Use Cases
        • E-Shops
  • ONTOLOGY ELEMENTS
    • Smart Contracts
      • Types of smart contracts
    • Token Protocols
    • Consensus Mechanism
    • Ontology Oracle
      • Oracle Process Flow
  • GUIDES & TUTORIALS
    • Development Guides
      • dApp Development
        • Using the dAPI
        • Data Synchronization
      • Smart Contract Development
        • EVM Contract
          • Development Environment and Tools
          • Wallet Setup
          • Contract Development
          • How to Deploy a Smart Contract with GetBlock
        • NeoVM Contract
          • Development tools and environment
          • Launching the IDE
          • Writing and editing program logic
          • Deploying and testing on private net
        • WASM Contract
          • Development Environment
          • Project Initiation - Hello World
          • Creating your own project
          • Development using SmartX
          • Runtime API
          • Contract Fundamentals
          • Inter-contract Interaction
          • Developing Contracts in C++
        • Publish Contract Source Code
    • Integration Guides
      • dApp Integration
        • dAPI Integration
          • Chrome Plugin
          • Mobile wallet dApp
          • QR code mechanism
          • Wake call mechanism
        • Cocos 2D-x
        • Unity 3D applications
      • Mobile Wallet Integration
        • SDK integration
        • dAPI Integration
          • In-wallet applications
          • QR code mechanism
          • Wake call mechanism
        • Stake
      • Using ONT ID
      • Exchange Integration
        • Exchange Docking Guide
        • Exchange API
      • Ontology for dApp Stores
    • EVM & Token Decimals Upgrade
  • ONTOLOGY NODE
    • Abstract
    • Node Deployment
      • Standard Node
      • Rosetta Node
    • Interacting with a Public Node
  • DEVELOPER TOOLS
    • dApp Development Framework
      • Punica CLI
      • Punica boxes
      • Solo Chain
    • IDE
    • APIs
      • HTTP API
        • Restful
        • WebSocket
        • Remote Procedure Call (RPC)
      • Explorer v2 API
        • Block
        • Address
        • Contract
        • Token
        • Transactions
        • ONT ID
        • Summary
        • Node
      • Native Token API
        • ONT Contract API
        • ONG Contract API
      • ONT ID Contract API
      • Web3 API
      • OScore Open API
      • Rosetta Node API
        • Data API
        • Construction API
      • DToken Contract API
      • DDXF
        • Marketplace Contract API
        • Storage API
      • Governance API
    • Digital Wallet
      • Chrome Plugin provider
      • Chrome Plugin dAPI
      • Mobile version provider
      • Mobile version dAPI
    • SDKs
    • Signing Server
      • Installation
      • API reference
  • COMMUNITY
    • Ecosystem Programs
    • Community Libraries
    • Community Events
    • Community Channels
    • Core Contributors
  • SUPPORT
    • FAQ
      • Basic blockchain concepts
      • Ontology Nodes
      • Ontology token protocols
      • Smart contracts
      • SDKs and APIs
    • Contact Us
Powered by GitBook
On this page
  • Set up Environment
  • Contract Design
  • Contract Logic
  • Define Contract Events
  • Define Functions
  • Compile and Test Contract using Hardhat
  • Create a Hardhat Project
  • Configure hardhat.config
  • File Preparation
  • Include Code in the test Folder
  • Compile Contract
  • Test Contract

Was this helpful?

  1. GUIDES & TUTORIALS
  2. Development Guides
  3. Smart Contract Development
  4. EVM Contract

Contract Development

Write and deploy EVM contracts

PreviousWallet SetupNextHow to Deploy a Smart Contract with GetBlock

Last updated 1 year ago

Was this helpful?

Before getting started, you can apply for TestNet ONG tokens that will be used for invoking any contracts you deploy over at the .

Now we will demonstrate the full process of contract development, deployment and testing using Hardhat.

Set up Environment

  • Install

  • Install

Contract Design

Contract Logic

The contract we use as an example here is for sending red packets, which is used when users send crypto assets as gifts. The core functions are:

  • Send red packets

  • Receive red packets

Before sending red packets, the user need to determine the amount of tokens to be sent and the number of red packets. For instance, 100 tokens will be sent in 10 red packets (to 10 different wallets). For ease of understanding, each red packet contains the same amount, i.e., each contains 10 tokens.

Consequently, we define the data structure:

EIP20Interface public token; // support token address
uint public nextPacketId; // the next redpacket ID

// packetId -> Packet, store all the redpacket
mapping(uint => Packet) public packets;

//packetId -> address -> bool,  store receive redpacket record
mapping(uint => mapping(address => bool)) public receiveRecords;

struct Packet {
    uint[] assetAmounts;// Number of tokens per copy
    uint receivedIndex; // Number of red packets received
}

Define Contract Events

When executing the contract, we can trace the process by adding events.

Here we design two events:

  1. When the user send a red packet, the contract generates an ID for the red packets, which will be sent through this event notification:

event SendRedPacket(uint packetId, uint amount); 

2. When a user receives a red packet, this event notification is sent to record the ID and token amount of the received red packet:

event ReceiveRedPacket(uint packetId, uint amount);

Define Functions

sendRedPacket

Sends red packets. Any system is able to call the function and send certain amount of tokens to the contract address. Other addresses can receive red packets from this contract address.

Note: Before invoking this function, the contract has to be authorized to transfer tokens from users' addresses. To do so, call the approve method of the token first.

function sendRedPacket(uint amount, uint packetNum) public payable returns (uint) {
    require(amount >= packetNum, "amount >= packetNum");
    require(packetNum > 0 && packetNum < 100, "packetNum>0 && packetNum < 100");
    uint before = token.universalBalanceOf(address(this));
    token.universalTransferFrom(address(msg.sender), address(this), amount);
    uint afterValue = token.universalBalanceOf(address(this));
    uint delta = afterValue - before;
    uint id = nextPacketId;
    uint[] memory assetAmounts = new uint[](packetNum);
    for (uint i = 0; i < packetNum; i++) {
        assetAmounts[i] = delta / packetNum;
    }
    packets[id] = Packet({assetAmounts : assetAmounts, receivedIndex : 0});
    nextPacketId = id + 1;
    emit SendRedPacket(id, amount);
    return id;
}

receivePacket

Receives red packets. Any address can call this function by red packet ID to receive a red packet, meaning that you need to specify which one to receive.

function receivePacket(uint packetId) public payable returns (bool) {
    require(packetId < nextPacketId, "not the redpacket");
    Packet memory p = packets[packetId];
    if (p.assetAmounts.length < 1) {
        return false;
    }
    require(p.receivedIndex < p.assetAmounts.length - 1, "It's over");
    require(receiveRecords[packetId][address(msg.sender)] == false, "has received");
    p.receivedIndex = p.receivedIndex + 1;
    bool res = token.universalTransfer(msg.sender, p.assetAmounts[p.receivedIndex]);
    require(res, "token transfer failed");
    packets[packetId] = p;
    receiveRecords[packetId][address(msg.sender)] == true;
    emit ReceiveRedPacket(packetId, p.assetAmounts[p.receivedIndex]);
    return true;
}

Compile and Test Contract using Hardhat

Create a Hardhat Project

mkdir hardhatdemo
cd hardhatdemo
npm init
npm install --save-dev hardhat
npx hardhat

Configure hardhat.config

Include TestNet node information:

module.exports = {
    defaultNetwork: "ontology_testnet",
    networks: {
        hardhat: {},
        ontology_testnet: {
            url: "http://polaris2.ont.io:20339",
            chainId: 5851,
            gasPrice:2500000000000,
            gas:2000000,
            timeout:10000000,
            accounts: ["your private key1","your private key2"]
        }
    },
    solidity: {
        version: "0.8.0",
        settings: {
            optimizer: {
                enabled: true,
                runs: 200
            }
        }
    },
};

File Preparation

Include Code in the test Folder

describe("RedPacket", function () {
    let tokenDemo, redPacket, owner, acct1, assetAmount, packetAmount;
    beforeEach(async function () {
        const TokenDemo = await ethers.getContractFactory("TokenDemo");
        tokenDemo = await TokenDemo.deploy(10000000, "L Token", 18, "LT");
        await tokenDemo.deployed();
        const RedPacket = await ethers.getContractFactory("RedPacket");
        redPacket = await RedPacket.deploy(tokenDemo.address);
        await redPacket.deployed();
        [owner, acct1] = await ethers.getSigners();
        assetAmount = 1000;
        packetAmount = 10;
    });
    it("token", async function () {
        expect(await redPacket.token()).to.equal(tokenDemo.address);
    });
    it("sendRedPacket", async function () {
        const approveTx = await tokenDemo.approve(redPacket.address, assetAmount);
        await approveTx.wait();

        const sendRedPacketTx = await redPacket.sendRedPacket(assetAmount, packetAmount);
        await sendRedPacketTx.wait();
        let balance = await tokenDemo.balanceOf(redPacket.address);
        expect(balance.toString()).to.equal(assetAmount.toString());

        res = await redPacket.nextPacketId();
        expect(res.toString()).to.equal("1");

        await redPacket.connect(acct1).receivePacket(0);
        balance = await tokenDemo.balanceOf(acct1.address);
        expect(balance.toString()).to.equal((assetAmount / packetAmount).toString());
    });
});

Compile Contract

Run this command in the root directory to compile the contract.

$ npx hardhat compile
Compiling 5 files with 0.8.0
Compilation finished successfully

Then the following folders are generated.

.
├── artifacts
├── cache
├── contracts
├── hardhat.config.js
├── node_modules
├── package-lock.json
├── package.json
├── scripts
└── test

Test Contract

npx hardhat test

You will get the following result:

sss@sss hardhatdemo % npx hardhat test
  RedPacket
    ✓ token
    ✓ sendRedPacket (16159ms)


  2 passing (41s)

You can refer to the Ethereum Web3 API by following the link below.

View the full code .

accounts field takes the array of selected private key. There should be enough ONG balance in the corresponding address to pay for transactions. You can apply for TestNet ONG .

Add the contract file in the contracts folder. To support ERC-20 token transfer, we also need EIP20Interface.sol, UniversalERC20.sol, and TokenDemo.sol which you can download from .

faucet here
nodejs
Hardhat
here
here
here
Web3 API