/
BoringBatchable.sol
65 lines (57 loc) · 2.72 KB
/
BoringBatchable.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;
// solhint-disable avoid-low-level-calls
// solhint-disable no-inline-assembly
// WARNING!!!
// Combining BoringBatchable with msg.value can cause double spending issues
// https://www.paradigm.xyz/2021/08/two-rights-might-make-a-wrong/
import "./interfaces/IERC20.sol";
contract BaseBoringBatchable {
error BatchError(bytes innerError);
/// @dev Helper function to extract a useful revert message from a failed call.
/// If the returned data is malformed or not correctly abi encoded then this call can fail itself.
function _getRevertMsg(bytes memory _returnData) internal pure{
// If the _res length is less than 68, then
// the transaction failed with custom error or silently (without a revert message)
if (_returnData.length < 68) revert BatchError(_returnData);
assembly {
// Slice the sighash.
_returnData := add(_returnData, 0x04)
}
revert(abi.decode(_returnData, (string))); // All that remains is the revert string
}
/// @notice Allows batched call to self (this contract).
/// @param calls An array of inputs for each call.
/// @param revertOnFail If True then reverts after a failed call and stops doing further calls.
// F1: External is ok here because this is the batch function, adding it to a batch makes no sense
// F2: Calls in the batch may be payable, delegatecall operates in the same context, so each call in the batch has access to msg.value
// C3: The length of the loop is fully under user control, so can't be exploited
// C7: Delegatecall is only used on the same contract, so it's safe
function batch(bytes[] calldata calls, bool revertOnFail) external payable {
for (uint256 i = 0; i < calls.length; i++) {
(bool success, bytes memory result) = address(this).delegatecall(calls[i]);
if (!success && revertOnFail) {
_getRevertMsg(result);
}
}
}
}
contract BoringBatchable is BaseBoringBatchable {
/// @notice Call wrapper that performs `ERC20.permit` on `token`.
/// Lookup `IERC20.permit`.
// F6: Parameters can be used front-run the permit and the user's permit will fail (due to nonce or other revert)
// if part of a batch this could be used to grief once as the second call would not need the permit
function permitToken(
IERC20 token,
address from,
address to,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public {
token.permit(from, to, amount, deadline, v, r, s);
}
}