Skip to content

Commit a5efcd6

Browse files
nthpoolcursoragent
andcommitted
Add EIP7702 batch DeleGator relay surface and coordinator contracts.
Introduce signed executeBatch relay execution with nonce replay protection, optional beacon proxy delegation target, batch relay coordinator, deploy script, and tests per O1-1/O1-2. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 1bdb8a3 commit a5efcd6

10 files changed

Lines changed: 1189 additions & 0 deletions
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// SPDX-License-Identifier: MIT AND Apache-2.0
2+
pragma solidity 0.8.23;
3+
4+
import "forge-std/Script.sol";
5+
import { console2 } from "forge-std/console2.sol";
6+
import { IEntryPoint } from "@account-abstraction/interfaces/IEntryPoint.sol";
7+
8+
import { EIP7702BatchDeleGator } from "../src/EIP7702/EIP7702BatchDeleGator.sol";
9+
import { EIP7702BatchDeleGatorBeacon } from "../src/EIP7702/EIP7702BatchDeleGatorBeacon.sol";
10+
import { EIP7702BatchDeleGatorProxy } from "../src/EIP7702/EIP7702BatchDeleGatorProxy.sol";
11+
import { DeleGatorBatchRelayCoordinator } from "../src/DeleGatorBatchRelayCoordinator.sol";
12+
import { IDelegationManager } from "../src/interfaces/IDelegationManager.sol";
13+
14+
/**
15+
* @title DeployEIP7702BatchDeleGator
16+
* @notice Deploys EIP7702BatchDeleGator, optional beacon/proxy, and the batch relay coordinator.
17+
* @dev Does not broadcast by default. Run with `--broadcast` only when ready to deploy.
18+
* @dev Required env:
19+
* - SALT
20+
* - ENTRYPOINT_ADDRESS
21+
* - DELEGATION_MANAGER_ADDRESS
22+
* @dev Optional env:
23+
* - BEACON_OWNER (defaults to deployer)
24+
* - COORDINATOR_OWNER (defaults to deployer)
25+
* - DEPLOY_PROXY=true|false (defaults to true)
26+
*/
27+
contract DeployEIP7702BatchDeleGator is Script {
28+
bytes32 internal salt;
29+
IEntryPoint internal entryPoint;
30+
IDelegationManager internal delegationManager;
31+
address internal deployer;
32+
address internal beaconOwner;
33+
address internal coordinatorOwner;
34+
bool internal deployProxy;
35+
36+
function setUp() public {
37+
salt = bytes32(abi.encodePacked(vm.envString("SALT")));
38+
entryPoint = IEntryPoint(vm.envAddress("ENTRYPOINT_ADDRESS"));
39+
delegationManager = IDelegationManager(vm.envAddress("DELEGATION_MANAGER_ADDRESS"));
40+
deployer = msg.sender;
41+
beaconOwner = vm.envOr("BEACON_OWNER", deployer);
42+
coordinatorOwner = vm.envOr("COORDINATOR_OWNER", deployer);
43+
deployProxy = vm.envOr("DEPLOY_PROXY", true);
44+
45+
console2.log("~~~ DeployEIP7702BatchDeleGator ~~~");
46+
console2.log("Deployer: %s", deployer);
47+
console2.log("Entry Point: %s", address(entryPoint));
48+
console2.log("Delegation Manager: %s", address(delegationManager));
49+
console2.log("Beacon Owner: %s", beaconOwner);
50+
console2.log("Coordinator Owner: %s", coordinatorOwner);
51+
console2.log("Deploy Proxy: %s", deployProxy);
52+
console2.log("Salt:");
53+
console2.logBytes32(salt);
54+
}
55+
56+
function run() public {
57+
vm.startBroadcast();
58+
59+
address implementation = address(
60+
new EIP7702BatchDeleGator{ salt: salt }(delegationManager, entryPoint)
61+
);
62+
console2.log("EIP7702BatchDeleGatorImpl: %s", implementation);
63+
64+
address authorizationTarget = implementation;
65+
address beacon;
66+
address proxy;
67+
68+
if (deployProxy) {
69+
beacon = address(new EIP7702BatchDeleGatorBeacon{ salt: salt }(implementation, beaconOwner));
70+
console2.log("EIP7702BatchDeleGatorBeacon: %s", beacon);
71+
72+
proxy = address(new EIP7702BatchDeleGatorProxy{ salt: salt }(beacon));
73+
console2.log("EIP7702BatchDeleGatorProxy: %s", proxy);
74+
75+
authorizationTarget = proxy;
76+
}
77+
78+
address coordinator = address(new DeleGatorBatchRelayCoordinator{ salt: salt }(coordinatorOwner));
79+
console2.log("DeleGatorBatchRelayCoordinator: %s", coordinator);
80+
81+
console2.log("~~~ Release Metadata ~~~");
82+
console2.log("authorizationTarget: %s", authorizationTarget);
83+
console2.log("implementation: %s", implementation);
84+
console2.log("beacon: %s", beacon);
85+
console2.log("proxy: %s", proxy);
86+
console2.log("coordinator: %s", coordinator);
87+
console2.log("eip712Name: EIP7702BatchDeleGator");
88+
console2.log("eip712Version: 1");
89+
console2.log("contractVersion: %s", EIP7702BatchDeleGator(payable(implementation)).VERSION());
90+
91+
vm.stopBroadcast();
92+
}
93+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// SPDX-License-Identifier: MIT AND Apache-2.0
2+
pragma solidity 0.8.23;
3+
4+
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
5+
6+
import { IDeleGatorBatchRelayCoordinator } from "./interfaces/IDeleGatorBatchRelayCoordinator.sol";
7+
import { IEIP7702BatchDeleGator } from "./interfaces/IEIP7702BatchDeleGator.sol";
8+
9+
/**
10+
* @title DeleGatorBatchRelayCoordinator
11+
* @notice Owner-gated multi-account coordinator for signed batch DeleGator relay execution.
12+
* @dev Non-atomic by default: a failed account row is recorded and later rows still execute.
13+
* @dev Does not forward ETH and does not authorize child account execution by itself.
14+
*/
15+
contract DeleGatorBatchRelayCoordinator is Ownable, IDeleGatorBatchRelayCoordinator {
16+
uint256 internal constant MAX_REVERT_DATA = 256;
17+
18+
/// @dev Emitted for each coordinator row after execution attempt.
19+
event BatchRowExecuted(uint256 indexed index, address indexed account, bool success, bytes revertData);
20+
21+
constructor(address initialOwner) Ownable(initialOwner) { }
22+
23+
/// @inheritdoc IDeleGatorBatchRelayCoordinator
24+
function executeBatches(AccountBatch[] calldata batches) external onlyOwner {
25+
uint256 len = batches.length;
26+
for (uint256 i = 0; i < len;) {
27+
AccountBatch calldata batch = batches[i];
28+
29+
(bool success, bytes memory revertData) = address(batch.account).call(
30+
abi.encodeWithSelector(IEIP7702BatchDeleGator.executeBatch.selector, batch.mode, batch.executionData)
31+
);
32+
33+
if (!success && revertData.length > MAX_REVERT_DATA) {
34+
revertData = abi.encodePacked(keccak256(revertData));
35+
}
36+
37+
emit BatchRowExecuted(i, batch.account, success, success ? bytes("") : revertData);
38+
39+
unchecked {
40+
++i;
41+
}
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)