Skip to content

Commit 80f8b6f

Browse files
authored
feat: ERC20 enters (#50)
1 parent 269ddd7 commit 80f8b6f

File tree

5 files changed

+78
-17
lines changed

5 files changed

+78
-17
lines changed

script/Zenith.s.sol

+8-6
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import {HostOrders, RollupOrders} from "../src/Orders.sol";
77

88
contract ZenithScript is Script {
99
// deploy:
10-
// 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 $SEQUENCER_ADMIN_ADDRESS
11-
function deploy(uint256 defaultRollupChainId, address withdrawalAdmin, address sequencerAdmin)
12-
public
13-
returns (Zenith z, HostOrders m)
14-
{
10+
// 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
11+
function deploy(
12+
uint256 defaultRollupChainId,
13+
address withdrawalAdmin,
14+
address[] memory initialEnterTokens,
15+
address sequencerAdmin
16+
) public returns (Zenith z, HostOrders m) {
1517
vm.startBroadcast();
16-
z = new Zenith(defaultRollupChainId, withdrawalAdmin, sequencerAdmin);
18+
z = new Zenith(defaultRollupChainId, withdrawalAdmin, initialEnterTokens, sequencerAdmin);
1719
m = new HostOrders();
1820
}
1921

src/Passage.sol

+57-6
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,32 @@ contract Passage {
1010
uint256 public immutable defaultRollupChainId;
1111

1212
/// @notice The address that is allowed to withdraw funds from the contract.
13-
address public immutable withdrawalAdmin;
13+
address public immutable tokenAdmin;
1414

15-
/// @notice Thrown when attempting to withdraw funds if not withdrawal admin.
16-
error OnlyWithdrawalAdmin();
15+
/// @notice tokenAddress => whether new EnterToken events are currently allowed for that token.
16+
mapping(address => bool) public canEnter;
17+
18+
/// @notice Thrown when attempting to call admin functions if not the token admin.
19+
error OnlyTokenAdmin();
20+
21+
/// @notice Thrown when attempting to enter the rollup with an ERC20 token that is not currently allowed.
22+
error DisallowedEnter(address token);
1723

1824
/// @notice Emitted when Ether enters the rollup.
25+
/// @param rollupChainId - The chainId of the destination rollup.
1926
/// @param rollupRecipient - The recipient of Ether on the rollup.
2027
/// @param amount - The amount of Ether entering the rollup.
2128
event Enter(uint256 indexed rollupChainId, address indexed rollupRecipient, uint256 amount);
2229

30+
/// @notice Emitted when ERC20 tokens enter the rollup.
31+
/// @param rollupChainId - The chainId of the destination rollup.
32+
/// @param rollupRecipient - The recipient of tokens on the rollup.
33+
/// @param token - The host chain address of the token entering the rollup.
34+
/// @param amount - The amount of tokens entering the rollup.
35+
event EnterToken(
36+
uint256 indexed rollupChainId, address indexed rollupRecipient, address indexed token, uint256 amount
37+
);
38+
2339
/// @notice Emitted to send a special transaction to the rollup.
2440
event Transact(
2541
uint256 indexed rollupChainId,
@@ -34,11 +50,17 @@ contract Passage {
3450
/// @notice Emitted when the admin withdraws tokens from the contract.
3551
event Withdrawal(address indexed token, address indexed recipient, uint256 amount);
3652

53+
/// @notice Emitted when the admin allow/disallow ERC20 Enters for a given token.
54+
event EnterConfigured(address indexed token, bool indexed canEnter);
55+
3756
/// @param _defaultRollupChainId - the chainId of the rollup that Ether will be sent to by default
3857
/// when entering the rollup via fallback() or receive() fns.
39-
constructor(uint256 _defaultRollupChainId, address _withdrawalAdmin) {
58+
constructor(uint256 _defaultRollupChainId, address _tokenAdmin, address[] memory initialEnterTokens) {
4059
defaultRollupChainId = _defaultRollupChainId;
41-
withdrawalAdmin = _withdrawalAdmin;
60+
tokenAdmin = _tokenAdmin;
61+
for (uint256 i; i < initialEnterTokens.length; i++) {
62+
_configureEnter(initialEnterTokens[i], true);
63+
}
4264
}
4365

4466
/// @notice Allows native Ether to enter the rollup by being sent directly to the contract.
@@ -66,6 +88,23 @@ contract Passage {
6688
enter(defaultRollupChainId, rollupRecipient);
6789
}
6890

91+
/// @notice Allows ERC20 tokens to enter the rollup.
92+
/// @param rollupChainId - The rollup chain to enter.
93+
/// @param rollupRecipient - The recipient of tokens on the rollup.
94+
/// @param token - The host chain address of the token entering the rollup.
95+
/// @param amount - The amount of tokens entering the rollup.
96+
function enterToken(uint256 rollupChainId, address rollupRecipient, address token, uint256 amount) public {
97+
if (!canEnter[token]) revert DisallowedEnter(token);
98+
IERC20(token).transferFrom(msg.sender, address(this), amount);
99+
emit EnterToken(rollupChainId, rollupRecipient, token, amount);
100+
}
101+
102+
/// @notice Allows ERC20 tokens to enter the default rollup.
103+
/// @dev see `enterToken` for docs.
104+
function enterToken(address rollupRecipient, address token, uint256 amount) external {
105+
enterToken(defaultRollupChainId, rollupRecipient, token, amount);
106+
}
107+
69108
/// @notice Allows a special transaction to be sent to the rollup with sender == L1 msg.sender.
70109
/// @dev Transaction is processed after normal rollup block execution.
71110
/// @dev See `enterTransact` for docs.
@@ -114,15 +153,27 @@ contract Passage {
114153
emit Transact(rollupChainId, msg.sender, to, data, value, gas, maxFeePerGas);
115154
}
116155

156+
/// @notice Alow/Disallow a given ERC20 token to enter the rollup.
157+
function configureEnter(address token, bool _canEnter) external {
158+
if (msg.sender != tokenAdmin) revert OnlyTokenAdmin();
159+
if (canEnter[token] != _canEnter) _configureEnter(token, _canEnter);
160+
}
161+
117162
/// @notice Allows the admin to withdraw ETH or ERC20 tokens from the contract.
118163
/// @dev Only the admin can call this function.
119164
function withdraw(address token, address recipient, uint256 amount) external {
120-
if (msg.sender != withdrawalAdmin) revert OnlyWithdrawalAdmin();
165+
if (msg.sender != tokenAdmin) revert OnlyTokenAdmin();
121166
if (token == address(0)) {
122167
payable(recipient).transfer(amount);
123168
} else {
124169
IERC20(token).transfer(recipient, amount);
125170
}
126171
emit Withdrawal(token, recipient, amount);
127172
}
173+
174+
/// @notice Helper to configure ERC20 enters on deploy & via admin function
175+
function _configureEnter(address token, bool _canEnter) internal {
176+
canEnter[token] = _canEnter;
177+
emit EnterConfigured(token, _canEnter);
178+
}
128179
}

src/Zenith.sol

+6-3
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,12 @@ contract Zenith is Passage {
6666
/// @notice Emitted when a sequencer is added or removed.
6767
event SequencerSet(address indexed sequencer, bool indexed permissioned);
6868

69-
constructor(uint256 _defaultRollupChainId, address _withdrawalAdmin, address _sequencerAdmin)
70-
Passage(_defaultRollupChainId, _withdrawalAdmin)
71-
{
69+
constructor(
70+
uint256 _defaultRollupChainId,
71+
address _withdrawalAdmin,
72+
address[] memory initialEnterTokens,
73+
address _sequencerAdmin
74+
) Passage(_defaultRollupChainId, _withdrawalAdmin, initialEnterTokens) {
7275
sequencerAdmin = _sequencerAdmin;
7376
deployBlockNumber = block.number;
7477
}

test/Helpers.t.sol

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ contract HelpersTest is Test {
99

1010
function setUp() public {
1111
vm.createSelectFork("https://rpc.holesky.ethpandaops.io");
12+
address[] memory initialEnterTokens;
1213
target = new Zenith(
13-
block.chainid + 1, 0x11Aa4EBFbf7a481617c719a2Df028c9DA1a219aa, 0x29403F107781ea45Bf93710abf8df13F67f2008f
14+
block.chainid + 1,
15+
0x11Aa4EBFbf7a481617c719a2Df028c9DA1a219aa,
16+
initialEnterTokens,
17+
0x29403F107781ea45Bf93710abf8df13F67f2008f
1418
);
1519
}
1620

test/Zenith.t.sol

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ contract ZenithTest is Test {
2424
);
2525

2626
function setUp() public {
27-
target = new Zenith(block.chainid + 1, address(this), address(this));
27+
address[] memory initialEnterTokens;
28+
target = new Zenith(block.chainid + 1, address(this), initialEnterTokens, address(this));
2829
target.addSequencer(vm.addr(sequencerKey));
2930

3031
// set default block values

0 commit comments

Comments
 (0)