Enhanced integration & optimized testing
This commit is contained in:
80
contracts/lib/utils/Multicall.sol
Normal file
80
contracts/lib/utils/Multicall.sol
Normal file
@@ -0,0 +1,80 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.8.4;
|
||||
|
||||
/// @title Multicall.
|
||||
/// @notice Contract that batches view function calls and aggregates their results.
|
||||
/// @author Adapted from Makerdao's Multicall2 (https://github.com/makerdao/multicall/blob/master/src/Multicall2.sol).
|
||||
|
||||
contract Multicall {
|
||||
/// @dev 0x
|
||||
error CallFailed(string reason);
|
||||
|
||||
struct Call {
|
||||
address target;
|
||||
bytes callData;
|
||||
}
|
||||
struct Result {
|
||||
bool success;
|
||||
bytes returnData;
|
||||
}
|
||||
|
||||
//prettier-ignore
|
||||
constructor(/* */) payable {/* */}
|
||||
|
||||
function mtc1(Call[] calldata calls)
|
||||
external
|
||||
returns (uint256, bytes[] memory)
|
||||
{
|
||||
uint256 bn = block.number;
|
||||
uint256 len = calls.length;
|
||||
bytes[] memory res = new bytes[](len);
|
||||
uint256 j;
|
||||
|
||||
while (j < len) {
|
||||
(bool success, bytes memory ret) = calls[j]
|
||||
.target
|
||||
.call(calls[j].callData);
|
||||
if (!success) {
|
||||
if (ret.length < 0x44) revert CallFailed("");
|
||||
assembly {
|
||||
ret := add(ret, 0x04)
|
||||
}
|
||||
revert CallFailed({
|
||||
reason: abi.decode(ret, (string))
|
||||
});
|
||||
}
|
||||
res[j] = ret;
|
||||
++j;
|
||||
}
|
||||
return (bn, res);
|
||||
}
|
||||
|
||||
function mtc2(Call[] calldata calls)
|
||||
external
|
||||
returns (
|
||||
uint256,
|
||||
bytes32,
|
||||
Result[] memory
|
||||
)
|
||||
{
|
||||
uint256 bn = block.number;
|
||||
// µ 0 s [0] ≡ P(IHp , µs [0], 0) ∴ P is the hash of a block of a particular number, up to a maximum age.
|
||||
// 0 is left on the stack if the looked for `block.number` is >= to the current `block.number` or more than 256
|
||||
// blocks behind the current block (Yellow Paper, p. 33, https://ethereum.github.io/yellowpaper/paper.pdf).
|
||||
bytes32 bh = blockhash(
|
||||
bn /* - 1 */
|
||||
);
|
||||
uint256 len = calls.length;
|
||||
Result[] memory res = new Result[](len);
|
||||
uint256 i;
|
||||
for (i; i < len; ) {
|
||||
(bool success, bytes memory ret) = calls[i]
|
||||
.target
|
||||
.call(calls[i].callData);
|
||||
|
||||
res[i] = Result(success, ret);
|
||||
++i;
|
||||
}
|
||||
return (bn, bh, res);
|
||||
}
|
||||
}
|
||||
@@ -114,46 +114,4 @@ library SafeTransferLib {
|
||||
|
||||
require(success, "TRANSFER_FAILED");
|
||||
}
|
||||
|
||||
function safeApprove(
|
||||
ERC20 token,
|
||||
address to,
|
||||
uint256 amount
|
||||
) internal {
|
||||
bool success;
|
||||
|
||||
assembly {
|
||||
// We'll write our calldata to this slot below, but restore it later.
|
||||
let memPointer := mload(0x40)
|
||||
|
||||
// Write the abi-encoded calldata into memory, beginning with the function selector.
|
||||
mstore(
|
||||
0,
|
||||
0x095ea7b300000000000000000000000000000000000000000000000000000000
|
||||
)
|
||||
mstore(4, to) // Append the "to" argument.
|
||||
mstore(36, amount) // Append the "amount" argument.
|
||||
|
||||
success := and(
|
||||
// Set success to whether the call reverted, if not we check it either
|
||||
// returned exactly 1 (can't just be non-zero data), or had no return data.
|
||||
or(
|
||||
and(
|
||||
eq(mload(0), 1),
|
||||
gt(returndatasize(), 31)
|
||||
),
|
||||
iszero(returndatasize())
|
||||
),
|
||||
// We use 68 because that's the total length of our calldata (4 + 32 * 2)
|
||||
// Counterintuitively, this call() must be positioned after the or() in the
|
||||
// surrounding and() because and() evaluates its arguments from right to left.
|
||||
call(gas(), token, 0, 0, 68, 0, 32)
|
||||
)
|
||||
|
||||
mstore(0x60, 0) // Restore the zero slot to zero.
|
||||
mstore(0x40, memPointer) // Restore the memPointer.
|
||||
}
|
||||
|
||||
require(success, "APPROVE_FAILED");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user