Ethersjs生成指令集,为Solidity合约中call参数使用

合约如下:


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

struct User{
    uint8 userType;
    uint8 age;
}

contract Test {
     User[] public users;  
     mapping(address => User) public user;

    function testCall(bytes memory _payload) external {
        (bool success, ) = address(this).call(_payload);
        require(success, "call fail.");
    }

    function testCalled(address[] memory _addr, User[] memory _user) external {
        require(_addr.length == _user.length, "Len is not the same.");
        for (uint8 i = 0; i < _user.length; i++) {
            user[_addr[i]] = _user[i];
            users.push(_user[i]);
        }
    }
    
    function getCallBytes() external pure returns (bytes memory){
        address[] memory _addrs = new address[](1);
        _addrs[0] = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;

        User memory _user = User(1,25);
        User[] memory _users = new User[](1);
        _users[0] = _user;
       
        bytes memory payload = abi.encodeWithSignature("testCalled(address[],(uint8,uint8)[])", _addrs, _users);
        return payload;
    }

}

在Remix中进行测试,通过测试函数 getCallBytes 生成payload,此函数中参数值是固定的

Ethersjs生成指令集,为Solidity合约中call参数使用_第1张图片

在etherjs 测试合约时,需要生成上面合约函数getCallBytes生成的payload,参数值都相同,写法如下:

const { expect } = require('chai');
const { ethers } = require('hardhat');

describe("测试合约", function () {
    let testContract;
    let owner;

    beforeEach(async () => {
        [owner] = await ethers.getSigners();
        console.log("owner地址: ", owner.address);

        console.log("================ 部署合约 ==================");
        const TestContract = await ethers.getContractFactory("Test");

        testContract = await TestContract.deploy();

        //验证合约地址
        expect(testContract).to.not.equal(ethers.constants.AddressZero);
        console.log("合约地址: ", testContract.address);
    });

   
    it("1、测试指令集", async function () {

        console.log("================ 生成指令集 ==================");
        //指令集 - 函数参数 - 账户地址
        const newAddrs = [owner.address];
        //指令集 - 函数参数 - 用户信息
        const newUsers = [
            {
                userType: 1,
                age: 25
            }
        ]

        //指令集 - Solidity中函数的名称和参数类型
        const functionName = "testCalled";
        const functionParamType = ["address[]", "(uint8,uint8)[]"];

        //指令集 - 用于编码函数名称和参数类型的selector
        const selector = ethers.utils.hexDataSlice(ethers.utils.keccak256(ethers.utils.toUtf8Bytes(`${functionName}(${functionParamType.join(',')})`)), 0, 4);

        //指令集 - 用于编码函数参数的ABI编码器
        const encodedData = ethers.utils.defaultAbiCoder.encode(
            ["address[]", "(uint8 userType, uint8 age)[]"],
            [newAddrs, newUsers]
        );

        //指令集 - 构造包含函数选择器的payload
        const payload = selector + encodedData.substr(2); // 去除前面的'0x'

        console.log("payload:", payload);

        console.log("================ 调用合约方法 ==================");
        //调用合约方法
        await testContract.testCall(payload);

        console.log("================ 验证执行方法结果 ==================");
        //验证用户是否已加

        const user = await testContract.user(owner.address);
        expect(user.userType).to.be.equal(newUsers[0].userType);
        expect(user.age).to.be.equal(newUsers[0].age);
        console.log("用户信息", user);
    });

});

测试结果如下,打印出payload, 与合约函数getCallBytes生成的payload是一致的。

Ethersjs生成指令集,为Solidity合约中call参数使用_第2张图片

注:使用call调用的函数需要是external,即上面的testCalled函数是external

你可能感兴趣的:(智能合约,智能合约)