Criar contrato para Multichamada de funções

porMatheusem15/06/2022

Nesse artigo iremos aprender a como criar um contrato inteligente para realizar multichamada de funções.

Multichamada de funções

Um exemplo de contrato que agrega várias consultas usando um loop for e staticcall.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract MultiCall {
function multiCall(address[] calldata targets, bytes[] calldata data)
external
view
returns (bytes[] memory)
{
require(targets.length == data.length, "comprimento de destino != comprimento de dados");
bytes[] memory results = new bytes[](data.length);
for (uint i; i < targets.length; i++) {
(bool success, bytes memory result) = targets[i].staticcall(data[i]);
require(success, "falha na chamada");
results[i] = result;
}
return results;
}
}

Contrato para testar MultiCall

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract TestMultiCall {
function test(uint _i) external pure returns (uint) {
return _i;
}
function getData(uint _i) external pure returns (bytes memory) {
return abi.encodeWithSelector(this.test.selector, _i);
}
}

Multichamada de funções

Um exemplo de chamada de várias funções com uma única transação, usando delegatecall.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract MultiDelegatecall {
error DelegatecallFailed();
function multiDelegatecall(bytes[] memory data)
external
payable
returns (bytes[] memory results)
{
results = new bytes[](data.length);
for (uint i; i < data.length; i++) {
(bool ok, bytes memory res) = address(this).delegatecall(data[i]);
if (!ok) {
revert DelegatecallFailed();
}
results[i] = res;
}
}
}
// Por que usar multi delegatecall? Por que não multi-chamada?
// alice -> multi call --- call ---> test (msg.sender = multi call)
// alice -> teste --- delegatecall ---> test (msg.sender = alice)
contract TestMultiDelegatecall is MultiDelegatecall {
event Log(address caller, string func, uint i);
function func1(uint x, uint y) external {
// msg.sender = alice
emit Log(msg.sender, "func1", x + y);
}
function func2() external returns (uint) {
// msg.sender = alice
emit Log(msg.sender, "func2", 2);
return 111;
}
mapping(address => uint) public balanceOf;
// AVISO: código inseguro quando usado em combinação com multi-delegatecall
// o usuário pode cunhar várias vezes pelo preço de msg.value
function mint() external payable {
balanceOf[msg.sender] += msg.value;
}
}
contract Helper {
function getFunc1Data(uint x, uint y) external pure returns (bytes memory) {
return abi.encodeWithSelector(TestMultiDelegatecall.func1.selector, x, y);
}
function getFunc2Data() external pure returns (bytes memory) {
return abi.encodeWithSelector(TestMultiDelegatecall.func2.selector);
}
function getMintData() external pure returns (bytes memory) {
return abi.encodeWithSelector(TestMultiDelegatecall.mint.selector);
}
}

Testar no Remix