From b9ba3f1743949e386a729e3c17117983db09ee67 Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Thu, 30 Nov 2023 17:12:30 -0500 Subject: [PATCH 1/7] forge install: openzeppelin-contracts v5.0.0 --- .gitmodules | 3 +++ lib/openzeppelin-contracts | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/openzeppelin-contracts diff --git a/.gitmodules b/.gitmodules index a92cdcee..d8089fee 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/trusted-multicall-forwarder"] path = lib/trusted-multicall-forwarder url = https://github.com/Synthetixio/trusted-multicall-forwarder +[submodule "lib/openzeppelin-contracts"] + path = lib/openzeppelin-contracts + url = https://github.com/openzeppelin/openzeppelin-contracts diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts new file mode 160000 index 00000000..932fddf6 --- /dev/null +++ b/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit 932fddf69a699a9a80fd2396fd1a2ab91cdda123 From 2028e9eae4337a5e45e5e67dcba365bca1cab921 Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Thu, 30 Nov 2023 17:32:05 -0500 Subject: [PATCH 2/7] =?UTF-8?q?=F0=9F=97=91=EF=B8=8F=20Remove=20trusted=20?= =?UTF-8?q?forwarder=20from=20lib/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitmodules | 3 --- lib/trusted-multicall-forwarder | 1 - 2 files changed, 4 deletions(-) delete mode 160000 lib/trusted-multicall-forwarder diff --git a/.gitmodules b/.gitmodules index d8089fee..e80ffd88 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std -[submodule "lib/trusted-multicall-forwarder"] - path = lib/trusted-multicall-forwarder - url = https://github.com/Synthetixio/trusted-multicall-forwarder [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts url = https://github.com/openzeppelin/openzeppelin-contracts diff --git a/lib/trusted-multicall-forwarder b/lib/trusted-multicall-forwarder deleted file mode 160000 index 77ff94f4..00000000 --- a/lib/trusted-multicall-forwarder +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 77ff94f448d8b747c4c297b38a54a80dbe5c9054 From 8dcc4fa19e6d6a56f3065e3ae42e066aaa1429a7 Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Thu, 30 Nov 2023 17:32:41 -0500 Subject: [PATCH 3/7] =?UTF-8?q?=F0=9F=91=B7=F0=9F=8F=BB=E2=80=8D=E2=99=82?= =?UTF-8?q?=EF=B8=8F=20Add=20updated=20trusted=20forwarder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Engine.sol | 2 +- test/TrustedMulticallForwarder.t.sol | 204 +++++++++++++++++++++++++++ 2 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 test/TrustedMulticallForwarder.t.sol diff --git a/src/Engine.sol b/src/Engine.sol index 85f4b504..097ef0e3 100644 --- a/src/Engine.sol +++ b/src/Engine.sol @@ -6,7 +6,7 @@ import {ConditionalOrderHashLib} from import {EIP712} from "src/utils/EIP712.sol"; import {EIP7412} from "src/utils/EIP7412.sol"; import {ERC2771Context} from - "lib/trusted-multicall-forwarder/lib/openzeppelin-contracts/contracts/metatx/ERC2771Context.sol"; + "lib/openzeppelin-contracts/contracts/metatx/ERC2771Context.sol"; import {IEngine, IPerpsMarketProxy} from "src/interfaces/IEngine.sol"; import {IERC20} from "src/interfaces/tokens/IERC20.sol"; import {IPyth, PythStructs} from "src/interfaces/oracles/IPyth.sol"; diff --git a/test/TrustedMulticallForwarder.t.sol b/test/TrustedMulticallForwarder.t.sol new file mode 100644 index 00000000..fa76a7ff --- /dev/null +++ b/test/TrustedMulticallForwarder.t.sol @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import {Test} from "../lib/forge-std/src/Test.sol"; +import {Vm} from "../lib/forge-std/src/Vm.sol"; + +import { + TrustedMulticallForwarder, + ERC2771Forwarder, + Address +} from "src/utils/TrustedMulticallForwarder.sol"; + +contract ERC2771Example { + function isTrustedForwarder(address /*forwarder*/ ) + public + pure + returns (bool) + { + return true; + } + + function ping() public payable returns (string memory) { + return "pong"; + } +} + +contract TrustedMulticallForwarderTest is Test { + // contract(s) + TrustedMulticallForwarder internal trustedMulticallForwarder; + ERC2771Example internal erc2771Example; + + // actor(s) + address internal signer; + uint256 internal signerPrivateKey = 0x1; + address internal badSigner; + uint256 internal badSignerPrivateKey = 0x2; + + function setUp() public { + // deploy contract(s) + trustedMulticallForwarder = new TrustedMulticallForwarder(); + erc2771Example = new ERC2771Example(); + + // initialize actor specific state + signer = vm.addr(signerPrivateKey); + badSigner = vm.addr(badSignerPrivateKey); + } +} + +contract ExecuteBatch is TrustedMulticallForwarderTest { + bytes32 private constant _TYPE_HASH = keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ); + + bytes32 internal constant _FORWARD_REQUEST_TYPEHASH = keccak256( + "ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,uint48 deadline,bytes data)" + ); + + function getForwardRequestDataSignatureRaw( + ERC2771Forwarder.ForwardRequestData memory request, + uint256 nonce, + uint256 privateKey, + bytes32 domainSeparator + ) internal returns (uint8 v, bytes32 r, bytes32 s) { + bytes32 msgHash = keccak256( + abi.encodePacked( + "\x19\x01", + domainSeparator, + keccak256( + abi.encode( + _FORWARD_REQUEST_TYPEHASH, + request.from, + request.to, + request.value, + request.gas, + nonce, + request.deadline, + keccak256(request.data) + ) + ) + ) + ); + + (v, r, s) = vm.sign(privateKey, msgHash); + } + + function getForwardRequestDataSignature( + ERC2771Forwarder.ForwardRequestData memory request, + uint256 nonce, + uint256 privateKey, + bytes32 domainSeparator + ) internal returns (bytes memory sig) { + (uint8 v, bytes32 r, bytes32 s) = getForwardRequestDataSignatureRaw( + request, nonce, privateKey, domainSeparator + ); + return bytes.concat(r, s, bytes1(v)); + } + + function test_executeBatch() public { + // tx details + uint256 value = 1 ether; + uint256 gas = 1 ether; + + // prepare forward request data (with empty signature) + ERC2771Forwarder.ForwardRequestData memory request = ERC2771Forwarder + .ForwardRequestData({ + from: address(signer), + to: address(erc2771Example), + value: value, + gas: gas, + deadline: type(uint48).max, + data: abi.encodeWithSelector(ERC2771Example.ping.selector), + signature: bytes("") // initially empty + }); + + // define domain separator + bytes32 domainSeparator = keccak256( + abi.encode( + _TYPE_HASH, + keccak256(bytes("trusted-multicall-forwarder")), + keccak256(bytes("1")), + block.chainid, + address(trustedMulticallForwarder) + ) + ); + + // sign forward request data + bytes memory signature = getForwardRequestDataSignature( + request, + trustedMulticallForwarder.nonces(address(signer)), + signerPrivateKey, + domainSeparator + ); + + // update forward request data object with signature + request.signature = signature; + + // define batch of forward requests + ERC2771Forwarder.ForwardRequestData[] memory batch = + new ERC2771Forwarder.ForwardRequestData[](1); + batch[0] = request; + + // execute batch + TrustedMulticallForwarder.Result[] memory results = + trustedMulticallForwarder.executeBatch{value: value, gas: gas}({ + requests: batch + }); + + // check results + assertEq(results.length, 1); + assertEq(results[0].success, true); + assertEq(results[0].returnData, abi.encode("pong")); + } + + function test_executeBatch_invalid_signature() public { + // tx details + uint256 value = 1 ether; + uint256 gas = 1 ether; + + // prepare forward request data (with empty signature) + ERC2771Forwarder.ForwardRequestData memory request = ERC2771Forwarder + .ForwardRequestData({ + from: address(signer), + to: address(erc2771Example), + value: value, + gas: gas, + deadline: type(uint48).max, + data: abi.encodeWithSelector(ERC2771Example.ping.selector), + signature: bytes("") // initially empty + }); + + // define domain separator + bytes32 domainSeparator = keccak256( + abi.encode( + _TYPE_HASH, + keccak256(bytes("trusted-multicall-forwarder")), + keccak256(bytes("1")), + block.chainid, + address(trustedMulticallForwarder) + ) + ); + + // sign forward request data as bad signer + bytes memory invalidSignature = getForwardRequestDataSignature( + request, + trustedMulticallForwarder.nonces(address(signer)), + badSignerPrivateKey, // bad signer + domainSeparator + ); + + // update forward request data object with signature + request.signature = invalidSignature; + + // define batch of forward requests + ERC2771Forwarder.ForwardRequestData[] memory batch = + new ERC2771Forwarder.ForwardRequestData[](1); + batch[0] = request; + + // execute batch + vm.expectRevert(Address.FailedInnerCall.selector); + trustedMulticallForwarder.executeBatch{value: value, gas: gas}({ + requests: batch + }); + } +} From 192f902ba82ac9077e8187cc746ab58f4b4304a8 Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Thu, 30 Nov 2023 17:32:55 -0500 Subject: [PATCH 4/7] =?UTF-8?q?=E2=9C=85=20Update/Add=20Tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/Deploy.s.sol | 2 +- src/utils/TrustedMulticallForwarder.sol | 366 ++++++++++++++++++++++++ test/Deployment.t.sol | 2 +- test/EthManagement.t.sol | 13 +- test/Multicall3.t.sol | 360 +++++++++++++++++++++++ test/utils/Bootstrap.sol | 2 +- test/utils/mocks/EtherSink.sol | 12 + test/utils/mocks/MockCallee.sol | 32 +++ 8 files changed, 779 insertions(+), 10 deletions(-) create mode 100644 src/utils/TrustedMulticallForwarder.sol create mode 100644 test/Multicall3.t.sol create mode 100644 test/utils/mocks/EtherSink.sol create mode 100644 test/utils/mocks/MockCallee.sol diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 90475c6d..053659a3 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.20; // contracts import {Engine} from "src/Engine.sol"; import {TrustedMulticallForwarder} from - "lib/trusted-multicall-forwarder/src/TrustedMulticallForwarder.sol"; + "src/utils/TrustedMulticallForwarder.sol"; // parameters import {BaseGoerliParameters} from diff --git a/src/utils/TrustedMulticallForwarder.sol b/src/utils/TrustedMulticallForwarder.sol new file mode 100644 index 00000000..c3a9a598 --- /dev/null +++ b/src/utils/TrustedMulticallForwarder.sol @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import { + ERC2771Forwarder, + Address +} from "lib/openzeppelin-contracts/contracts/metatx/ERC2771Forwarder.sol"; + +/* solhint-disable meta-transactions/no-msg-sender */ + +/// @title TrustedMulticallForwarder +/// @notice Aggregate results from multiple function calls +/// @dev Derived from Multicall3 +/// @dev Modified for support to bubble errors +/// @dev Multicall & Multicall2 backwards-compatible +/// @dev Aggregate methods are marked `payable` to save 24 gas per call +/// @dev Includes ERC-2771 trusted forwarder functionality +/// @author Michael Elliot +/// @author Joshua Levine +/// @author Nick Johnson +/// @author Andreas Bigger +/// @author Matt Solomon +/// @author Daniel Beal +/// @author Noah Litvin +/// @author Jared Borders +contract TrustedMulticallForwarder is ERC2771Forwarder { + struct Call { + address target; + bytes callData; + } + + struct Call3 { + address target; + bool requireSuccess; + bytes callData; + } + + struct Call3Value { + address target; + bool requireSuccess; + uint256 value; + bytes callData; + } + + struct Result { + bool success; + bytes returnData; + } + + constructor() ERC2771Forwarder("trusted-multicall-forwarder") {} + + /// @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 + 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(abi.encodePacked(call.callData, msg.sender)); + if (!success) { + bytes memory revertData = returnData[i]; + uint256 len = revertData.length; + assembly { + revert(add(revertData, 0x20), len) + } + } + + 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 + 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(abi.encodePacked(call.callData, msg.sender)); + if (requireSuccess && !result.success) { + bytes memory revertData = result.returnData; + uint256 len = revertData.length; + assembly { + revert(add(revertData, 0x20), len) + } + } + 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(abi.encodePacked(calli.callData, msg.sender)); + if (calli.requireSuccess && !result.success) { + bytes memory revertData = result.returnData; + uint256 len = revertData.length; + assembly { + revert(add(revertData, 0x20), len) + } + } + 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}( + abi.encodePacked(calli.callData, msg.sender) + ); + if (calli.requireSuccess && !result.success) { + bytes memory revertData = result.returnData; + uint256 len = revertData.length; + assembly { + revert(add(revertData, 0x20), len) + } + } + unchecked { + ++i; + } + } + // Finally, make sure the msg.value == SUM(call[0...i].value) + if (msg.value != valAccumulator) { + revert ERC2771ForwarderMismatchedValue(valAccumulator, msg.value); + } + } + + /// @notice Aggregate ForwardRequestData objects + /// @notice Reverts if msg.value does not equal the sum of the call values + /// @notice Reverts if the msg.sender is the zero address + /// @param requests An array of ForwardRequestData structs + /// @return returnData An array of Result structs + function executeBatch(ForwardRequestData[] calldata requests) + public + payable + returns (Result[] memory returnData) + { + uint256 length = requests.length; + returnData = new Result[](length); + + ForwardRequestData calldata req; + + uint256 requestsValue; + uint256 refundValue; + + for (uint256 i; i < length;) { + Result memory result = returnData[i]; + + req = requests[i]; + requestsValue += requests[i].value; + + ( + bool isTrustedForwarder, + bool active, + bool signerMatch, + address signer + ) = _validate(req); + + if (isTrustedForwarder && signerMatch && active) { + // Nonce should be used before the call to prevent reusing by reentrancy + uint256 currentNonce = _useNonce(signer); + + (result.success, result.returnData) = req.to.call{ + value: req.value, + gas: req.gas + }(abi.encodePacked(req.data, req.from)); + + /// @dev see ERC2771Forwarder._checkForwardedGas() for further details + if (gasleft() < req.gas / 63) { + assembly { + invalid() + } + } + + emit ExecutedForwardRequest( + signer, currentNonce, result.success + ); + } + + /// @notice If the call was not successful, we refund the value to the msg.sender + /// @dev unsuccessful calls are never reverted + if (!result.success) { + refundValue += requests[i].value; + } + + unchecked { + ++i; + } + } + + // The batch should revert if there's a mismatched msg.value provided + // to avoid request value tampering + if (requestsValue != msg.value) { + revert ERC2771ForwarderMismatchedValue(requestsValue, msg.value); + } + + // Some requests with value were invalid (possibly due to frontrunning). + // To avoid leaving ETH in the contract this value is refunded. + if (refundValue != 0) { + // We know msg.sender != address(0) && requestsValue == msg.value + // meaning we can ensure refundValue is not taken from the original contract's balance + // and msg.sender is a known account. + Address.sendValue(payable(msg.sender), refundValue); + } + } + + /// @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 prevrandao + function getPrevRandao() public view returns (uint256 prevrandao) { + prevrandao = block.prevrandao; + } + + /// @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; + } +} diff --git a/test/Deployment.t.sol b/test/Deployment.t.sol index 60875c95..942adf1b 100644 --- a/test/Deployment.t.sol +++ b/test/Deployment.t.sol @@ -5,7 +5,7 @@ import {Engine, Setup} from "script/Deploy.s.sol"; import {IEngine} from "src/interfaces/IEngine.sol"; import {Test} from "lib/forge-std/src/Test.sol"; import {TrustedMulticallForwarder} from - "lib/trusted-multicall-forwarder/src/TrustedMulticallForwarder.sol"; + "src/utils/TrustedMulticallForwarder.sol"; contract DeploymentTest is Test, Setup { Setup setup; diff --git a/test/EthManagement.t.sol b/test/EthManagement.t.sol index 201397c9..a842339d 100644 --- a/test/EthManagement.t.sol +++ b/test/EthManagement.t.sol @@ -5,7 +5,7 @@ import {Bootstrap, TrustedMulticallForwarder} from "test/utils/Bootstrap.sol"; import {IEngine} from "src/interfaces/IEngine.sol"; import {SynthetixMock} from "test/utils/mocks/SynthetixMock.sol"; import {ERC2771Forwarder} from - "lib/trusted-multicall-forwarder/lib/openzeppelin-contracts/contracts/metatx/ERC2771Forwarder.sol"; + "lib/openzeppelin-contracts/contracts/metatx/ERC2771Forwarder.sol"; contract CantReceiveEth {} @@ -33,7 +33,7 @@ contract Deposit is EthManagementTest { TrustedMulticallForwarder.Call3Value memory call = TrustedMulticallForwarder.Call3Value( address(engine), - true, + true, // requireSuccess AMOUNT, abi.encodeWithSelector(engine.depositEth.selector, accountId) ); @@ -49,13 +49,12 @@ contract Deposit is EthManagementTest { assertEq(engine.ethBalances(accountId), AMOUNT * 2); } - function test_depositEth_via_trustedForwarder_value_mismatch_allow_failure() - public - { + function test_depositEth_via_trustedForwarder_value_mismatch_require_success( + ) public { TrustedMulticallForwarder.Call3Value memory call = TrustedMulticallForwarder.Call3Value( address(engine), - true, // allow failure + true, // requireSuccess AMOUNT, abi.encodeWithSelector(engine.depositEth.selector, accountId) ); @@ -80,7 +79,7 @@ contract Deposit is EthManagementTest { TrustedMulticallForwarder.Call3Value memory call = TrustedMulticallForwarder.Call3Value( address(engine), - false, // do not allow failure + false, // requireSuccess = false AMOUNT, abi.encodeWithSelector(engine.depositEth.selector, accountId) ); diff --git a/test/Multicall3.t.sol b/test/Multicall3.t.sol new file mode 100644 index 00000000..5400bce7 --- /dev/null +++ b/test/Multicall3.t.sol @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import {Test} from "forge-std/Test.sol"; +import { + ERC2771Forwarder, + Address +} from "lib/openzeppelin-contracts/contracts/metatx/ERC2771Forwarder.sol"; +import {TrustedMulticallForwarder} from + "src/utils/TrustedMulticallForwarder.sol"; +import {MockCallee} from "test/utils/mocks/MockCallee.sol"; +import {EtherSink} from "test/utils/mocks/EtherSink.sol"; + +contract TrustedMulticallForwarderTest is Test { + TrustedMulticallForwarder multicall; + MockCallee callee; + EtherSink etherSink; + + /// @notice Setups up the testing suite + function setUp() public { + multicall = new TrustedMulticallForwarder(); + callee = new MockCallee(); + etherSink = new EtherSink(); + } + + /// >>>>>>>>>>>>>>>>>>>>> AGGREGATE TESTS <<<<<<<<<<<<<<<<<<<<< /// + + function testAggregation() public { + // Test successful call + TrustedMulticallForwarder.Call[] memory calls = + new TrustedMulticallForwarder.Call[](1); + calls[0] = TrustedMulticallForwarder.Call( + address(callee), + abi.encodeWithSignature("getBlockHash(uint256)", block.number) + ); + (uint256 blockNumber, bytes[] memory returnData) = + multicall.aggregate(calls); + assertEq(blockNumber, block.number); + assertEq( + keccak256(returnData[0]), + keccak256(abi.encodePacked(blockhash(block.number))) + ); + } + + function testUnsuccessfulAggregation() public { + // Test unexpected revert + TrustedMulticallForwarder.Call[] memory calls = + new TrustedMulticallForwarder.Call[](2); + calls[0] = TrustedMulticallForwarder.Call( + address(callee), + abi.encodeWithSignature("getBlockHash(uint256)", block.number) + ); + calls[1] = TrustedMulticallForwarder.Call( + address(callee), abi.encodeWithSignature("thisMethodReverts()") + ); + vm.expectRevert(bytes(hex"81775cc3")); + multicall.aggregate(calls); + } + + /// >>>>>>>>>>>>>>>>>>> TRY AGGREGATE TESTS <<<<<<<<<<<<<<<<<<< /// + + function testTryAggregate() public { + TrustedMulticallForwarder.Call[] memory calls = + new TrustedMulticallForwarder.Call[](2); + calls[0] = TrustedMulticallForwarder.Call( + address(callee), + abi.encodeWithSignature("getBlockHash(uint256)", block.number) + ); + calls[1] = TrustedMulticallForwarder.Call( + address(callee), abi.encodeWithSignature("thisMethodReverts()") + ); + TrustedMulticallForwarder.Result[] memory returnData = + multicall.tryAggregate(false, calls); + assertTrue(returnData[0].success); + assertEq( + keccak256(returnData[0].returnData), + keccak256(abi.encodePacked(blockhash(block.number))) + ); + assertTrue(!returnData[1].success); + } + + function testTryAggregateUnsuccessful() public { + TrustedMulticallForwarder.Call[] memory calls = + new TrustedMulticallForwarder.Call[](2); + calls[0] = TrustedMulticallForwarder.Call( + address(callee), + abi.encodeWithSignature("getBlockHash(uint256)", block.number) + ); + calls[1] = TrustedMulticallForwarder.Call( + address(callee), abi.encodeWithSignature("thisMethodReverts()") + ); + vm.expectRevert(bytes(hex"81775cc3")); + multicall.tryAggregate(true, calls); + } + + /// >>>>>>>>>>>>>> TRY BLOCK AND AGGREGATE TESTS <<<<<<<<<<<<<< /// + + function testTryBlockAndAggregate() public { + TrustedMulticallForwarder.Call[] memory calls = + new TrustedMulticallForwarder.Call[](2); + calls[0] = TrustedMulticallForwarder.Call( + address(callee), + abi.encodeWithSignature("getBlockHash(uint256)", block.number) + ); + calls[1] = TrustedMulticallForwarder.Call( + address(callee), abi.encodeWithSignature("thisMethodReverts()") + ); + ( + uint256 blockNumber, + bytes32 blockHash, + TrustedMulticallForwarder.Result[] memory returnData + ) = multicall.tryBlockAndAggregate(false, calls); + assertEq(blockNumber, block.number); + assertEq(blockHash, blockhash(block.number)); + assertTrue(returnData[0].success); + assertEq( + keccak256(returnData[0].returnData), + keccak256(abi.encodePacked(blockhash(block.number))) + ); + assertTrue(!returnData[1].success); + } + + function testTryBlockAndAggregateUnsuccessful() public { + TrustedMulticallForwarder.Call[] memory calls = + new TrustedMulticallForwarder.Call[](2); + calls[0] = TrustedMulticallForwarder.Call( + address(callee), + abi.encodeWithSignature("getBlockHash(uint256)", block.number) + ); + calls[1] = TrustedMulticallForwarder.Call( + address(callee), abi.encodeWithSignature("thisMethodReverts()") + ); + vm.expectRevert(bytes(hex"81775cc3")); + multicall.tryBlockAndAggregate(true, calls); + } + + function testBlockAndAggregateUnsuccessful() public { + TrustedMulticallForwarder.Call[] memory calls = + new TrustedMulticallForwarder.Call[](2); + calls[0] = TrustedMulticallForwarder.Call( + address(callee), + abi.encodeWithSignature("getBlockHash(uint256)", block.number) + ); + calls[1] = TrustedMulticallForwarder.Call( + address(callee), abi.encodeWithSignature("thisMethodReverts()") + ); + vm.expectRevert(bytes(hex"81775cc3")); + multicall.blockAndAggregate(calls); + } + + /// >>>>>>>>>>>>>>>>>>> AGGREGATE3 TESTS <<<<<<<<<<<<<<<<<<<<<< /// + + function testAggregate3() public { + TrustedMulticallForwarder.Call3[] memory calls = + new TrustedMulticallForwarder.Call3[](3); + calls[0] = TrustedMulticallForwarder.Call3( + address(callee), + true, + abi.encodeWithSignature("getBlockHash(uint256)", block.number) + ); + calls[1] = TrustedMulticallForwarder.Call3( + address(callee), + false, + abi.encodeWithSignature("thisMethodReverts()") + ); + calls[2] = TrustedMulticallForwarder.Call3( + address(multicall), + false, + abi.encodeWithSignature("getCurrentBlockTimestamp()") + ); + TrustedMulticallForwarder.Result[] memory returnData = + multicall.aggregate3(calls); + + // Call 1. + assertTrue(returnData[0].success); + assertEq( + blockhash(block.number), + abi.decode(returnData[0].returnData, (bytes32)) + ); + assertEq( + keccak256(returnData[0].returnData), + keccak256(abi.encodePacked(blockhash(block.number))) + ); + + // Call 2. + assertTrue(!returnData[1].success); + assertEq(returnData[1].returnData.length, 4); + assertEq( + bytes4(returnData[1].returnData), + bytes4(keccak256("Unsuccessful()")) + ); + + // Call 3. + assertTrue(returnData[2].success); + assertEq( + abi.decode(returnData[2].returnData, (uint256)), block.timestamp + ); + } + + function testAggregate3Unsuccessful() public { + TrustedMulticallForwarder.Call3[] memory calls = + new TrustedMulticallForwarder.Call3[](2); + calls[0] = TrustedMulticallForwarder.Call3( + address(callee), + true, + abi.encodeWithSignature("getBlockHash(uint256)", block.number) + ); + calls[1] = TrustedMulticallForwarder.Call3( + address(callee), + true, + abi.encodeWithSignature("thisMethodReverts()") + ); + vm.expectRevert(bytes(hex"81775cc3")); + multicall.aggregate3(calls); + } + + /// >>>>>>>>>>>>>>>>> AGGREGATE3VALUE TESTS <<<<<<<<<<<<<<<<<<< /// + + function testAggregate3Value() public { + TrustedMulticallForwarder.Call3Value[] memory calls = + new TrustedMulticallForwarder.Call3Value[](3); + calls[0] = TrustedMulticallForwarder.Call3Value( + address(callee), + true, + 0, + abi.encodeWithSignature("getBlockHash(uint256)", block.number) + ); + calls[1] = TrustedMulticallForwarder.Call3Value( + address(callee), + false, + 0, + abi.encodeWithSignature("thisMethodReverts()") + ); + calls[2] = TrustedMulticallForwarder.Call3Value( + address(callee), + false, + 1, + abi.encodeWithSignature( + "sendBackValue(address)", address(etherSink) + ) + ); + TrustedMulticallForwarder.Result[] memory returnData = + multicall.aggregate3Value{value: 1}(calls); + assertTrue(returnData[0].success); + assertEq( + keccak256(returnData[0].returnData), + keccak256(abi.encodePacked(blockhash(block.number))) + ); + assertTrue(!returnData[1].success); + assertTrue(returnData[2].success); + } + + function testAggregate3ValueUnsuccessful() public { + TrustedMulticallForwarder.Call3Value[] memory calls = + new TrustedMulticallForwarder.Call3Value[](3); + calls[0] = TrustedMulticallForwarder.Call3Value( + address(callee), + true, + 0, + abi.encodeWithSignature("getBlockHash(uint256)", block.number) + ); + calls[1] = TrustedMulticallForwarder.Call3Value( + address(callee), + true, + 0, + abi.encodeWithSignature("thisMethodReverts()") + ); + calls[2] = TrustedMulticallForwarder.Call3Value( + address(callee), + true, + 1, + abi.encodeWithSignature( + "sendBackValue(address)", address(etherSink) + ) + ); + vm.expectRevert(bytes(hex"81775cc3")); + multicall.aggregate3Value{value: 1}(calls); + + // Should fail if we don't provide enough value + TrustedMulticallForwarder.Call3Value[] memory calls2 = + new TrustedMulticallForwarder.Call3Value[](1); + calls2[0] = TrustedMulticallForwarder.Call3Value( + address(callee), + false, + 1, + abi.encodeWithSignature( + "sendBackValue(address)", address(etherSink) + ) + ); + // trying to figure out how to check the actual error here but its hard + //vm.expectRevert(ERC2771Forwarder.ERC2771ForwarderMismatchedValue.selector); + vm.expectRevert(); + multicall.aggregate3Value(calls2); + + // Works if we provide enough value + TrustedMulticallForwarder.Call3Value[] memory calls3 = + new TrustedMulticallForwarder.Call3Value[](3); + calls3[0] = TrustedMulticallForwarder.Call3Value( + address(callee), + true, + 0, + abi.encodeWithSignature("getBlockHash(uint256)", block.number) + ); + calls3[1] = TrustedMulticallForwarder.Call3Value( + address(callee), + false, + 0, + abi.encodeWithSignature("thisMethodReverts()") + ); + calls3[2] = TrustedMulticallForwarder.Call3Value( + address(callee), + true, + 1, + abi.encodeWithSignature( + "sendBackValue(address)", address(etherSink) + ) + ); + multicall.aggregate3Value{value: 1}(calls3); + } + + /// >>>>>>>>>>>>>>>>>>>>>> HELPER TESTS <<<<<<<<<<<<<<<<<<<<<<< /// + + function testGetBlockHash(uint256 blockNumber) public { + assertEq(blockhash(blockNumber), multicall.getBlockHash(blockNumber)); + } + + function testGetBlockNumber() public { + assertEq(block.number, multicall.getBlockNumber()); + } + + function testGetCurrentBlockCoinbase() public { + assertEq(block.coinbase, multicall.getCurrentBlockCoinbase()); + } + + function testGetCurrentBlockGasLimit() public { + assertEq(block.gaslimit, multicall.getCurrentBlockGasLimit()); + } + + function testGetCurrentBlockTimestamp() public { + assertEq(block.timestamp, multicall.getCurrentBlockTimestamp()); + } + + function testGetEthBalance(address addr) public { + assertEq(addr.balance, multicall.getEthBalance(addr)); + } + + function testGetLastBlockHash() public { + // Prevent arithmetic underflow on the genesis block + if (block.number == 0) return; + assertEq(blockhash(block.number - 1), multicall.getLastBlockHash()); + } + + function testGetBasefee() public { + assertEq(block.basefee, multicall.getBasefee()); + } + + function testGetChainId() public { + assertEq(block.chainid, multicall.getChainId()); + } +} diff --git a/test/utils/Bootstrap.sol b/test/utils/Bootstrap.sol index 9f06b3e9..ca58275c 100644 --- a/test/utils/Bootstrap.sol +++ b/test/utils/Bootstrap.sol @@ -19,7 +19,7 @@ import {ISpotMarketProxy} from "src/interfaces/synthetix/ISpotMarketProxy.sol"; import {IPyth} from "src/interfaces/oracles/IPyth.sol"; import {SynthMinter} from "test/utils/SynthMinter.sol"; import {TrustedMulticallForwarder} from - "lib/trusted-multicall-forwarder/src/TrustedMulticallForwarder.sol"; + "src/utils/TrustedMulticallForwarder.sol"; contract Bootstrap is Test, Constants, Conditions, SynthetixV3Errors { using console2 for *; diff --git a/test/utils/mocks/EtherSink.sol b/test/utils/mocks/EtherSink.sol new file mode 100644 index 00000000..762a55c5 --- /dev/null +++ b/test/utils/mocks/EtherSink.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +/// @title EtherSink +/// @notice Receives Ether, that's about it \( o_o )/ +/// @author andreas@nascent.xyz +contract EtherSink { + /// >>>>>>>>>>>>>>>>>>>>>> ACCEPT CALLS <<<<<<<<<<<<<<<<<<<<<<< /// + + /// @notice Allows the test to receive eth via low level calls + receive() external payable {} +} diff --git a/test/utils/mocks/MockCallee.sol b/test/utils/mocks/MockCallee.sol new file mode 100644 index 00000000..b47a5985 --- /dev/null +++ b/test/utils/mocks/MockCallee.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +/// @title MockCallee +/// @notice Receives calls from the Multicaller +/// @author andreas@nascent.xyz +contract MockCallee { + /// @notice Failure + error Unsuccessful(); + + /// @notice Returns the block hash for the given block number + /// @param blockNumber The block number + /// @return blockHash The 32 byte block hash + function getBlockHash(uint256 blockNumber) + public + view + returns (bytes32 blockHash) + { + blockHash = blockhash(blockNumber); + } + + /// @notice Reverts o______O + function thisMethodReverts() public pure { + revert Unsuccessful(); + } + + /// @notice Accepts a value + function sendBackValue(address target) public payable { + (bool ok,) = target.call{value: msg.value}(""); + if (!ok) revert Unsuccessful(); + } +} From b6260e152ebadd467447b29271d055e4115c4489 Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Thu, 30 Nov 2023 17:37:22 -0500 Subject: [PATCH 5/7] =?UTF-8?q?=F0=9F=91=B7=F0=9F=8F=BB=E2=80=8D=E2=99=82?= =?UTF-8?q?=EF=B8=8F=20Remove=20constructor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/TrustedMulticallForwarder.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/TrustedMulticallForwarder.sol b/src/utils/TrustedMulticallForwarder.sol index c3a9a598..30f1bf39 100644 --- a/src/utils/TrustedMulticallForwarder.sol +++ b/src/utils/TrustedMulticallForwarder.sol @@ -23,7 +23,9 @@ import { /// @author Daniel Beal /// @author Noah Litvin /// @author Jared Borders -contract TrustedMulticallForwarder is ERC2771Forwarder { +contract TrustedMulticallForwarder is + ERC2771Forwarder("trusted-multicall-forwarder") +{ struct Call { address target; bytes callData; @@ -47,8 +49,6 @@ contract TrustedMulticallForwarder is ERC2771Forwarder { bytes returnData; } - constructor() ERC2771Forwarder("trusted-multicall-forwarder") {} - /// @notice Backwards-compatible call aggregation with Multicall /// @param calls An array of Call structs /// @return blockNumber The block number where the calls were executed From d068a91785171c7f505b9f726933ec62d67aefe4 Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Thu, 30 Nov 2023 17:37:40 -0500 Subject: [PATCH 6/7] =?UTF-8?q?=F0=9F=93=B8=20Update=20gas-snapshot/lcov?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gas-snapshot | 34 ++++++-- lcov.info | 228 ++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 238 insertions(+), 24 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 54484d18..080f51f1 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -22,18 +22,18 @@ Conditions:test_isPriceAbove() (gas: 19098) Conditions:test_isPriceBelow() (gas: 19092) Conditions:test_isTimestampAfter() (gas: 7711) Conditions:test_isTimestampBefore() (gas: 7579) -DeploymentTest:test_deploy() (gas: 4835682) -DeploymentTest:test_deploy_oracle_zero_address() (gas: 1903169) -DeploymentTest:test_deploy_perps_market_proxy_zero_address() (gas: 1903085) -DeploymentTest:test_deploy_spot_market_proxy_zero_address() (gas: 1903152) -DeploymentTest:test_deploy_susd_proxy_zero_address() (gas: 1903165) +DeploymentTest:test_deploy() (gas: 4835482) +DeploymentTest:test_deploy_oracle_zero_address() (gas: 1902969) +DeploymentTest:test_deploy_perps_market_proxy_zero_address() (gas: 1902885) +DeploymentTest:test_deploy_spot_market_proxy_zero_address() (gas: 1902952) +DeploymentTest:test_deploy_susd_proxy_zero_address() (gas: 1902965) DeploymentTest:test_deploy_trusted_forwarder_zero_address() (gas: 37498) Deposit:test_depositEth() (gas: 40987) Deposit:test_depositEth_event() (gas: 42547) Deposit:test_depositEth_fuzz(uint256,uint128) (runs: 256, μ: 39369, ~: 40409) Deposit:test_depositEth_via_trustedForwarder() (gas: 68338) Deposit:test_depositEth_via_trustedForwarder_value_mismatch() (gas: 73201) -Deposit:test_depositEth_via_trustedForwarder_value_mismatch_allow_failure() (gas: 72445) +Deposit:test_depositEth_via_trustedForwarder_value_mismatch_require_success() (gas: 72445) DepositCollateral:test_depositCollateral() (gas: 258530) DepositCollateral:test_depositCollateral_availableMargin() (gas: 266098) DepositCollateral:test_depositCollateral_collateralAmount() (gas: 259106) @@ -45,6 +45,8 @@ Execute:test_execute_CannotExecuteOrder_invalid_settlementStrategyId() (gas: 968 Execute:test_execute_CannotExecuteOrder_too_leveraged() (gas: 361524) Execute:test_execute_event() (gas: 433933) Execute:test_execute_order_committed() (gas: 430425) +ExecuteBatch:test_executeBatch() (gas: 95256) +ExecuteBatch:test_executeBatch_invalid_signature() (gas: 48173) Fee:test_fee_exceeds_account_credit() (gas: 53639) Fee:test_fee_exceeds_maxExecutorFee() (gas: 53218) Fee:test_fee_imposed() (gas: 465846) @@ -65,6 +67,26 @@ ReduceOnly:test_reduce_only_same_sign() (gas: 72481) ReduceOnly:test_reduce_only_truncate_size_down() (gas: 432232) ReduceOnly:test_reduce_only_truncate_size_up() (gas: 408894) ReduceOnly:test_reduce_only_zero_size() (gas: 162976) +TrustedMulticallForwarderTest:testAggregate3() (gas: 24779) +TrustedMulticallForwarderTest:testAggregate3Unsuccessful() (gas: 21395) +TrustedMulticallForwarderTest:testAggregate3Value() (gas: 49261) +TrustedMulticallForwarderTest:testAggregate3ValueUnsuccessful() (gas: 87877) +TrustedMulticallForwarderTest:testAggregation() (gas: 15186) +TrustedMulticallForwarderTest:testBlockAndAggregateUnsuccessful() (gas: 21080) +TrustedMulticallForwarderTest:testGetBasefee() (gas: 5486) +TrustedMulticallForwarderTest:testGetBlockHash(uint256) (runs: 256, μ: 5674, ~: 5674) +TrustedMulticallForwarderTest:testGetBlockNumber() (gas: 5487) +TrustedMulticallForwarderTest:testGetChainId() (gas: 5486) +TrustedMulticallForwarderTest:testGetCurrentBlockCoinbase() (gas: 5579) +TrustedMulticallForwarderTest:testGetCurrentBlockGasLimit() (gas: 5463) +TrustedMulticallForwarderTest:testGetCurrentBlockTimestamp() (gas: 5433) +TrustedMulticallForwarderTest:testGetEthBalance(address) (runs: 256, μ: 8421, ~: 8451) +TrustedMulticallForwarderTest:testGetLastBlockHash() (gas: 5670) +TrustedMulticallForwarderTest:testTryAggregate() (gas: 19577) +TrustedMulticallForwarderTest:testTryAggregateUnsuccessful() (gas: 21084) +TrustedMulticallForwarderTest:testTryBlockAndAggregate() (gas: 19879) +TrustedMulticallForwarderTest:testTryBlockAndAggregateUnsuccessful() (gas: 21185) +TrustedMulticallForwarderTest:testUnsuccessfulAggregation() (gas: 20818) VerifyConditions:test_max_condition_size_exceeded() (gas: 45101) VerifyConditions:test_verifyConditions_InvalidConditionSelector() (gas: 14132) VerifyConditions:test_verify_conditions_not_verified() (gas: 29707) diff --git a/lcov.info b/lcov.info index 1cd11278..c546720a 100644 --- a/lcov.info +++ b/lcov.info @@ -6,12 +6,12 @@ DA:134,0 DA:135,0 DA:137,0 DA:144,0 -FN:54,DeployBase_Synthetix.run -FNDA:0,DeployBase_Synthetix.run -DA:55,0 -DA:56,0 -DA:58,0 -DA:65,0 +FN:114,DeployBaseGoerli_Andromeda.run +FNDA:0,DeployBaseGoerli_Andromeda.run +DA:115,0 +DA:116,0 +DA:118,0 +DA:125,0 FN:26,Setup.deploySystem FNDA:5,Setup.deploySystem DA:38,5 @@ -28,18 +28,18 @@ DA:153,0 DA:154,0 DA:156,0 DA:163,0 +FN:54,DeployBase_Synthetix.run +FNDA:0,DeployBase_Synthetix.run +DA:55,0 +DA:56,0 +DA:58,0 +DA:65,0 FN:95,DeployBaseGoerli_KwentaFork.run FNDA:0,DeployBaseGoerli_KwentaFork.run DA:96,0 DA:97,0 DA:99,0 DA:106,0 -FN:114,DeployBaseGoerli_Andromeda.run -FNDA:0,DeployBaseGoerli_Andromeda.run -DA:115,0 -DA:116,0 -DA:118,0 -DA:125,0 FNF:7 FNH:1 LF:26 @@ -382,6 +382,156 @@ BRF:0 BRH:0 end_of_record TN: +SF:src/utils/TrustedMulticallForwarder.sol +FN:56,TrustedMulticallForwarder.aggregate +FNDA:2,TrustedMulticallForwarder.aggregate +DA:60,2 +DA:61,2 +DA:62,2 +DA:63,2 +DA:64,2 +DA:65,3 +DA:66,3 +DA:67,3 +DA:69,3 +BRDA:69,0,0,- +BRDA:69,0,1,1 +DA:70,1 +DA:71,1 +DA:78,2 +FN:88,TrustedMulticallForwarder.tryAggregate +FNDA:2,TrustedMulticallForwarder.tryAggregate +DA:92,5 +DA:93,5 +DA:94,5 +DA:95,5 +DA:96,10 +DA:97,10 +DA:98,10 +DA:100,10 +BRDA:100,1,0,3 +BRDA:100,1,1,7 +DA:101,3 +DA:102,3 +DA:108,7 +FN:119,TrustedMulticallForwarder.tryBlockAndAggregate +FNDA:2,TrustedMulticallForwarder.tryBlockAndAggregate +DA:128,3 +DA:129,3 +DA:130,3 +FN:139,TrustedMulticallForwarder.blockAndAggregate +FNDA:1,TrustedMulticallForwarder.blockAndAggregate +DA:148,1 +FN:154,TrustedMulticallForwarder.aggregate3 +FNDA:2,TrustedMulticallForwarder.aggregate3 +DA:159,2 +DA:160,2 +DA:161,2 +DA:162,2 +DA:163,5 +DA:164,5 +DA:165,5 +DA:167,5 +BRDA:167,2,0,1 +BRDA:167,2,1,4 +DA:168,1 +DA:169,1 +DA:175,4 +FN:184,TrustedMulticallForwarder.aggregate3Value +FNDA:7,TrustedMulticallForwarder.aggregate3Value +DA:189,7 +DA:190,7 +DA:191,7 +DA:192,7 +DA:193,7 +DA:194,15 +DA:195,15 +DA:196,15 +DA:200,15 +DA:202,15 +DA:205,15 +BRDA:205,3,0,2 +BRDA:205,3,1,13 +DA:206,2 +DA:207,2 +DA:213,13 +DA:217,5 +BRDA:217,4,0,2 +BRDA:217,4,1,3 +DA:218,2 +FN:227,TrustedMulticallForwarder.executeBatch +FNDA:2,TrustedMulticallForwarder.executeBatch +DA:232,2 +DA:233,2 +DA:235,2 +DA:237,2 +DA:238,2 +DA:240,2 +DA:241,2 +DA:243,2 +DA:244,2 +DA:246,2 +DA:251,2 +DA:253,2 +BRDA:253,5,0,- +BRDA:253,5,1,1 +DA:255,1 +DA:257,1 +DA:263,1 +BRDA:263,6,0,- +BRDA:263,6,1,1 +DA:269,1 +DA:276,2 +BRDA:276,7,0,- +BRDA:276,7,1,1 +DA:277,1 +DA:281,2 +DA:287,2 +BRDA:287,8,0,- +BRDA:287,8,1,2 +DA:288,0 +DA:293,2 +BRDA:293,9,0,1 +BRDA:293,9,1,1 +DA:297,1 +FN:303,TrustedMulticallForwarder.getBlockHash +FNDA:256,TrustedMulticallForwarder.getBlockHash +DA:308,256 +FN:312,TrustedMulticallForwarder.getBlockNumber +FNDA:1,TrustedMulticallForwarder.getBlockNumber +DA:313,1 +FN:317,TrustedMulticallForwarder.getCurrentBlockCoinbase +FNDA:1,TrustedMulticallForwarder.getCurrentBlockCoinbase +DA:318,1 +FN:322,TrustedMulticallForwarder.getPrevRandao +FNDA:0,TrustedMulticallForwarder.getPrevRandao +DA:323,0 +FN:327,TrustedMulticallForwarder.getCurrentBlockGasLimit +FNDA:1,TrustedMulticallForwarder.getCurrentBlockGasLimit +DA:328,1 +FN:332,TrustedMulticallForwarder.getCurrentBlockTimestamp +FNDA:2,TrustedMulticallForwarder.getCurrentBlockTimestamp +DA:337,2 +FN:341,TrustedMulticallForwarder.getEthBalance +FNDA:256,TrustedMulticallForwarder.getEthBalance +DA:346,256 +FN:350,TrustedMulticallForwarder.getLastBlockHash +FNDA:1,TrustedMulticallForwarder.getLastBlockHash +DA:352,1 +FN:358,TrustedMulticallForwarder.getBasefee +FNDA:1,TrustedMulticallForwarder.getBasefee +DA:359,1 +FN:363,TrustedMulticallForwarder.getChainId +FNDA:1,TrustedMulticallForwarder.getChainId +DA:364,1 +FNF:17 +FNH:16 +LF:87 +LH:85 +BRF:20 +BRH:15 +end_of_record +TN: SF:test/AsyncOrder.t.sol FN:8,AsyncOrderTest.setUp FNDA:0,AsyncOrderTest.setUp @@ -462,6 +612,27 @@ BRF:0 BRH:0 end_of_record TN: +SF:test/TrustedMulticallForwarder.t.sol +FN:14,ERC2771Example.isTrustedForwarder +FNDA:2,ERC2771Example.isTrustedForwarder +DA:19,2 +FN:22,ERC2771Example.ping +FNDA:1,ERC2771Example.ping +DA:23,1 +FN:38,TrustedMulticallForwarderTest.setUp +FNDA:0,TrustedMulticallForwarderTest.setUp +DA:40,0 +DA:41,0 +DA:44,0 +DA:45,0 +FNF:3 +FNH:2 +LF:6 +LH:2 +BRF:0 +BRH:0 +end_of_record +TN: SF:test/utils/Bootstrap.sol FN:113,BootstrapOptimism.init FNDA:0,BootstrapOptimism.init @@ -469,6 +640,12 @@ DA:117,0 DA:118,0 DA:125,0 DA:133,0 +FN:146,BootstrapOptimismGoerli.init +FNDA:0,BootstrapOptimismGoerli.init +DA:150,0 +DA:151,0 +DA:158,0 +DA:166,0 FN:41,Bootstrap.initializeOptimismGoerli FNDA:0,Bootstrap.initializeOptimismGoerli DA:42,0 @@ -507,12 +684,6 @@ DA:100,0 DA:101,0 DA:106,0 DA:108,0 -FN:146,BootstrapOptimismGoerli.init -FNDA:0,BootstrapOptimismGoerli.init -DA:150,0 -DA:151,0 -DA:158,0 -DA:166,0 FNF:4 FNH:0 LF:42 @@ -603,6 +774,27 @@ BRF:0 BRH:0 end_of_record TN: +SF:test/utils/mocks/MockCallee.sol +FN:14,MockCallee.getBlockHash +FNDA:12,MockCallee.getBlockHash +DA:19,12 +FN:23,MockCallee.thisMethodReverts +FNDA:11,MockCallee.thisMethodReverts +DA:24,11 +FN:28,MockCallee.sendBackValue +FNDA:2,MockCallee.sendBackValue +DA:29,2 +DA:30,2 +BRDA:30,0,0,- +BRDA:30,0,1,2 +FNF:3 +FNH:3 +LF:4 +LH:4 +BRF:2 +BRH:1 +end_of_record +TN: SF:test/utils/mocks/PythMock.sol FN:8,PythMock.mock_pyth_getPrice FNDA:0,PythMock.mock_pyth_getPrice From b60df105f2b3c4233fe0535932f0302d8f444173 Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Thu, 30 Nov 2023 17:39:32 -0500 Subject: [PATCH 7/7] =?UTF-8?q?=F0=9F=9A=80=20Update=20Andromeda=20Deploym?= =?UTF-8?q?ent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deployments/BaseGoerli.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deployments/BaseGoerli.json b/deployments/BaseGoerli.json index 8eee59f8..c45d9fe5 100644 --- a/deployments/BaseGoerli.json +++ b/deployments/BaseGoerli.json @@ -4,8 +4,8 @@ "TrustedMulticallForwarder": "0x14aE2D8fA531A9e77aE434d2d700218C2845Bc83" }, "Andromeda": { - "Engine": "0x6b5864815A42565D2b421D7CEB70440cc479c568", - "TrustedMulticallForwarder": "0xAD9b7B21456E0d4c99d39AC2aB8a54241d3f1FF8" + "Engine": "0x3617154844291712cBD2148D912b61d6641229a4", + "TrustedMulticallForwarder": "0xb5dCFb08a2CB07399b75B650B980732340c5Ed90" }, "Kwenta": { "Engine": "0x0b5456EB6eE169C533a931aD2a420237ADf3Da49",