Skip to content

Commit

Permalink
Merge pull request #28 from datachainlab/lo2152-multicall
Browse files Browse the repository at this point in the history
Lo2152 multicall
  • Loading branch information
siburu authored Jul 25, 2024
2 parents 36df633 + 5214e32 commit 1087765
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 9 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ replace (
)

require (
github.com/datachainlab/ethereum-ibc-relay-chain v0.3.6
github.com/datachainlab/ethereum-ibc-relay-chain v0.3.7
github.com/datachainlab/ibc-hd-signer v0.1.0
github.com/hyperledger-labs/yui-relayer v0.5.5
)
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -415,8 +415,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
github.com/datachainlab/ethereum-ibc-relay-chain v0.3.6 h1:7OUPp16JgyGgmsPj18FbaXhfQX0j5e5kKXO0NYFIeAA=
github.com/datachainlab/ethereum-ibc-relay-chain v0.3.6/go.mod h1:9rJTxXmEFVw8VrHYbKSK7bIQk1PVJm8pzyAYknMNNqc=
github.com/datachainlab/ethereum-ibc-relay-chain v0.3.7 h1:rwBkbaqKiqAS8Xb+4hUXoOylvuEoCQLgO84D2zTneeU=
github.com/datachainlab/ethereum-ibc-relay-chain v0.3.7/go.mod h1:ehlg1jKBVAW+JnFQIqRyCFGuzf4g0ZOECg58kV3uY2w=
github.com/datachainlab/ibc-hd-signer v0.1.0 h1:dmnFTAwFpl0m7Lx6+b+N/rrNpHQnXpyJAYnM25GhDi0=
github.com/datachainlab/ibc-hd-signer v0.1.0/go.mod h1:wUbLb2EryMCY+GfEsziU0T032Gch04jmrN0D4XGAfOI=
github.com/datachainlab/ibc-mock-client v0.4.1 h1:FQfyFOodgnchCIicpS7Vzji3yxXDe4Jl5hmE5Vz7M1s=
Expand Down
14 changes: 13 additions & 1 deletion tests/cases/eth2eth/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,19 @@ network:

.PHONY: test
test:
./scripts/fixture
$(MAKE) test-single
$(MAKE) test-multi

.PHONY: test-multi
test-multi:
./scripts/fixture multi
./scripts/init-rly
./scripts/handshake
./scripts/test-tx

.PHONY: test-single
test-single:
./scripts/fixture single
./scripts/init-rly
./scripts/handshake
./scripts/test-tx
Expand Down
1 change: 1 addition & 0 deletions tests/cases/eth2eth/configs/template/ibc-0.template.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"path": "m/44'/60'/0'/0/0"
},
"ibc_address": "PLACE_HOLDER",
"multicall3_address": "PLACE_HOLDER",
"initial_send_checkpoint": 1,
"initial_recv_checkpoint": 1,
"enable_debug_trace": true,
Expand Down
1 change: 1 addition & 0 deletions tests/cases/eth2eth/configs/template/ibc-1.template.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"path": "m/44'/60'/0'/0/0"
},
"ibc_address": "PLACE_HOLDER",
"multicall3_address": "PLACE_HOLDER",
"initial_send_checkpoint": 1,
"initial_recv_checkpoint": 1,
"enable_debug_trace": true,
Expand Down
21 changes: 17 additions & 4 deletions tests/cases/eth2eth/scripts/fixture
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/usr/bin/env bash
set -eux

ARG1=$1

DOCKER=docker
SCRIPT_DIR=$(cd $(dirname $0); pwd)
FIXTURES_DIR=${SCRIPT_DIR}/../fixtures
Expand All @@ -22,8 +24,19 @@ ${DOCKER} cp ethereum-geth0:/root/addresses ${FIXTURES_DIR}/ethereum/ibc0/addres
${DOCKER} cp ethereum-geth1:/root/addresses ${FIXTURES_DIR}/ethereum/ibc1/addresses

