苏铭客

导航

21条以太坊solidity智能合约开发最佳实践

1.处理合约调用的方法

  call:最常用的调用方式,调用后内置变量 msg 的值会修改为调用者,执行环境为被调用者的运行环境。

  delegatecall:调用后内置变量 msg 的值不会修改为调用者,但执行环境为调用者的运行环境。(常用)  

  callcode: 调用后内置变量 msg 的值会修改为调用者,但执行环境为调用者的运行环境。

  跨合约调用,最好先实例化一个合约

  A contractA = new A(xxx);
  contractA.doSomething();

  call,delegatecall 的返回值都是bool。并没有具体执行后的返回值

 

2.关于assert,require, if 使用

  最佳实践是使用if判断。assert() require() 如果调用不成立合约就会抛出异常。
  使用if判断可以将错误的参数信息记入log日志中方面查看

 

3.标注函数,变量的可见性

  public : 修饰的变量和函数,任何用户或者合约都能调用和访问

  private : 修饰的变量和函数,只能在其所在的合约中调用和访问,即使是其子合约也没有权限访问

  internal :  如果某个合约继承自其父合约,这个合约也可以访问父合约中定义的internal函数。(internal可继承性访问

  external :  external修饰的函数只能在合约之外调用 - 不能被合约内的其他函数调用

 

4.区分函数和事件

  //不友好的写法

 event Transfer() {}
 function transfer() {}

  //友好形象的写法

 event LogTransfer() {}
 function transfer() external {}

  事件尽量以Log为前缀与函数区分开

 

5.智能合约日志设计最佳实践

  log事件记录尽量存入一两个日志合约中。并且需要被查询的字段尽量保证在事件相同的索引处。

  如:要跟踪查询一笔资产的流转步骤,那么每笔流转过程中写入资产编号的日志,确保在该字段是位于同一个位置索引。如下
  contract A{
    event LogCreateA(bool boo,bytes32 indexed assetId,string msg);
  }

  contract B{
    event LogCreateB(uint u,bytes32 indexed assetId,bytes32 other);
  }

  注意:一个事件方法中,最多只能有三个参数可接受indexed事件参数的属性

 

6.带流程编码的智能合约设计

  合约设计资产流转过程中,最好保存所有资产流转过的节点编码,而不是修改单一的修改状态。如:Apply,Accept,Audit,Agree ,End。

  使用bytes32[] public flows; 使用数组存储,这样便于回归查询出资产的流转轨迹过程。

 

7.solidity合约方法参数受限

  solidity <0.4.25版本 合约传入和返回参数不能超过11个。高版本的合约目前还不清楚。

 

8.合约方法中创建数组的正确方式

 //错误
 bytes32[] memory types = new bytes32[array.length];
 //正确
 bytes32[] memory types = new bytes32[](array.length);

 

9.删除数组的方式

 delete array[k];
 array[k]=array[array.length-1];
 array.length--;

 

10.合约中引入其他合约时,最好为其添加set方法。方便后期合约修改或升级调整

 LogAsset public logAsset
 function setLogAsset(address _log) isOwner public{
  logAsset = _log;
 }

 

11.合约继承中子类继承父类,先继承父类构造,在写modifier修饰器

 constructor(address _addr, uint _num, address[] _voters) Voteable(_num, _voters) Base(_addr) isOwner public {
  owner = tx.origin;
  creator = msg.sender;
 }

  注意:子类继承父类,必须要继承父类的构造

 

12.solidity 基础类型简称

 uint<==>uint256 
 byte <==>bytes1
 sha3<==>keccak256
 now <==> block.timestamp
 uint160 <==> address

 

13.合约中被public 修饰的变量,evm自身对外生成get方法

  如:  address public localAddr;

  合约编译部署上链后,自动生成localAddr() 方法,函数签名: 597d0a2f

 

14.solidity 基本类型默认值

 uint t;  //0
 int n;  // 0
 string str;  // ""
 address addr;  // 0x0000000000000000000000000000000000000000
 bool b;  // false

 

15. solidity abi内置编码函数

 abi.encode(...) returns (bytes) //encode编码给定参数
abi.encodePacked(...) returns (bytes) //简单编码,对于动态类型不会填充补齐64
abi.encodeWithSelector(bytes4 selector, ...) returns (bytes) //计算函数选择器和参数的ABI编码
abi.encodeWithSignature(string signature, ...) returns (bytes) //获取函数签名,等价于* abi.encodeWithSelector(bytes4(keccak256(signature), ...)
keccak256(abi.encodePacked("xx1","xx2")) //对"xx1","xx2"进行加密

 

16. string[] 可以在合约中静态定义变量数组,但是不能从外部传参string[]或返回strnig[]

 

17. solidity 可以返回对象,只不过是在合约内部使用internal

 struct Info{
  bytes32 name;
  bytes32 code;
  bytes32 phone;
 }

 function abcd()internal view returns(Info){
  Info memory info = Info("name","code","123423234");
  return info;
 }
//内部函数 internal 可以在接受或返回结构体对象。 function saveVote(Voter v) internal view returns(Voter fs){   v.weight = v.weight +1;   if(!v.voted){     v.voted = true;   }   fs = v; }

 

18.在合约中含有结构体时,需要判断结构体是否存在,最好加一个bool类型。

 //bool 标识该结构体对象是否存在,确保每次赋值时都要给结构体boo赋值true
 struct License{
  bytes32 ltype;
  bool boo; //是否存在
 }
 mapping(bytes32 => License) public map_license;
 if(map_license[_licenses].boo)

 

19.结构体赋值 两种方式

 struct SettleInfo{ 
  uint stype;
  string reason;
  bool ifSettle;
 }
 map_settle[bytes32(_loan)] = SettleInfo({reason:_reason,ifSettle:true,stype:_type}); //指定了结构体属性名称,可以无序赋值
 map_settle[bytes32(_loan)] = SettleInfo(1,"reason",true); //按结构体顺序赋值

 

20. constant,view,pure 关键字
  constant: 常量字段的定义修饰符
  view: 只能读取状态变量但是不能改
  pure:pure修饰的函数不能改也不能读状态变量。

 //没有更改任何状态变量,或读取状态变量。仅仅是对输入值做了操作返回
 function f(uint a, uint b) public pure returns (uint) {
  return a * (b + 42);
 }

 

21. 提升可读性,尽量将默认返回值补全

 //bad
 function compareNumber(uint num1, uint num2, bytes32 symbol) internal pure returns (bool){
  if (symbol == ">=" && num1 >= num2) return true;
  else if (symbol == "<=" && num1 <= num2) return true;
 }

 //good
 function compareNumber(uint num1, uint num2, bytes32 symbol) internal pure returns (bool){
  if (symbol == ">=" && num1 >= num2) return true;
  else if (symbol == "<=" && num1 <= num2) return true;
  return false; }

 

关注公众号"刻意链习",获取更多区块链技术知识分享, 欢迎转载,转载请注明出处!

posted on 2019-09-28 19:09  苏铭客  阅读(1317)  评论(0编辑  收藏  举报