1、MultiCall
一个RPC节点限制了客户端对链的调用,在20秒间隔之内只能调用一次,如果需要多次调用方法,那么可以将多次调用命令打包,仅对RPC节点进行一次调用。
因此,本节使用一种方法实现了使用一个Call同时调用一个或多个合约的多个方法的例子。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;
contract TestMultiCall{
function fun1() external view returns(uint,uint){
return(1,block.timestamp);
}
function fun2() external view returns(uint,uint){
return(2,block.timestamp);
}
function getfun1() external pure returns (bytes memory){
//abi.encodeWithSignature("fun1()")
//获取fun1的机器码
return abi.encodeWithSelector(this.fun1.selector);
}
function getfun2() external pure returns (bytes memory){
return abi.encodeWithSelector(this.fun2.selector);
}
}
contract MultiCall{
function multiCall(address[] calldata targets,bytes[] calldata data)
external
view
returns(bytes[] memory)
{
//合约地址长度应当与输入参数的数据相等
require(targets.length == data.length,"target length != data length");
bytes[] memory results = new bytes[](data.length);
for(uint i;i
2、ABI解码
对已经成为机器码的数据进行解码,在解码时应知道数组类型
abi.decode(data,(uint,address,uint[]))
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;
contract AbiDecode{
struct MyStruct{
string name;
uint[2] nums;
}
function encode(
uint x,
address addr,
uint[] calldata arr,
MyStruct calldata myStruct
) external pure returns (bytes memory) {
return abi.encode(x,addr,arr,myStruct);
}
function decode(bytes calldata data)external pure
returns(
uint x,address addr,uint[] memory arr,MyStruct memory mystruct
){
(x,addr,arr,mystruct) = abi.decode(data, (uint,address,uint[],MyStruct));
}
}
结果:
3、gas优化
(1)内存变量的运算成本低于存储变量
(2)不进行二次赋值
(3)小技巧
(4)缓存数组的长度
在循环中,每次循环都要读取数组长度,因此需要缓存数组的长度以减少gas浪费。
(5)将数组元素提前赋值在内存变量中
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;
contract notGasGolf{
uint public total;
function sumIfEvenAndlessThen99(uint[] memory nums) external {
for (uint i = 0; i
4、时间锁合约
经常用在Dapp和Defi上,用于保护管理员权限,如果针对合约进行重要操作,排在队列中等待48小时或更长时间,如果发现该操作有作恶行为,那么通过时间锁合约能够及时取消。
任何用户部署合约都需要具有锁定期。
交易延迟:在最小的交易延迟之后和最大的交易延迟之前能够执行合约
(1)将交易推入等待队列
(2)到达时间后可执行交易
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;
contract TimeLock{
error NotOwnerError();
error AlreadyQueueError(bytes32 txId);
error timestampNotInRangeError(uint blockTimestamp,uint timestamp);
error NotQueuedError(bytes32 txId);
error timestampNotPassedError(uint blockTimestamp,uint timestamp);
error TimestampExpiredError(uint blockTimestamp,uint timestamp);
error TxFailedError();
uint public constant MIN_DELAY = 10;
uint public constant MAX_DELAY = 1000;
uint public constant GRACE_PERIOD = 1000;
address public owner;
mapping(bytes32 => bool) public queued;
event Queue(bytes32 indexed txId,address indexed target,uint value,string func,bytes data,uint timestamp);
event Execute(bytes32 indexed txId,address indexed target,uint value,string func,bytes data,uint timestamp);
event Cancel(bytes32 txId);
constructor(){
owner = msg.sender;
}
receive() external payable{}
modifier onlyOwner(){
if(msg.sender != owner){
revert NotOwnerError();
}
_;
}
//对函数进行打包
function getTxId(
address _target,
uint _value,
string calldata _func,
bytes calldata _data,
uint _timestamp
) public pure returns (bytes32 txId){
return keccak256(
abi.encode(
_target,
_value,
_func,
_data,
_timestamp
)
);
}
function queue(
address _target,
uint _value,
string calldata _func,
bytes calldata _data,
uint _timestamp
) external onlyOwner {
//create tx id
bytes32 txId = getTxId(_target,_value,_func,_data,_timestamp);
//check tx id unique
if (queued[txId]){
revert AlreadyQueueError(txId);
}
//check timestamp
if (_timestamp < block.timestamp + MIN_DELAY || _timestamp > block.timestamp + MAX_DELAY){
revert timestampNotInRangeError(block.timestamp,_timestamp);
}
//queue tx
queued[txId] = true;
//记录某事件推入队列中
emit Queue(
txId,_target,_value,_func,_data,_timestamp
);
}
function execute(
address _target,
uint _value,
string calldata _func,
bytes calldata _data,
uint _timestamp
) external payable onlyOwner returns (bytes memory){
bytes32 txId = getTxId(_target,_value,_func,_data,_timestamp);
//check txId is in queue
if(!queued[txId]){
revert NotQueuedError(txId);
}
//check block.timestamp > _timestamp
if(block.timestamp < _timestamp){
revert timestampNotPassedError(block.timestamp,_timestamp);
}
//block.timestamp < _timestamp+grace
if(block.timestamp > _timestamp + GRACE_PERIOD){
revert TimestampExpiredError(block.timestamp,_timestamp + GRACE_PERIOD);
}
//delete tx from queue
queued[txId] = false;
//execute the tx
bytes memory data;
//判断函数是否是对方合约的回退函数
if(bytes(_func).length > 0){
data = abi.encodePacked(
bytes4(keccak256(bytes(_func)))
);
}else{
data = _data;
}
(bool ok,bytes memory res) = _target.call{value:_value}(data);
if(!ok){
revert TxFailedError();
}
emit Execute(txId,_target, _value, _func, _data, _timestamp);
return res;
}
function cancel(bytes32 _txId) external onlyOwner{
if(!queued[_txId]){
revert NotQueuedError(_txId);
}
queued[_txId] = false;
emit Cancel(_txId);
}
}
contract test{
uint public value;
function Test() public {
value = 10;
}
function getTimestamp() external view returns(uint){
return block.timestamp +100;
}
}