# assign the contract address to ChainConfig
IBC_HANDLER_ADDRESS_A=`cat ${FIXTURES_DIR}/ethereum/ibc0/addresses/IBCHandler`
IBC_HANDLER_ADDRESS_B=`cat ${FIXTURES_DIR}/ethereum/ibc1/addresses/IBCHandler`
mkdir -p "$CONF_DIR/chains"
jq ".chain.ibc_address |= \"$IBC_HANDLER_ADDRESS_A\"" < "$CONF_DIR/template/ibc-0.template.json" > "$CONF_DIR/chains/ibc-0.json"
jq ".chain.ibc_address |= \"$IBC_HANDLER_ADDRESS_B\"" < "$CONF_DIR/template/ibc-1.template.json" > "$CONF_DIR/chains/ibc-1.json"
for id in 0 1; do
IBC_HANDLER_ADDRESS=`cat ${FIXTURES_DIR}/ethereum/ibc${id}/addresses/IBCHandler`
MULTICALL3_ADDRESS=`cat ${FIXTURES_DIR}/ethereum/ibc${id}/addresses/Multicall3`
if [ "$ARG1" = "multi" ]; then
cat "$CONF_DIR/template/ibc-${id}.template.json" \
| jq ".chain.ibc_address |= \"$IBC_HANDLER_ADDRESS\"" \
| jq ".chain.multicall3_address |= \"$MULTICALL3_ADDRESS\"" \
> "$CONF_DIR/chains/ibc-${id}.json"
else
cat "$CONF_DIR/template/ibc-${id}.template.json" \
| jq ".chain.ibc_address |= \"$IBC_HANDLER_ADDRESS\"" \
| jq ".chain.multicall3_address |= null" \
> "$CONF_DIR/chains/ibc-${id}.json"
fi
done
2 changes: 2 additions & 0 deletions tests/chains/ethereum/contracts/contracts/Dependencies.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ import {MockClient} from "@hyperledger-labs/yui-ibc-solidity/contracts/clients/M
import {ERC20Token} from "@hyperledger-labs/yui-ibc-solidity/contracts/apps/20-transfer/ERC20Token.sol";
import {ICS20Bank} from "@hyperledger-labs/yui-ibc-solidity/contracts/apps/20-transfer/ICS20Bank.sol";
import {ICS20TransferBank} from "@hyperledger-labs/yui-ibc-solidity/contracts/apps/20-transfer/ICS20TransferBank.sol";

import {Multicall3} from "./Multicall3.sol";
216 changes: 216 additions & 0 deletions tests/chains/ethereum/contracts/contracts/Multicall3.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

/// @title Multicall3
/// @notice Aggregate results from multiple function calls
/// @dev Multicall & Multicall2 backwards-compatible
/// @dev Aggregate methods are marked `payable` to save 24 gas per call
/// @author Michael Elliot <[email protected]>
/// @author Joshua Levine <[email protected]>
/// @author Nick Johnson <[email protected]>
/// @author Andreas Bigger <[email protected]>
/// @author Matt Solomon <[email protected]>
contract Multicall3 {
struct Call {
address target;
bytes callData;
}

struct Call3 {
address target;
bool allowFailure;
bytes callData;
}

struct Call3Value {
address target;
bool allowFailure;
uint256 value;
bytes callData;
}

struct Result {
bool success;
bytes returnData;
}

/// @notice Backwards-compatible call aggregation with Multicall
/// @param calls An array of Call structs
/// @return blockNumber The block number where the calls were executed
/// @return returnData An array of bytes containing the responses
function aggregate(Call[] calldata calls) public payable returns (uint256 blockNumber, bytes[] memory returnData) {
blockNumber = block.number;
uint256 length = calls.length;
returnData = new bytes[](length);
Call calldata call;
for (uint256 i = 0; i < length;) {
bool success;
call = calls[i];
(success, returnData[i]) = call.target.call(call.callData);
require(success, "Multicall3: call failed");
unchecked { ++i; }
}
}

/// @notice Backwards-compatible with Multicall2
/// @notice Aggregate calls without requiring success
/// @param requireSuccess If true, require all calls to succeed
/// @param calls An array of Call structs
/// @return returnData An array of Result structs
function tryAggregate(bool requireSuccess, Call[] calldata calls) public payable returns (Result[] memory returnData) {
uint256 length = calls.length;
returnData = new Result[](length);
Call calldata call;
for (uint256 i = 0; i < length;) {
Result memory result = returnData[i];
call = calls[i];
(result.success, result.returnData) = call.target.call(call.callData);
if (requireSuccess) require(result.success, "Multicall3: call failed");
unchecked { ++i; }
}
}

/// @notice Backwards-compatible with Multicall2
/// @notice Aggregate calls and allow failures using tryAggregate
/// @param calls An array of Call structs
/// @return blockNumber The block number where the calls were executed
/// @return blockHash The hash of the block where the calls were executed
/// @return returnData An array of Result structs
function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls) public payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) {
blockNumber = block.number;
blockHash = blockhash(block.number);
returnData = tryAggregate(requireSuccess, calls);
}

/// @notice Backwards-compatible with Multicall2
/// @notice Aggregate calls and allow failures using tryAggregate
/// @param calls An array of Call structs
/// @return blockNumber The block number where the calls were executed
/// @return blockHash The hash of the block where the calls were executed
/// @return returnData An array of Result structs
function blockAndAggregate(Call[] calldata calls) public payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) {
(blockNumber, blockHash, returnData) = tryBlockAndAggregate(true, calls);
}

