Skip to content

feat: configurable transact gas limits #61

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 3 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 18 additions & 13 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,28 @@ OrdersTest:test_sweepERC20() (gas: 60446)
OrdersTest:test_sweepETH() (gas: 81940)
OrdersTest:test_underflowETH() (gas: 63528)
PassageTest:test_configureEnter() (gas: 82311)
PassageTest:test_disallowedEnter() (gas: 17916)
PassageTest:test_enter() (gas: 25563)
PassageTest:test_enterToken() (gas: 64332)
PassageTest:test_enterToken_defaultChain() (gas: 62915)
PassageTest:test_enterTransact() (gas: 60890)
PassageTest:test_enter_defaultChain() (gas: 24033)
PassageTest:test_fallback() (gas: 21534)
PassageTest:test_onlyTokenAdmin() (gas: 16926)
PassageTest:test_receive() (gas: 21384)
PassageTest:test_setUp() (gas: 16968)
PassageTest:test_transact() (gas: 58562)
PassageTest:test_transact_defaultChain() (gas: 57475)
PassageTest:test_withdraw() (gas: 59166)
PassageTest:test_disallowedEnter() (gas: 17938)
PassageTest:test_enter() (gas: 25507)
PassageTest:test_enterToken() (gas: 64354)
PassageTest:test_enterToken_defaultChain() (gas: 62870)
PassageTest:test_enter_defaultChain() (gas: 24011)
PassageTest:test_fallback() (gas: 21445)
PassageTest:test_onlyTokenAdmin() (gas: 16881)
PassageTest:test_receive() (gas: 21339)
PassageTest:test_setUp() (gas: 16901)
PassageTest:test_withdraw() (gas: 59188)
RollupPassageTest:test_exit() (gas: 22347)
RollupPassageTest:test_exitToken() (gas: 50183)
RollupPassageTest:test_fallback() (gas: 19883)
RollupPassageTest:test_receive() (gas: 19844)
TransactTest:test_configureGas() (gas: 22828)
TransactTest:test_enterTransact() (gas: 103961)
TransactTest:test_onlyGasAdmin() (gas: 8810)
TransactTest:test_setUp() (gas: 17494)
TransactTest:test_transact() (gas: 101431)
TransactTest:test_transact_defaultChain() (gas: 100544)
TransactTest:test_transact_globalGasLimit() (gas: 105063)
TransactTest:test_transact_perTransactGasLimit() (gas: 32774)
ZenithTest:test_addSequencer() (gas: 88121)
ZenithTest:test_badSignature() (gas: 37241)
ZenithTest:test_incorrectHostBlock() (gas: 35086)
Expand Down
10 changes: 6 additions & 4 deletions script/Zenith.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@ pragma solidity ^0.8.24;
import {Script} from "forge-std/Script.sol";
import {Zenith} from "../src/Zenith.sol";
import {Passage, RollupPassage} from "../src/Passage.sol";
import {Transactor} from "../src/Transact.sol";
import {HostOrders, RollupOrders} from "../src/Orders.sol";

