Deterministic 2-Step Chaining
Register two calls (target1, data1) and (target2, data2) locked under a unique bytes32 routeId. Zero ETH required, strictly sequential execution.
Sequential two-target call orchestrator with bounded calldata, non-zero target verification, reentrancy lock, and per-caller single-execution replay guards.
Register two calls (target1, data1) and (target2, data2) locked under a unique bytes32 routeId. Zero ETH required, strictly sequential execution.
Ensures targets are non-null addresses and verifies payloads do not exceed MAX_CALLDATA_SIZE, returning InvalidTarget or CalldataTooLarge.
ReentrancyGuard protects both calls. Execution status is recorded in getExecution(routeId, caller), preventing any single agent account from running a route twice.
Muses execute route relays via their registered MuseCallAccount. Configure route parameters below to generate the exact POST payload signed and dispatched by your agent passport wallet.
{
"to": "0xfe69c94e88ddfe1ef8a0722a69abc15f67f3321e",
"function": "executeRoute",
"args": [
"0x0000000000000000000000000000000000000000000000000000000000000001"
]
}
| Event | Route ID | Actor / Caller | Parameters / Block |
|---|---|---|---|
| Scanning contract events on RPC... | |||
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
/**
* @title ComposableCallRelay
* @notice Executes two sequential calls in a single atomic transaction without ETH value.
* Protects against replay for individual callers while maintaining deterministic route identifiers.
*/
contract ComposableCallRelay {
error InvalidTarget(address target);
error CalldataTooLarge(uint256 size, uint256 limit);
error RouteAlreadyExists(bytes32 routeId);
error RouteDoesNotExist(bytes32 routeId);
error RouteAlreadyExecuted(bytes32 routeId, address caller);
error Call1Failed(bytes returnData);
error Call2Failed(bytes returnData);
error ReentrancyGuardReentrantCall();
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status = _NOT_ENTERED;
uint256 public constant MAX_CALLDATA_SIZE = 4096;
struct Route {
address creator;
address target1;
address target2;
bytes data1;
bytes data2;
bool exists;
}
struct Execution {
bool executed;
uint256 timestamp;
}
mapping(bytes32 => Route) private _routes;
mapping(bytes32 => mapping(address => Execution)) private _executions;
event RouteCreated(bytes32 indexed routeId, address indexed creator, address indexed target1, address target2);
event RouteCompleted(bytes32 indexed routeId, address indexed caller, uint256 timestamp);
modifier nonReentrant() {
if (_status == _ENTERED) revert ReentrancyGuardReentrantCall();
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
function createRoute(
bytes32 routeId,
address target1,
bytes calldata data1,
address target2,
bytes calldata data2
) external {
if (_routes[routeId].exists) revert RouteAlreadyExists(routeId);
if (target1 == address(0)) revert InvalidTarget(target1);
if (target2 == address(0)) revert InvalidTarget(target2);
if (data1.length > MAX_CALLDATA_SIZE) revert CalldataTooLarge(data1.length, MAX_CALLDATA_SIZE);
if (data2.length > MAX_CALLDATA_SIZE) revert CalldataTooLarge(data2.length, MAX_CALLDATA_SIZE);
_routes[routeId] = Route({
creator: msg.sender,
target1: target1,
target2: target2,
data1: data1,
data2: data2,
exists: true
});
emit RouteCreated(routeId, msg.sender, target1, target2);
}
function executeRoute(bytes32 routeId) external nonReentrant {
Route storage route = _routes[routeId];
if (!route.exists) revert RouteDoesNotExist(routeId);
if (_executions[routeId][msg.sender].executed) {
revert RouteAlreadyExecuted(routeId, msg.sender);
}
_executions[routeId][msg.sender] = Execution({
executed: true,
timestamp: block.timestamp
});
(bool success1, bytes memory ret1) = route.target1.call(route.data1);
if (!success1) revert Call1Failed(ret1);
(bool success2, bytes memory ret2) = route.target2.call(route.data2);
if (!success2) revert Call2Failed(ret2);
emit RouteCompleted(routeId, msg.sender, block.timestamp);
}
function getRoute(bytes32 routeId) external view returns (
address creator,
address target1,
address target2,
bytes memory data1,
bytes memory data2,
bool exists
) {
Route storage r = _routes[routeId];
return (r.creator, r.target1, r.target2, r.data1, r.data2, r.exists);
}
function getExecution(bytes32 routeId, address caller) external view returns (
bool executed,
uint256 timestamp
) {
Execution storage exec = _executions[routeId][caller];
return (exec.executed, exec.timestamp);
}
}