Skip to content

Commit 9e8e460

Browse files
authored
feat: configurable transact gas limits (#61)
* feat: move Transact to its own contract * feat: non-configurable gas limits * feat: configurable gas limits
1 parent a24ddde commit 9e8e460

File tree

6 files changed

+275
-106
lines changed

6 files changed

+275
-106
lines changed

.gas-snapshot

+18-13
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,28 @@ OrdersTest:test_sweepERC20() (gas: 60446)
1414
OrdersTest:test_sweepETH() (gas: 81940)
1515
OrdersTest:test_underflowETH() (gas: 63528)
1616
PassageTest:test_configureEnter() (gas: 82311)
17-
PassageTest:test_disallowedEnter() (gas: 17916)
18-
PassageTest:test_enter() (gas: 25563)
19-
PassageTest:test_enterToken() (gas: 64332)
20-
PassageTest:test_enterToken_defaultChain() (gas: 62915)
21-
PassageTest:test_enterTransact() (gas: 60890)
22-
PassageTest:test_enter_defaultChain() (gas: 24033)
23-
PassageTest:test_fallback() (gas: 21534)
24-
PassageTest:test_onlyTokenAdmin() (gas: 16926)
25-
PassageTest:test_receive() (gas: 21384)
26-
PassageTest:test_setUp() (gas: 16968)
27-
PassageTest:test_transact() (gas: 58562)
28-
PassageTest:test_transact_defaultChain() (gas: 57475)
29-
PassageTest:test_withdraw() (gas: 59166)
17+
PassageTest:test_disallowedEnter() (gas: 17938)
18+
PassageTest:test_enter() (gas: 25507)
19+
PassageTest:test_enterToken() (gas: 64354)
20+
PassageTest:test_enterToken_defaultChain() (gas: 62870)
21+
PassageTest:test_enter_defaultChain() (gas: 24011)
22+
PassageTest:test_fallback() (gas: 21445)
23+
PassageTest:test_onlyTokenAdmin() (gas: 16881)
24+
PassageTest:test_receive() (gas: 21339)
25+
PassageTest:test_setUp() (gas: 16901)
26+
PassageTest:test_withdraw() (gas: 59188)
3027
RollupPassageTest:test_exit() (gas: 22347)
3128
RollupPassageTest:test_exitToken() (gas: 50183)
3229
RollupPassageTest:test_fallback() (gas: 19883)
3330
RollupPassageTest:test_receive() (gas: 19844)
31+
TransactTest:test_configureGas() (gas: 22828)
32+
TransactTest:test_enterTransact() (gas: 103961)
33+
TransactTest:test_onlyGasAdmin() (gas: 8810)
34+
TransactTest:test_setUp() (gas: 17494)
35+
TransactTest:test_transact() (gas: 101431)
36+
TransactTest:test_transact_defaultChain() (gas: 100544)
37+
TransactTest:test_transact_globalGasLimit() (gas: 105063)
38+
TransactTest:test_transact_perTransactGasLimit() (gas: 32774)
3439
ZenithTest:test_addSequencer() (gas: 88121)
3540
ZenithTest:test_badSignature() (gas: 37241)
3641
ZenithTest:test_incorrectHostBlock() (gas: 35086)

script/Zenith.s.sol

+6-4
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,22 @@ pragma solidity ^0.8.24;
44
import {Script} from "forge-std/Script.sol";
55
import {Zenith} from "../src/Zenith.sol";
66
import {Passage, RollupPassage} from "../src/Passage.sol";
7+
import {Transactor} from "../src/Transact.sol";
78
import {HostOrders, RollupOrders} from "../src/Orders.sol";
89

910
contract ZenithScript is Script {
1011
// deploy:
11-
// forge script ZenithScript --sig "deploy(uint256,address,address)" --rpc-url $RPC_URL --etherscan-api-key $ETHERSCAN_API_KEY --private-key $PRIVATE_KEY --broadcast --verify $ROLLUP_CHAIN_ID $WITHDRAWAL_ADMIN_ADDRESS $INITIAL_ENTER_TOKENS_ARRAY $SEQUENCER_ADMIN_ADDRESS
12+
// forge script ZenithScript --sig "deploy(uint256,address,address)" --rpc-url $RPC_URL --etherscan-api-key $ETHERSCAN_API_KEY --private-key $PRIVATE_KEY --broadcast --verify $ROLLUP_CHAIN_ID $WITHDRAWAL_ADMIN_ADDRESS $INITIAL_ENTER_TOKENS_ARRAY $SEQUENCER_AND_GAS_ADMIN_ADDRESS
1213
function deploy(
1314
uint256 defaultRollupChainId,
1415
address withdrawalAdmin,
1516
address[] memory initialEnterTokens,
16-
address sequencerAdmin
17-
) public returns (Zenith z, Passage p, HostOrders m) {
17+
address sequencerAndGasAdmin
18+
) public returns (Zenith z, Passage p, Transactor t, HostOrders m) {
1819
vm.startBroadcast();
19-
z = new Zenith(sequencerAdmin);
20+
z = new Zenith(sequencerAndGasAdmin);
2021
p = new Passage(defaultRollupChainId, withdrawalAdmin, initialEnterTokens);
22+
t = new Transactor(defaultRollupChainId, sequencerAndGasAdmin, p, 30_000_000, 5_000_000);
2123
m = new HostOrders();
2224
}
2325

src/Passage.sol

-59
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,6 @@ contract Passage {
3636
uint256 indexed rollupChainId, address indexed rollupRecipient, address indexed token, uint256 amount
3737
);
3838

39-
/// @notice Emitted to send a special transaction to the rollup.
40-
event Transact(
41-
uint256 indexed rollupChainId,
42-
address indexed sender,
43-
address indexed to,
44-
bytes data,
45-
uint256 value,
46-
uint256 gas,
47-
uint256 maxFeePerGas
48-
);
49-
5039
/// @notice Emitted when the admin withdraws tokens from the contract.
5140
event Withdrawal(address indexed token, address indexed recipient, uint256 amount);
5241

@@ -106,54 +95,6 @@ contract Passage {
10695
enterToken(defaultRollupChainId, rollupRecipient, token, amount);
10796
}
10897

109-
/// @notice Allows a special transaction to be sent to the rollup with sender == L1 msg.sender.
110-
/// @dev Transaction is processed after normal rollup block execution.
111-
/// @dev See `enterTransact` for docs.
112-
function transact(
113-
uint256 rollupChainId,
114-
address to,
115-
bytes calldata data,
116-
uint256 value,
117-
uint256 gas,
118-
uint256 maxFeePerGas
119-
) public payable {
120-
enterTransact(rollupChainId, msg.sender, to, data, value, gas, maxFeePerGas);
121-
}
122-
123-
/// @dev See `transact` for docs.
124-
function transact(address to, bytes calldata data, uint256 value, uint256 gas, uint256 maxFeePerGas)
125-
external
126-
payable
127-
{
128-
enterTransact(defaultRollupChainId, msg.sender, to, data, value, gas, maxFeePerGas);
129-
}
130-
131-
/// @notice Send Ether on the rollup, send a special transaction to be sent to the rollup with sender == L1 msg.sender.
132-
/// @dev Enter and Transact are processed after normal rollup block execution.
133-
/// @dev See `enter` for Enter docs.
134-
/// @param rollupChainId - The rollup chain to send the transaction to.
135-
/// @param etherRecipient - The recipient of the ether.
136-
/// @param to - The address to call on the rollup.
137-
/// @param data - The data to send to the rollup.
138-
/// @param value - The amount of Ether to send on the rollup.
139-
/// @param gas - The gas limit for the transaction.
140-
/// @param maxFeePerGas - The maximum fee per gas for the transaction (per EIP-1559).
141-
/// @custom:emits Transact indicating the transaction to mine on the rollup.
142-
function enterTransact(
143-
uint256 rollupChainId,
144-
address etherRecipient,
145-
address to,
146-
bytes calldata data,
147-
uint256 value,
148-
uint256 gas,
149-
uint256 maxFeePerGas
150-
) public payable {
151-
// if msg.value is attached, Enter
152-
enter(rollupChainId, etherRecipient);
153-
// emit Transact event
154-
emit Transact(rollupChainId, msg.sender, to, data, value, gas, maxFeePerGas);
155-
}
156-
15798
/// @notice Alow/Disallow a given ERC20 token to enter the rollup.
15899
function configureEnter(address token, bool _canEnter) external {
159100
if (msg.sender != tokenAdmin) revert OnlyTokenAdmin();

src/Transact.sol

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.24;
3+
4+
import {Passage} from "./Passage.sol";
5+
6+
/// @notice A contract deployed to Host chain that enables transactions from L1 to be sent on an L2.
7+
contract Transactor {
8+
/// @notice The chainId of rollup that Ether will be sent to by default when entering the rollup via fallback() or receive().
9+
uint256 public immutable defaultRollupChainId;
10+
11+
/// @notice The address that is allowed to configure `transact` gas limits.
12+
address public immutable gasAdmin;
13+
14+
/// @notice The address of the Passage contract, to enable transact + enter.
15+
Passage public immutable passage;
16+
17+
/// @notice The sum of `transact` calls in a block cannot use more than this limit.
18+
uint256 public perBlockGasLimit;
19+
20+
/// @notice Each `transact` call cannot use more than this limit.
21+
uint256 public perTransactGasLimit;
22+
23+
/// @notice The total gas used by `transact` so far in this block.
24+
/// rollupChainId => block number => `transasct` gasLimit used so far.
25+
mapping(uint256 => mapping(uint256 => uint256)) public transactGasUsed;
26+
27+
/// @notice Emitted to send a special transaction to the rollup.
28+
event Transact(
29+
uint256 indexed rollupChainId,
30+
address indexed sender,
31+
address indexed to,
32+
bytes data,
33+
uint256 value,
34+
uint256 gas,
35+
uint256 maxFeePerGas
36+
);
37+
38+
/// @notice Emitted when the admin configures gas limits.
39+
event GasConfigured(uint256 perBlock, uint256 perTransact);
40+
41+
/// @notice Thrown when attempting to use more then the current global `transact` gasLimit for the block.
42+
error PerBlockTransactGasLimit();
43+
44+
/// @notice Thrown when attempting to use too much gas per single `transact` call.
45+
error PerTransactGasLimit();
46+
47+
/// @notice Thrown when attempting to configure gas if not the admin.
48+
error OnlyGasAdmin();
49+
50+
/// @param _defaultRollupChainId - the chainId of the rollup that Ether will be sent to by default
51+
/// when entering the rollup via fallback() or receive() fns.
52+
constructor(
53+
uint256 _defaultRollupChainId,
54+
address _gasAdmin,
55+
Passage _passage,
56+
uint256 _perBlockGasLimit,
57+
uint256 _perTransactGasLimit
58+
) {
59+
defaultRollupChainId = _defaultRollupChainId;
60+
gasAdmin = _gasAdmin;
61+
passage = _passage;
62+
_configureGas(_perBlockGasLimit, _perTransactGasLimit);
63+
}
64+
65+
/// @notice Configure the `transact` gas limits.
66+
function configureGas(uint256 perBlock, uint256 perTransact) external {
67+
if (msg.sender != gasAdmin) revert OnlyGasAdmin();
68+
_configureGas(perBlock, perTransact);
69+
}
70+
71+
/// @notice Allows a special transaction to be sent to the rollup with sender == L1 msg.sender.
72+
/// @dev Transaction is processed after normal rollup block execution.
73+
/// @dev See `enterTransact` for docs.
74+
function transact(
75+
uint256 rollupChainId,
76+
address to,
77+
bytes calldata data,
78+
uint256 value,
79+
uint256 gas,
80+
uint256 maxFeePerGas
81+
) public payable {
82+
enterTransact(rollupChainId, msg.sender, to, data, value, gas, maxFeePerGas);
83+
}
84+
85+
/// @dev See `transact` for docs.
86+
function transact(address to, bytes calldata data, uint256 value, uint256 gas, uint256 maxFeePerGas)
87+
external
88+
payable
89+
{
90+
enterTransact(defaultRollupChainId, msg.sender, to, data, value, gas, maxFeePerGas);
91+
}
92+
93+
/// @notice Send Ether on the rollup, send a special transaction to be sent to the rollup with sender == L1 msg.sender.
94+
/// @dev Enter and Transact are processed after normal rollup block execution.
95+
/// @dev See `enter` for Enter docs.
96+
/// @param rollupChainId - The rollup chain to send the transaction to.
97+
/// @param etherRecipient - The recipient of the ether.
98+
/// @param to - The address to call on the rollup.
99+
/// @param data - The data to send to the rollup.
100+
/// @param value - The amount of Ether to send on the rollup.
101+
/// @param gas - The gas limit for the transaction.
102+
/// @param maxFeePerGas - The maximum fee per gas for the transaction (per EIP-1559).
103+
/// @custom:emits Transact indicating the transaction to mine on the rollup.
104+
function enterTransact(
105+
uint256 rollupChainId,
106+
address etherRecipient,
107+
address to,
108+
bytes calldata data,
109+
uint256 value,
110+
uint256 gas,
111+
uint256 maxFeePerGas
112+
) public payable {
113+
// if msg.value is attached, Enter
114+
if (msg.value > 0) {
115+
passage.enter{value: msg.value}(rollupChainId, etherRecipient);
116+
}
117+
118+
// ensure per-transact gas limit is respected
119+
if (gas > perTransactGasLimit) revert PerTransactGasLimit();
120+
121+
// ensure global transact gas limit is respected
122+
uint256 gasUsed = transactGasUsed[rollupChainId][block.number];
123+
if (gasUsed + gas > perBlockGasLimit) revert PerBlockTransactGasLimit();
124+
transactGasUsed[rollupChainId][block.number] = gasUsed + gas;
125+
126+
// emit Transact event
127+
emit Transact(rollupChainId, msg.sender, to, data, value, gas, maxFeePerGas);
128+
}
129+
130+
/// @notice Helper to configure gas limits on deploy & via admin function
131+
function _configureGas(uint256 perBlock, uint256 perTransact) internal {
132+
perBlockGasLimit = perBlock;
133+
perTransactGasLimit = perTransact;
134+
emit GasConfigured(perBlock, perTransact);
135+
}
136+
}

test/Passage.t.sol

-30
Original file line numberDiff line numberDiff line change
@@ -151,36 +151,6 @@ contract PassageTest is Test {
151151
target.enterToken(recipient, token, amount);
152152
}
153153

154-
function test_transact() public {
155-
vm.expectEmit();
156-
emit Transact(chainId, address(this), to, data, value, gas, maxFeePerGas);
157-
target.transact(chainId, to, data, value, gas, maxFeePerGas);
158-
159-
vm.expectEmit();
160-
emit Enter(chainId, address(this), amount);
161-
target.transact{value: amount}(chainId, to, data, value, gas, maxFeePerGas);
162-
}
163-
164-
function test_transact_defaultChain() public {
165-
vm.expectEmit();
166-
emit Transact(target.defaultRollupChainId(), address(this), to, data, value, gas, maxFeePerGas);
167-
target.transact(to, data, value, gas, maxFeePerGas);
168-
169-
vm.expectEmit();
170-
emit Enter(target.defaultRollupChainId(), address(this), amount);
171-
target.transact{value: amount}(to, data, value, gas, maxFeePerGas);
172-
}
173-
174-
function test_enterTransact() public {
175-
vm.expectEmit();
176-
emit Transact(chainId, address(this), to, data, value, gas, maxFeePerGas);
177-
target.enterTransact(chainId, recipient, to, data, value, gas, maxFeePerGas);
178-
179-
vm.expectEmit();
180-
emit Enter(chainId, recipient, amount);
181-
target.enterTransact{value: amount}(chainId, recipient, to, data, value, gas, maxFeePerGas);
182-
}
183-
184154
function test_withdraw() public {
185155
TestERC20(token).mint(address(target), amount);
186156

0 commit comments

Comments
 (0)