Solidez gramática básica y casos sencillos 20221204

1, multillamada

        Un nodo RPC limita las llamadas del cliente a la cadena, a la que solo se puede llamar una vez dentro de un intervalo de segundos 20. Si necesita llamar al método varias veces, puede empaquetar los comandos de llamada múltiple y solo llamar al nodo RPC una vez. 

        Por lo tanto, esta sección usa un método para implementar un ejemplo del uso de una Llamada para llamar a múltiples métodos de uno o más contratos al mismo tiempo.

  • código selector
    • Esta sección utiliza la codificación de selector para generar el código de máquina del método de destino (abi.encodeWithSelector)
    •  abi.encodeWithSelector(this.fun1.selector);
    • Ventaja: no es necesario ingresar el nombre de la función y los parámetros en forma de cadena
  • llamada estática
    • En esta sección, al llamar al método del contrato de destino, se adopta el método de llamada estática en lugar de llamada dinámica.
    • La llamada es una llamada dinámica, que puede causar errores al llamar a varios métodos al mismo tiempo.
  • Pasar el parámetro de matriz
    • Al pasar un parámetro de matriz, use el formato ["A","B"]

// 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<targets.length;i++){
            //进行多重调用时采用静态调用的方式(staticcall),因为底层调用call会采用动态的写入调用方法
            //这条语句的意思是:对于目标地址target[i],使用静态方式staticcall()传入机器码data,并返回调用是否成功以及调用的结果
            (bool success,bytes memory result) = targets[i].staticcall(data[i]);
            //在使用底层call调用方法时,可以使用require确认方法结果,如果调用失败终止程序
            require(success,"call failed");
            results[i] = result;
        }

        return results;
    }
}

2. Decodificación ABI

Para decodificar datos que se han convertido en código de máquina, se debe conocer el tipo de matriz al decodificar

abi.decode(datos,(uint,dirección,uint[]))

  • Introduzca los datos de la estructura:

    • Los datos de la estructura se ingresan como una matriz multidimensional
    • ejemplo:
  • el código
// 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));
    }

}

resultado:

 3. Optimización de gases

(1) El costo de operación de las variables de memoria es menor que el de las variables de almacenamiento

  • cambiar la memoria a calldata
  • Para una variable llamada varias veces en un método, configure la variable de memoria y finalmente escriba la variable de memoria en la variable de almacenamiento una vez

(2) Sin asignación secundaria

  • Use la condición de juicio directamente en lugar de la asignación booleana

(3) Consejos

  •  Reemplace i++ con ++i

(4) La longitud de la matriz de caché

En el bucle, la longitud de la matriz debe leerse cada vez que se pasa por el bucle, por lo que la longitud de la matriz debe almacenarse en caché para reducir el desperdicio de gas.

(5) Asigne elementos de matriz en variables de memoria por adelantado

// 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<nums.length; i+=1){
            bool isEven = nums[i] % 2 == 0;
            bool isLessThan99 = nums[i] < 99;
            if(isEven && isLessThan99){
                total += nums[i];
            }
        }
    }
}

contract GasGolf{
    uint public total;
    function sumIfEvenAndlessThen99(uint[] memory nums) external {
        uint _total = total;
        uint len = nums.length;

        for (uint i = 0; i<len; ++i){
            uint num = nums[i];
            if(num % 2 == 0 && num < 99){
                _total += num;
            }
        }
        total = _total;
    }
}

4. Contrato de bloqueo de tiempo

        A menudo se usa en Dapp y Defi para proteger los derechos de administrador. Si se realiza una operación importante en el contrato, se pondrá en cola durante 48 horas o más. Si se determina que la operación es maliciosa, se puede cancelar a tiempo a través del contrato de bloqueo de tiempo.

        Cualquier usuario que implemente un contrato debe tener un período de bloqueo.

        Latencia de transacción: capacidad de ejecutar contratos después de la latencia mínima de transacción y antes de la latencia máxima de transacción

(1) Empuje la transacción a la cola de espera

(2) La transacción se puede ejecutar después de que se alcance el tiempo

// 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;
    }
}

Supongo que te gusta

Origin blog.csdn.net/H_Roger/article/details/128169321
Recomendado
Clasificación