/// @notice Aggregate calls, ensuring each returns success if required
/// @param calls An array of Call3 structs
/// @return returnData An array of Result structs
function aggregate3(Call3[] calldata calls) public payable returns (Result[] memory returnData) {
uint256 length = calls.length;
returnData = new Result[](length);
Call3 calldata calli;
for (uint256 i = 0; i < length;) {
Result memory result = returnData[i];
calli = calls[i];
(result.success, result.returnData) = calli.target.call(calli.callData);
assembly {
// Revert if the call fails and failure is not allowed
// `allowFailure := calldataload(add(calli, 0x20))` and `success := mload(result)`
if iszero(or(calldataload(add(calli, 0x20)), mload(result))) {
// set "Error(string)" signature: bytes32(bytes4(keccak256("Error(string)")))
mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000)
// set data offset
mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020)
// set length of revert string
mstore(0x24, 0x0000000000000000000000000000000000000000000000000000000000000017)
// set revert string: bytes32(abi.encodePacked("Multicall3: call failed"))
mstore(0x44, 0x4d756c746963616c6c333a2063616c6c206661696c6564000000000000000000)
revert(0x00, 0x64)
}
}
unchecked { ++i; }
}
}

/// @notice Aggregate calls with a msg value
/// @notice Reverts if msg.value is less than the sum of the call values
/// @param calls An array of Call3Value structs
/// @return returnData An array of Result structs
function aggregate3Value(Call3Value[] calldata calls) public payable returns (Result[] memory returnData) {
uint256 valAccumulator;
uint256 length = calls.length;
returnData = new Result[](length);
Call3Value calldata calli;
for (uint256 i = 0; i < length;) {
Result memory result = returnData[i];
calli = calls[i];
uint256 val = calli.value;
// Humanity will be a Type V Kardashev Civilization before this overflows - andreas
// ~ 10^25 Wei in existence << ~ 10^76 size uint fits in a uint256
unchecked { valAccumulator += val; }
(result.success, result.returnData) = calli.target.call{value: val}(calli.callData);
assembly {
// Revert if the call fails and failure is not allowed
// `allowFailure := calldataload(add(calli, 0x20))` and `success := mload(result)`
if iszero(or(calldataload(add(calli, 0x20)), mload(result))) {
// set "Error(string)" signature: bytes32(bytes4(keccak256("Error(string)")))
mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000)
// set data offset
mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020)
// set length of revert string
mstore(0x24, 0x0000000000000000000000000000000000000000000000000000000000000017)
// set revert string: bytes32(abi.encodePacked("Multicall3: call failed"))
mstore(0x44, 0x4d756c746963616c6c333a2063616c6c206661696c6564000000000000000000)
revert(0x00, 0x84)
}
}
unchecked { ++i; }
}
// Finally, make sure the msg.value = SUM(call[0...i].value)
require(msg.value == valAccumulator, "Multicall3: value mismatch");
}

/// @notice Returns the block hash for the given block number
/// @param blockNumber The block number
function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) {
blockHash = blockhash(blockNumber);
}

/// @notice Returns the block number
function getBlockNumber() public view returns (uint256 blockNumber) {
blockNumber = block.number;
}

/// @notice Returns the block coinbase
function getCurrentBlockCoinbase() public view returns (address coinbase) {
coinbase = block.coinbase;
}

/// @notice Returns the block difficulty
function getCurrentBlockDifficulty() public view returns (uint256 difficulty) {
difficulty = block.difficulty;
}

/// @notice Returns the block gas limit
function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) {
gaslimit = block.gaslimit;
}

/// @notice Returns the block timestamp
function getCurrentBlockTimestamp() public view returns (uint256 timestamp) {
timestamp = block.timestamp;
}

/// @notice Returns the (ETH) balance of a given address
function getEthBalance(address addr) public view returns (uint256 balance) {
balance = addr.balance;
}

/// @notice Returns the block hash of the last block
function getLastBlockHash() public view returns (bytes32 blockHash) {
unchecked {
blockHash = blockhash(block.number - 1);
}
}

/// @notice Gets the base fee of the given block
/// @notice Can revert if the BASEFEE opcode is not implemented by the given chain
function getBasefee() public view returns (uint256 basefee) {
basefee = block.basefee;
}

/// @notice Returns the chain id
function getChainId() public view returns (uint256 chainid) {
chainid = block.chainid;
}
}
5 changes: 4 additions & 1 deletion tests/chains/ethereum/contracts/scripts/deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ async function deployIBC(deployer) {
"IBCChannelPacketSendRecv",
"IBCChannelPacketTimeout",
"IBCChannelUpgradeInitTryAck",
"IBCChannelUpgradeConfirmTimeoutCancel"
"IBCChannelUpgradeConfirmTimeoutCancel",
];
const logics = [];
for (const name of logicNames) {
Expand Down Expand Up @@ -71,6 +71,9 @@ async function main() {
const mockClient = await deploy(deployer, "MockClient", [ibcHandler.target]);
saveAddress("MockClient", mockClient);

const multicall3 = await deploy(deployer, "Multicall3", []);
saveAddress("Multicall3", multicall3);

await ibcHandler.bindPort("transfer", ics20transferbank.target);
await ibcHandler.registerClient("mock-client", mockClient.target);
await ics20bank.setOperator(ics20transferbank.target);
Expand Down

0 comments on commit 1087765

Please sign in to comment.