// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
contract RelayReceiver {
// --- Structs ---
struct Call {
address to;
bytes data;
uint256 value;
}
// --- Errors ---
error CallFailed();
error NativeTransferFailed();
error Unauthorized();
// --- Events ---
event FundsForwardedWithData(bytes data);
// --- Fields ---
address private immutable SOLVER;
// --- Constructor ---
constructor(address solver) {
SOLVER = solver;
}
// --- Public methods ---
fallback() external payable {
send(SOLVER, msg.value);
emit FundsForwardedWithData(msg.data);
}
function forward(bytes calldata data) external payable {
send(SOLVER, msg.value);
emit FundsForwardedWithData(data);
}
// --- Restricted methods ---
function makeCalls(Call[] calldata calls) external payable {
if (msg.sender != SOLVER) {
revert Unauthorized();
}
unchecked {
uint256 length = calls.length;
for (uint256 i; i < length; i++) {
Call memory c = calls[i];
(bool success, ) = c.to.call{value: c.value}(c.data);
if (!success) {
revert CallFailed();
}
}
}
}
// --- Internal methods ---
function send(address to, uint256 value) internal {
bool success;
assembly {
// Save gas by avoiding copying the return data to memory.
// Provide at most 100k gas to the internal call, which is
// more than enough to cover common use-cases of logic for
// receiving native tokens (eg. SCW payable fallbacks).
success := call(100000, to, value, 0, 0, 0, 0)
}
if (!success) {
revert NativeTransferFailed();
}
}
}
{
"compilationTarget": {
"src/RelayReceiver.sol": "RelayReceiver"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin/=lib/openzeppelin-contracts/contracts/",
":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":solady/=lib/solady/"
],
"viaIR": true
}
[{"inputs":[{"internalType":"address","name":"solver","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CallFailed","type":"error"},{"inputs":[],"name":"NativeTransferFailed","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"FundsForwardedWithData","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"forward","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct RelayReceiver.Call[]","name":"calls","type":"tuple[]"}],"name":"makeCalls","outputs":[],"stateMutability":"payable","type":"function"}]