您正在查看: Ethereum-新手教程 分类下的文章

Hardhat 编译多个solidity版本的合约

今天由于需要同时验证新版本solidity已废弃的方法“callcode”,以及新版本才支持的方法“staticcall”,所以需要编写两个版本的合约,可以修改hardhat config,支持多个版本的编译器,类似如下

const config: HardhatUserConfig = {
  solidity: {
    compilers: [
      {
        version: "0.8.4"
      },
      {
        version: "0.4.26",
      }
    ]
  },
  ...

hardhat create address

web3

let singer = await web3.eth.accounts.create();

ethers

const pKey = new ethers.Wallet.createRandom();
const signer = new ethers.Wallet(pKey, ethers.provider);

如何获得以太坊编码的函数签名

  • 当编写代码以原始方式调用智能合约的函数或从多重签名钱包调用合约函数时,需要函数签名。
  • 要获得函数签名,您需要像functionName(type1,type2,...)Keccak256 一样对函数的原型字符串进行哈希处理。然后提取前 4 个字节。
  • 例如,如果你想得到函数的编码函数签名,用 Keccak256sendMessage(string message, address to)对函数的原型字符串进行哈希处理。sendMessage(string,address)然后提取前 4 个字节“0xc48d6d5e”。

使用 Web3.js 获取编码的函数签名

在 Web3.js 1.0.0 中,可以通过实用函数获得编码的函数签名。

let encodedFunctionSignature = web3.eth.abi.encodeFunctionSignature('sendMessage(string,address)'); 
console.log(encodedFunctionSignature); 
// => 0xc48d6d5e

在线计算

https://piyolab.github.io/playground/ethereum/getEncodedFunctionSignature/

相关文章

http://blog.playground.io/entry/2018/05/08/163727

参考

https://web3js.readthedocs.io/en/1.0/web3-eth-abi.html#encodefunctionsignature

英文原文:https://piyopiyo.medium.com/how-to-get-ethereum-encoded-function-signatures-1449e171c840

Solidity — 启用 ABIEncoderV2 以使用 Structs 作为函数参数

如果您一直在以太坊上进行开发,您就会知道无法将结构从合约传递到合约或从 web3 传递到合约的痛苦。在 Atra Blockchain Services,我们为用户自动创建和部署以太坊合约,这一限制直接影响了我们的数据存储服务 dTables。

现在,启用 ABIEncoderV2 后,您可以将结构类型从 web3 或其他合约传递给函数。在启用 ABIEncoderV2 的情况下编译合约时,编译后的 ABI 输出会发生一些变化。ABI JSON 现在将包含一种称为“元组”的新类型,当它遇到结构作为函数中的参数时。元组类型与属性“组件”配对,组件属性是一个包含 {name, type} 对象列表的数组。
下面是使用结构体作为输入参数的合约的 ABI 示例。注意类型和组件属性。
GitHub code

{
  "constant": false,
  "inputs": [{
    "components": [{
      "name": "text",
      "type": "string"
    }],
    "name": "recordData",
    "type": "tuple"
  }],
  "name": "Insert",
  "outputs": [{
    "name": "success",
    "type": "bool"
  }],
  "payable": false,
  "stateMutability": "nonpayable",
  "type": "function"
}

下面是一个使用新编码器的示例存储合约 (GitHub code

pragma solidity ^0.5.3;
//注意此处
pragma experimental ABIEncoderV2;  //0.7.0 之前需添加支持
pragma abicoder v2; // 0.7.0 之后需要添加

contract storageContract {

  event Inserted(address _sender, address _recordId);
  event Updated(address _sender, address _recordId);
  event Deleted(address _sender, address _recordId);

  struct Data {
    string text;
  }
  struct Record {
    Data data;
    uint idListPointer;
  }

  mapping(address => Record) public Table;
  address[] public IdList;

  constructor() public {}

  // Check if recordId is in IdList, it's common for the record to be deleted and not by in the IdList anymore
  function Exists(address recordId) public view returns(bool exists) {
    if (IdList.length == 0) return false;
    return (IdList[Table[recordId].idListPointer] == recordId);
  }

  function GetLength() public view returns(uint count) {
    return IdList.length;
  }

  function GetByIndex(uint recordIndex) public view returns(address recordId, Data memory record) {
    require(recordIndex < IdList.length);
    return (IdList[recordIndex], Table[IdList[recordIndex]].data);
  }

  function GetById(address recordId) public view returns(uint index, Data memory record) {
    require(Exists(recordId));
    return (Table[recordId].idListPointer, Table[recordId].data);
  }

  function Insert(Data memory recordData) public returns(bool success) {
    address recordAddress = address(now);
    require(!Exists(recordAddress));
    Table[recordAddress].data = recordData;
    Table[recordAddress].idListPointer = IdList.push(recordAddress) - 1;
    emit Inserted(msg.sender, recordAddress);
    return true;
  }

  function Update(address recordId, Data memory recordData) public returns(bool success) {
    require(Exists(recordId));
    Table[recordId].data = recordData;
    emit Updated(msg.sender, recordId);
    return true;
  }

  // once a record has been deleted from the idList it can no longer be modified, but the memory remains
  // You can still pull deleted records if you hit the Table object directly, you will not be able to use GetByIndex or GetById
  function Delete(address recordId) public returns(bool success) {
    require(Exists(recordId));
    // get the record id to delete
    uint recordToDelete = Table[recordId].idListPointer;
    // set the last item in the id list to keep and move
    address keyToMove = IdList[IdList.length - 1];
    // replace the id of the deleted record with the one we want to keep i.e the last item
    IdList[recordToDelete] = keyToMove;
    // update the last record in the list to point to it's new position in the key list which is the old deleted records spot
    Table[keyToMove].idListPointer = recordToDelete;
    // remove the last element from the id list that holds the old pointer for the keep record
    IdList.length--;
    // emit event
    emit Deleted(msg.sender, recordId);
    return true;
  }

}

在函数参数中使用结构可以显着减少合约的混乱和复杂性,同时使它们交互起来更加愉快。

英文原文:https://medium.com/@dillonsvincent/solidity-enable-experimental-abiencoderv2-to-use-a-struct-as-function-parameter-27979603a879

注意

外部函数 不可以接受多维数组作为参数
如果原文件加入 pragma abicoder v2; 可以启用ABI v2版编码功能,这此功能可用。 (注:在 0.7.0 之前是使用pragma experimental ABIEncoderV2;

内部函数 则不需要启用ABI v2 就接受多维数组作为参数。
参考自:https://learnblockchain.cn/docs/solidity/contracts.html

以太坊合约方法参数的一些注意项

  1. 一个方法的参数最多有16个
    Solidity 允许 16 个参数。参考链接,EVM 不是寄存器机而是堆栈机,因此所有计算都在称为堆栈的数据区域上执行。它的最大大小为 1024 个元素并包含 256 位的字。通过以下方式对堆栈的访问仅限于顶端:可以将最顶端的 16 个元素之一复制到堆栈的顶部,或者将最顶端的元素与它下面的 16 个元素之一交换。

  2. 可以使用结构体作为参数
    https://ethereum.stackexchange.com/questions/65980/passing-struct-as-an-argument-in-call
    https://medium.com/@dillonsvincent/solidity-enable-experimental-abiencoderv2-to-use-a-struct-as-function-parameter-27979603a879

持续总结

参考链接

https://ethereum.stackexchange.com/questions/5945/how-many-arguments-can-solidity-take