Skip to content

feat: ERC20 enters #50

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 1, 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
14 changes: 8 additions & 6 deletions script/Zenith.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ 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 $SEQUENCER_ADMIN_ADDRESS
function deploy(uint256 defaultRollupChainId, address withdrawalAdmin, address sequencerAdmin)
public
returns (Zenith z, HostOrders m)
{
// 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
function deploy(
uint256 defaultRollupChainId,
address withdrawalAdmin,
address[] memory initialEnterTokens,
address sequencerAdmin
) public returns (Zenith z, HostOrders m) {
vm.startBroadcast();
z = new Zenith(defaultRollupChainId, withdrawalAdmin, sequencerAdmin);
z = new Zenith(defaultRollupChainId, withdrawalAdmin, initialEnterTokens, sequencerAdmin);
m = new HostOrders();
}

Expand Down
63 changes: 57 additions & 6 deletions src/Passage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,32 @@ contract Passage {
uint256 public immutable defaultRollupChainId;

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

/// @notice Thrown when attempting to withdraw funds if not withdrawal admin.
error OnlyWithdrawalAdmin();
/// @notice tokenAddress => whether new EnterToken events are currently allowed for that token.
mapping(address => bool) public canEnter;

/// @notice Thrown when attempting to call admin functions if not the token admin.
error OnlyTokenAdmin();

/// @notice Thrown when attempting to enter the rollup with an ERC20 token that is not currently allowed.
error DisallowedEnter(address token);

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

/// @notice Emitted when ERC20 tokens enter the rollup.
/// @param rollupChainId - The chainId of the destination rollup.
/// @param rollupRecipient - The recipient of tokens on the rollup.
/// @param token - The host chain address of the token entering the rollup.
/// @param amount - The amount of tokens entering the rollup.
event EnterToken(
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,
Expand All @@ -34,11 +50,17 @@ contract Passage {
/// @notice Emitted when the admin withdraws tokens from the contract.
event Withdrawal(address indexed token, address indexed recipient, uint256 amount);

/// @notice Emitted when the admin allow/disallow ERC20 Enters for a given token.
event EnterConfigured(address indexed token, bool indexed canEnter);

/// @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 _withdrawalAdmin) {
constructor(uint256 _defaultRollupChainId, address _tokenAdmin, address[] memory initialEnterTokens) {
defaultRollupChainId = _defaultRollupChainId;
withdrawalAdmin = _withdrawalAdmin;
tokenAdmin = _tokenAdmin;
for (uint256 i; i < initialEnterTokens.length; i++) {
_configureEnter(initialEnterTokens[i], true);
}
}

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

/// @notice Allows ERC20 tokens to enter the rollup.
/// @param rollupChainId - The rollup chain to enter.
/// @param rollupRecipient - The recipient of tokens on the rollup.
/// @param token - The host chain address of the token entering the rollup.
/// @param amount - The amount of tokens entering the rollup.
function enterToken(uint256 rollupChainId, address rollupRecipient, address token, uint256 amount) public {
if (!canEnter[token]) revert DisallowedEnter(token);
IERC20(token).transferFrom(msg.sender, address(this), amount);
emit EnterToken(rollupChainId, rollupRecipient, token, amount);
}

/// @notice Allows ERC20 tokens to enter the default rollup.
/// @dev see `enterToken` for docs.
function enterToken(address rollupRecipient, address token, uint256 amount) external {
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.
Expand Down Expand Up @@ -114,15 +153,27 @@ contract Passage {
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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

followup work:

bump solidity to 0.8.26 so we can use require(_, Error) instead of if-revert

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (canEnter[token] != _canEnter) _configureEnter(token, _canEnter);
}

/// @notice Allows the admin to withdraw ETH or ERC20 tokens from the contract.
/// @dev Only the admin can call this function.
function withdraw(address token, address recipient, uint256 amount) external {
if (msg.sender != withdrawalAdmin) revert OnlyWithdrawalAdmin();
if (msg.sender != tokenAdmin) revert OnlyTokenAdmin();
if (token == address(0)) {
payable(recipient).transfer(amount);
} else {
IERC20(token).transfer(recipient, amount);
}
emit Withdrawal(token, recipient, amount);
}

/// @notice Helper to configure ERC20 enters on deploy & via admin function
function _configureEnter(address token, bool _canEnter) internal {
canEnter[token] = _canEnter;
emit EnterConfigured(token, _canEnter);
}
}
9 changes: 6 additions & 3 deletions src/Zenith.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,12 @@ contract Zenith is Passage {
/// @notice Emitted when a sequencer is added or removed.
event SequencerSet(address indexed sequencer, bool indexed permissioned);

constructor(uint256 _defaultRollupChainId, address _withdrawalAdmin, address _sequencerAdmin)
Passage(_defaultRollupChainId, _withdrawalAdmin)
{
constructor(
uint256 _defaultRollupChainId,
address _withdrawalAdmin,
address[] memory initialEnterTokens,
address _sequencerAdmin
) Passage(_defaultRollupChainId, _withdrawalAdmin, initialEnterTokens) {
sequencerAdmin = _sequencerAdmin;
}

Expand Down
6 changes: 5 additions & 1 deletion test/Helpers.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ contract HelpersTest is Test {

function setUp() public {
vm.createSelectFork("https://rpc.holesky.ethpandaops.io");
address[] memory initialEnterTokens;
target = new Zenith(
block.chainid + 1, 0x11Aa4EBFbf7a481617c719a2Df028c9DA1a219aa, 0x29403F107781ea45Bf93710abf8df13F67f2008f
block.chainid + 1,
0x11Aa4EBFbf7a481617c719a2Df028c9DA1a219aa,
initialEnterTokens,
0x29403F107781ea45Bf93710abf8df13F67f2008f
);
}

Expand Down
3 changes: 2 additions & 1 deletion test/Zenith.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ contract ZenithTest is Test {
);

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

// set default block values
Expand Down