contract ZenithScript is Script {
// deploy:
// 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
// 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
function deploy(
uint256 defaultRollupChainId,
address withdrawalAdmin,
address[] memory initialEnterTokens,
address sequencerAdmin
) public returns (Zenith z, Passage p, HostOrders m) {
address sequencerAndGasAdmin
) public returns (Zenith z, Passage p, Transactor t, HostOrders m) {
vm.startBroadcast();
z = new Zenith(sequencerAdmin);
z = new Zenith(sequencerAndGasAdmin);
p = new Passage(defaultRollupChainId, withdrawalAdmin, initialEnterTokens);
t = new Transactor(defaultRollupChainId, sequencerAndGasAdmin, p, 30_000_000, 5_000_000);
m = new HostOrders();
}

Expand Down
59 changes: 0 additions & 59 deletions src/Passage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,6 @@ contract Passage {
uint256 indexed rollupChainId, address indexed rollupRecipient, address indexed token, uint256 amount
);

/// @notice Emitted to send a special transaction to the rollup.
event Transact(
uint256 indexed rollupChainId,
address indexed sender,
address indexed to,
bytes data,
uint256 value,
uint256 gas,
uint256 maxFeePerGas
);

/// @notice Emitted when the admin withdraws tokens from the contract.
event Withdrawal(address indexed token, address indexed recipient, uint256 amount);

Expand Down Expand Up @@ -106,54 +95,6 @@ contract Passage {
enterToken(defaultRollupChainId, rollupRecipient, token, amount);
}

/// @notice Allows a special transaction to be sent to the rollup with sender == L1 msg.sender.
/// @dev Transaction is processed after normal rollup block execution.
/// @dev See `enterTransact` for docs.
function transact(
uint256 rollupChainId,
address to,
bytes calldata data,
uint256 value,
uint256 gas,
uint256 maxFeePerGas
) public payable {
enterTransact(rollupChainId, msg.sender, to, data, value, gas, maxFeePerGas);
}

/// @dev See `transact` for docs.
function transact(address to, bytes calldata data, uint256 value, uint256 gas, uint256 maxFeePerGas)
external
payable
{
enterTransact(defaultRollupChainId, msg.sender, to, data, value, gas, maxFeePerGas);
}

/// @notice Send Ether on the rollup, send a special transaction to be sent to the rollup with sender == L1 msg.sender.
/// @dev Enter and Transact are processed after normal rollup block execution.
/// @dev See `enter` for Enter docs.
/// @param rollupChainId - The rollup chain to send the transaction to.
/// @param etherRecipient - The recipient of the ether.
/// @param to - The address to call on the rollup.
/// @param data - The data to send to the rollup.
/// @param value - The amount of Ether to send on the rollup.
/// @param gas - The gas limit for the transaction.
/// @param maxFeePerGas - The maximum fee per gas for the transaction (per EIP-1559).
/// @custom:emits Transact indicating the transaction to mine on the rollup.
function enterTransact(
uint256 rollupChainId,
address etherRecipient,
address to,
bytes calldata data,
uint256 value,
uint256 gas,
uint256 maxFeePerGas
) public payable {
// if msg.value is attached, Enter
enter(rollupChainId, etherRecipient);
// emit Transact event
emit Transact(rollupChainId, msg.sender, to, data, value, gas, maxFeePerGas);
}

/// @notice Alow/Disallow a given ERC20 token to enter the rollup.
function configureEnter(address token, bool _canEnter) external {
if (msg.sender != tokenAdmin) revert OnlyTokenAdmin();
Expand Down
136 changes: 136 additions & 0 deletions src/Transact.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {Passage} from "./Passage.sol";

/// @notice A contract deployed to Host chain that enables transactions from L1 to be sent on an L2.
contract Transactor {
/// @notice The chainId of rollup that Ether will be sent to by default when entering the rollup via fallback() or receive().
uint256 public immutable defaultRollupChainId;

/// @notice The address that is allowed to configure `transact` gas limits.
address public immutable gasAdmin;

/// @notice The address of the Passage contract, to enable transact + enter.
Passage public immutable passage;

/// @notice The sum of `transact` calls in a block cannot use more than this limit.
uint256 public perBlockGasLimit;

/// @notice Each `transact` call cannot use more than this limit.
uint256 public perTransactGasLimit;

/// @notice The total gas used by `transact` so far in this block.
/// rollupChainId => block number => `transasct` gasLimit used so far.
mapping(uint256 => mapping(uint256 => uint256)) public transactGasUsed;

/// @notice Emitted to send a special transaction to the rollup.
event Transact(
uint256 indexed rollupChainId,
address indexed sender,
address indexed to,
bytes data,
uint256 value,
uint256 gas,
uint256 maxFeePerGas
);

/// @notice Emitted when the admin configures gas limits.
event GasConfigured(uint256 perBlock, uint256 perTransact);

/// @notice Thrown when attempting to use more then the current global `transact` gasLimit for the block.
error PerBlockTransactGasLimit();

/// @notice Thrown when attempting to use too much gas per single `transact` call.
error PerTransactGasLimit();

/// @notice Thrown when attempting to configure gas if not the admin.
error OnlyGasAdmin();

/// @param _defaultRollupChainId - the chainId of the rollup that Ether will be sent to by default
/// when entering the rollup via fallback() or receive() fns.
constructor(
uint256 _defaultRollupChainId,
address _gasAdmin,
Passage _passage,
uint256 _perBlockGasLimit,
uint256 _perTransactGasLimit
) {
defaultRollupChainId = _defaultRollupChainId;
gasAdmin = _gasAdmin;
passage = _passage;
_configureGas(_perBlockGasLimit, _perTransactGasLimit);
}

/// @notice Configure the `transact` gas limits.
function configureGas(uint256 perBlock, uint256 perTransact) external {
if (msg.sender != gasAdmin) revert OnlyGasAdmin();
_configureGas(perBlock, perTransact);
}

/// @notice Allows a special transaction to be sent to the rollup with sender == L1 msg.sender.
/// @dev Transaction is processed after normal rollup block execution.
/// @dev See `enterTransact` for docs.
function transact(
uint256 rollupChainId,
address to,
bytes calldata data,
uint256 value,
uint256 gas,
uint256 maxFeePerGas
) public payable {
enterTransact(rollupChainId, msg.sender, to, data, value, gas, maxFeePerGas);
}

/// @dev See `transact` for docs.
function transact(address to, bytes calldata data, uint256 value, uint256 gas, uint256 maxFeePerGas)
external
payable
{
enterTransact(defaultRollupChainId, msg.sender, to, data, value, gas, maxFeePerGas);
}

/// @notice Send Ether on the rollup, send a special transaction to be sent to the rollup with sender == L1 msg.sender.
/// @dev Enter and Transact are processed after normal rollup block execution.
/// @dev See `enter` for Enter docs.
/// @param rollupChainId - The rollup chain to send the transaction to.
/// @param etherRecipient - The recipient of the ether.
/// @param to - The address to call on the rollup.
/// @param data - The data to send to the rollup.
/// @param value - The amount of Ether to send on the rollup.
/// @param gas - The gas limit for the transaction.
/// @param maxFeePerGas - The maximum fee per gas for the transaction (per EIP-1559).
/// @custom:emits Transact indicating the transaction to mine on the rollup.
function enterTransact(
uint256 rollupChainId,
address etherRecipient,
address to,
bytes calldata data,
uint256 value,
uint256 gas,
uint256 maxFeePerGas
) public payable {
// if msg.value is attached, Enter
if (msg.value > 0) {
passage.enter{value: msg.value}(rollupChainId, etherRecipient);
}

// ensure per-transact gas limit is respected
if (gas > perTransactGasLimit) revert PerTransactGasLimit();

// ensure global transact gas limit is respected
uint256 gasUsed = transactGasUsed[rollupChainId][block.number];
if (gasUsed + gas > perBlockGasLimit) revert PerBlockTransactGasLimit();
transactGasUsed[rollupChainId][block.number] = gasUsed + gas;

// emit Transact event
emit Transact(rollupChainId, msg.sender, to, data, value, gas, maxFeePerGas);
}

/// @notice Helper to configure gas limits on deploy & via admin function
function _configureGas(uint256 perBlock, uint256 perTransact) internal {
perBlockGasLimit = perBlock;
perTransactGasLimit = perTransact;
emit GasConfigured(perBlock, perTransact);
}
}
30 changes: 0 additions & 30 deletions test/Passage.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -151,36 +151,6 @@ contract PassageTest is Test {
target.enterToken(recipient, token, amount);
}

function test_transact() public {
vm.expectEmit();
emit Transact(chainId, address(this), to, data, value, gas, maxFeePerGas);
target.transact(chainId, to, data, value, gas, maxFeePerGas);

vm.expectEmit();
emit Enter(chainId, address(this), amount);
target.transact{value: amount}(chainId, to, data, value, gas, maxFeePerGas);
}

function test_transact_defaultChain() public {
vm.expectEmit();
emit Transact(target.defaultRollupChainId(), address(this), to, data, value, gas, maxFeePerGas);
target.transact(to, data, value, gas, maxFeePerGas);

vm.expectEmit();
emit Enter(target.defaultRollupChainId(), address(this), amount);
target.transact{value: amount}(to, data, value, gas, maxFeePerGas);
}

function test_enterTransact() public {
vm.expectEmit();
emit Transact(chainId, address(this), to, data, value, gas, maxFeePerGas);
target.enterTransact(chainId, recipient, to, data, value, gas, maxFeePerGas);

vm.expectEmit();
emit Enter(chainId, recipient, amount);
target.enterTransact{value: amount}(chainId, recipient, to, data, value, gas, maxFeePerGas);
}

function test_withdraw() public {
TestERC20(token).mint(address(target), amount);

Expand Down
Loading