天天看點

本體技術視點 | 關于本體EVM合約開發,你必須知道的事(三)

本體技術視點 | 關于本體EVM合約開發,你必須知道的事(三)

上周,本體宣布支援 EVM 的測試網正式部署并向全球開發者開放 EVM 相容公測。同時,與知名代碼審計機構慢霧科技合作釋出《本體安全漏洞與威脅情報賞金計劃》(https://slowmist.io/en/ontology/)正式啟動,上報單個有效漏洞獎勵最高可達12,000美金。

本體技術視點 | 關于本體EVM合約開發,你必須知道的事(三)

上一期,我們介紹了在本體上開發和部署 EVM 合約的工具,以及如何使用 MetaMask 插件資料用戶端來管理以太坊資料用戶端。這一期,我們将為您帶來本體 EVM 合約開發流程示範。

第四部分 EVM 合約開發流程示範

下面我們将使用 Hardhat 工具來示範在本體網絡中開發部署和測試 EVM 合約的完整流程。

4.1 環境準備

  • 安裝 nodejs(https://nodejs.org/en/)
  • 安裝 Hardhat(https://hardhat.org/getting-started/)

4.2 合約設計

4.2.1 合約邏輯

我們将以一個紅包合約為例,該合約主要提供以下功能:

  • 發紅包
  • 領紅包

每次發紅包需要指定紅包金額和該紅包數量。

例如,紅包總金額是100個通證,紅包的數量是10,即有10個不同的位址領取紅包。為了簡單起見,我們設定每個紅包金額相等, 也就是每個位址可以領10個通證。

根據以上的邏輯我們可以設定如下的存儲結構:

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

}

4.2.2 定義合約事件

在合約執行的過程中,我們可以通過添加事件來追溯合約執行流程。

在本例中我們設計以下兩個事件:

  • 發紅包時,合約會生成紅包的 ID,該 ID 要通過事件推送給調用者
  • 領取紅包時,需要推送一個事件用來記錄領取的紅包 ID 和 Token 數量

event SendRedPacket(uint packetId, uint amount);

event ReceiveRedPacket(uint packetId, uint amount);

4.2.3 定義函數

sendRedPacket

1. 發紅包。任何人都可以調用該接口,将一定量的通證打給該合約位址,進而其他的位址可以從該合約位址領取紅包。

注意: 在調用該方法之前,需要先授權該合約位址能夠從使用者的位址把通證轉移走,是以需要先調用該通證的 approve 方法。

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

2. 領取紅包。任何位址都可以通過調用該接口領取紅包,調用該接口的時候需要指定紅包的 ID,即指定要領取的紅包。

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;

}

合約完整的代碼請參考此連結:(https://github.com/ontio/ontology/tree/master/docs/specifications/evm_refernce/contract-demo/hardhatdemo/contracts)。

4.3 使用 Hardhat 編譯和測試合約

4.3.1 建立 Hardhat 項目

mkdir hardhatdemo

cd hardhatdemo

npm init

npm install --save-dev hardhat

npx hardhat

4.3.2 修改 hardhat.config.js 檔案

添加測試網節點配置資訊

module.exports = {

defaultNetwork: "ontology_testnet",

networks: {

hardhat: {},

ontology_testnet: {

url: "http://polaris2.ont.io:20339",

chainId: 5851,

gasPrice:500,

gas:2000000,

timeout:10000000,

accounts: ["使用者私鑰1",

"使用者私鑰2"]

}

},

solidity: {

version: "0.8.0",

settings: {

optimizer: {

enabled: true,

runs: 200

}

}

},

};

accounts 字段指定的私鑰數組,對應的位址需要有測試網的 ONG 來支付交易的手續費,可以在這裡(https://developer.ont.io/)領取測試網 ONG。

4.3.3 檔案準備

把紅包合約代碼檔案放到 contracts 檔案夾下,為了支援 ERC-20 數字資産的轉賬,我們還需要 EIP20Interface.sol,UniversalERC20.sol 和 TokenDemo.sol 檔案,可以從此處(https://github.com/ontio/ontology/tree/master/docs/specifications/evm_refernce/contract-demo/hardhatdemo)下載下傳相關檔案。

4.3.4 在 test 檔案夾下添加測試代碼

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());

});

});

4.3.5 編譯合約

在項目根目錄執行如下指令編譯合約

$ npx hardhat compile

Compiling 5 files with 0.8.0

Compilation finished successfully

該指令執行完成後會生成如下的檔案夾

├── artifacts

├── cache

├── contracts

├── hardhat.config.js

├── node_modules

├── package-lock.json

├── package.json

├── scripts

└── test

4.3.6 測試合約

$ npx hardhat test

執行結果如下

$ npx hardhat test

RedPacket

✓ Token

✓ sendRedPacket (16159ms)

2 passing (41s)

以上便是本體 EVM 合約開發的完整示範流程,希望我們提供的教程能夠幫助到您。下期,我們将為您提供 Web3 API 參考,并教您如何使用 Ontology Bridge 本體跨鍊橋實作本體數字資産與以太坊數字資産的一鍵互跨,敬請期待。