packages/contracts-rfq/contracts/utils/MulticallTarget.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {IMulticallTarget} from "../interfaces/IMulticallTarget.sol";
// solhint-disable avoid-low-level-calls
/// @notice Abstract contract template that supports batched calls while preserving msg.sender.
/// Only calls with msg.value of 0 can be batched.
abstract contract MulticallTarget is IMulticallTarget {
error MulticallTarget__UndeterminedRevert();
/// @inheritdoc IMulticallTarget
function multicallNoResults(bytes[] calldata data, bool ignoreReverts) external {
for (uint256 i = 0; i < data.length; ++i) {
// We perform a delegate call to ourself to preserve the msg.sender. This is identical to `msg.sender`
// calling the functions directly one by one, therefore doesn't add any security risks.
// Note: msg.value is also preserved when doing a delegate call, but this function is not payable,
// so it's always 0 and not a security risk.
(bool success, bytes memory result) = address(this).delegatecall(data[i]);
if (!success && !ignoreReverts) {
_bubbleRevert(result);
}
}
}
/// @inheritdoc IMulticallTarget
function multicallWithResults(
bytes[] calldata data,
bool ignoreReverts
)
external
returns (Result[] memory results)
{
results = new Result[](data.length);
for (uint256 i = 0; i < data.length; ++i) {
// We perform a delegate call to ourself to preserve the msg.sender. This is identical to `msg.sender`
// calling the functions directly one by one, therefore doesn't add any security risks.
// Note: msg.value is also preserved when doing a delegate call, but this function is not payable,
// so it's always 0 and not a security risk.
(results[i].success, results[i].returnData) = address(this).delegatecall(data[i]);
if (!results[i].success && !ignoreReverts) {
_bubbleRevert(results[i].returnData);
}
}
}
/// @dev Bubbles the revert message from the underlying call.
/// Note: preserves the same custom error or revert string, if one was used.
/// Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.0.2/contracts/utils/Address.sol#L143-L158
function _bubbleRevert(bytes memory returnData) internal pure {
// Look for revert reason and bubble it up if present
if (returnData.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returnData)
revert(add(32, returnData), returndata_size)
}
} else {
revert MulticallTarget__UndeterminedRevert();
}
}
}