Skip to content

refactor: modify style of Zenith functions #36

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 2 commits into from
May 29, 2024
Merged
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
193 changes: 60 additions & 133 deletions src/Passage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,53 +12,25 @@ contract Passage {
/// @notice The address that is allowed to withdraw funds from the contract.
address public immutable withdrawalAdmin;

/// @notice Thrown when attempting to fulfill an exit order with a deadline that has passed.
error OrderExpired();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this was no longer used in the Host passage contract


/// @notice Thrown when attempting to withdraw funds if not withdrawal admin.
error OnlyWithdrawalAdmin();

/// @notice Emitted when tokens enter the rollup.
/// @param token - The address of the token entering the rollup.
/// @param rollupRecipient - The recipient of the token on the rollup.
/// @param amount - The amount of the token entering the rollup.
event Enter(uint256 rollupChainId, address indexed token, address indexed rollupRecipient, uint256 amount);
event Enter(uint256 indexed rollupChainId, address indexed token, address indexed rollupRecipient, uint256 amount);

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

/// @notice Emitted when an exit order is fulfilled by the Builder.
/// @param token - The address of the token transferred to the recipient.
/// @param token - The address of the token transferred to the recipient. address(0) corresponds to native host Ether.
/// @param hostRecipient - The recipient of the token on host.
/// @param amount - The amount of the token transferred to the recipient.
event ExitFilled(uint256 rollupChainId, address indexed token, address indexed hostRecipient, uint256 amount);

/// @notice Emitted when the admin withdraws tokens from the contract.
event Withdraw(Withdrawal withdrawal);

/// @notice A bundled withdrawal of Ether and ERC20 tokens.
/// @param recipient - The address to receive the Ether and ERC20 tokens.
/// @param ethAmount - The amount of Ether to transfer to the recipient. Zero if no Ether to transfer.
/// @param tokens - The addresses of the ERC20 tokens to transfer to the recipient.
/// @param tokenAmounts - The amounts of the ERC20 tokens to transfer to the recipient.
struct Withdrawal {
address recipient;
uint256 ethAmount;
address[] tokens;
uint256[] tokenAmounts;
}

/// @notice Details of an exit order to be fulfilled by the Builder.
/// @param token - The address of the token to be transferred to the recipient.
/// If token is the zero address, the amount is native Ether.
/// Corresponds to tokenOut_H in the RollupPassage contract.
/// @param recipient - The recipient of the token on host.
/// Corresponds to recipient_H in the RollupPassage contract.
/// @param amount - The amount of the token to be transferred to the recipient.
/// Corresponds to one or more amountOutMinimum_H in the RollupPassage contract.
struct ExitOrder {
uint256 rollupChainId;
address token;
address recipient;
uint256 amount;
}
event ExitFulfilled(
uint256 indexed rollupChainId, address indexed token, address indexed hostRecipient, uint256 amount
);

/// @param _defaultRollupChainId - the chainId of the rollup that Ether will be sent to by default
/// when entering the rollup via fallback() or receive() fns.
Expand Down Expand Up @@ -93,64 +65,42 @@ contract Passage {
/// @param token - The address of the ERC20 token on the Host.
/// @param amount - The amount of the ERC20 token to transfer to the rollup.
/// @custom:emits Enter indicating the amount of tokens to mint on the rollup & its recipient.
function enter(uint256 rollupChainId, address rollupRecipient, address token, uint256 amount) public payable {
function enter(uint256 rollupChainId, address token, address rollupRecipient, uint256 amount) external payable {
IERC20(token).transferFrom(msg.sender, address(this), amount);
emit Enter(rollupChainId, token, rollupRecipient, amount);
}

/// @notice Fulfills exit orders by transferring tokenOut to the recipient
/// @param orders The exit orders to fulfill
/// @custom:emits ExitFilled for each exit order fulfilled.
/// @dev Builder SHOULD call `fulfillExits` atomically with `submitBlock`.
/// Builder SHOULD set a block expiration time that is AT MOST the minimum of all exit order deadlines;
/// this way, `fulfillExits` + `submitBlock` will revert atomically on mainnet if any exit orders have expired.
/// Otherwise, `fulfillExits` may mine on mainnet, while `submitExit` reverts on the rollup,
/// and the Builder can't collect the corresponding value on the rollup.
/// @dev Called by the Builder atomically with a transaction calling `submitBlock`.
/// The user-submitted transactions initiating the ExitOrders on the rollup
/// must be included by the Builder in the rollup block submitted via `submitBlock`.
/// @dev The user transfers tokenIn on the rollup, and receives tokenOut on host.
/// @dev The Builder receives tokenIn on the rollup, and transfers tokenOut to the user on host.
/// @dev The rollup STF MUST NOT apply `submitExit` transactions to the rollup state
/// UNLESS a corresponding ExitFilled event is emitted on host in the same block.
/// @dev If the user submits multiple exit transactions for the same token in the same rollup block,
/// the Builder may transfer the cumulative tokenOut to the user in a single ExitFilled event.
/// The rollup STF will apply the user's exit transactions on the rollup up to the point that sum(tokenOut) is lte the ExitFilled amount.
/// TODO: add option to fulfill ExitOrders with native ETH? or is it sufficient to only allow users to exit via WETH?
function fulfillExits(ExitOrder[] calldata orders) external payable {
uint256 ethRemaining = msg.value;
for (uint256 i = 0; i < orders.length; i++) {
// transfer value
if (orders[i].token == address(0)) {
// transfer native Ether to the recipient
payable(orders[i].recipient).transfer(orders[i].amount);
// NOTE: this will underflow if sender attempts to transfer more Ether than they sent to the contract
ethRemaining -= orders[i].amount;
} else {
// transfer tokens to the recipient
IERC20(orders[i].token).transferFrom(msg.sender, orders[i].recipient, orders[i].amount);
}
// emit
emit ExitFilled(orders[i].rollupChainId, orders[i].token, orders[i].recipient, orders[i].amount);
/// @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 (token == address(0)) {
payable(recipient).transfer(amount);
} else {
IERC20(token).transfer(recipient, amount);
}
emit Withdrawal(token, recipient, amount);
}

/// @notice Allows the admin to withdraw tokens from the contract.
/// @dev Only the admin can call this function.
/// @param withdrawals - The withdrawals to process. See Withdrawal struct docs for details.
function withdraw(Withdrawal[] calldata withdrawals) external {
if (msg.sender != withdrawalAdmin) revert OnlyWithdrawalAdmin();
for (uint256 i = 0; i < withdrawals.length; i++) {
// transfer ether
if (withdrawals[i].ethAmount > 0) {
payable(withdrawals[i].recipient).transfer(withdrawals[i].ethAmount);
}
// transfer ERC20 tokens
for (uint256 j = 0; j < withdrawals[i].tokens.length; j++) {
IERC20(withdrawals[i].tokens[j]).transfer(withdrawals[i].recipient, withdrawals[i].tokenAmounts[j]);
}
emit Withdraw(withdrawals[i]);
/// @notice Fulfill a rollup Exit order.
/// The user calls `exit` on Rollup; the Builder calls `fulfillExit` on Host.
/// @custom:emits ExitFilled
/// @param rollupChainId - The chainId of the rollup on which the `submitExit` was called.
/// @param token - The address of the token to be transferred to the recipient.
/// If token is the zero address, the amount is native Ether.
/// Corresponds to tokenOut_H in the RollupPassage contract.
/// @param recipient - The recipient of the token on host.
/// Corresponds to recipient_H in the RollupPassage contract.
/// @param amount - The amount of the token to be transferred to the recipient.
/// Corresponds to one or more amountOutMinimum_H in the RollupPassage contract.
function fulfillExit(uint256 rollupChainId, address token, address recipient, uint256 amount) external payable {
if (token == address(0)) {
require(amount == msg.value);
payable(recipient).transfer(msg.value);
} else {
IERC20(token).transferFrom(msg.sender, recipient, amount);
}
emit ExitFulfilled(rollupChainId, token, recipient, amount);
}
}

Expand All @@ -159,8 +109,8 @@ contract RollupPassage {
/// @notice Thrown when an exit transaction is submitted with a deadline that has passed.
error OrderExpired();

/// @notice Emitted when an exit order is submitted & successfully processed, indicating it was also fulfilled on host.
/// @dev See `submitExit` for parameter docs.
/// @notice Emitted when an exit order is successfully processed, indicating it was also fulfilled on host.
/// @dev See `exit` for parameter docs.
event Exit(
address indexed tokenIn_RU,
address indexed tokenOut_H,
Expand All @@ -173,9 +123,9 @@ contract RollupPassage {
/// @notice Emitted when tokens or native Ether is swept from the contract.
/// @dev Intended to improve visibility for Builders to ensure Sweep isn't called unexpectedly.
/// Intentionally does not bother to emit which token(s) were swept, nor their amounts.
event Sweep(address indexed recipient);
event Sweep(address indexed token, address indexed recipient, uint256 amount);

/// @notice Expresses an intent to exit the rollup with ERC20s.
/// @notice Request to exit the rollup with ERC20s.
/// @dev Exits are modeled as a swap between two tokens.
/// tokenIn_RU is provided on the rollup; in exchange,
/// tokenOut_H is expected to be received on host.
Expand All @@ -195,65 +145,42 @@ contract RollupPassage {
/// @param amountOutMinimum_H - The minimum amount of tokenOut_H the user expects to receive on host.
/// @custom:reverts Expired if the deadline has passed.
/// @custom:emits Exit if the exit transaction succeeds.
function submitExit(
function exit(
address tokenIn_RU,
address tokenOut_H,
address recipient_H,
uint256 deadline,
uint256 amountIn_RU,
uint256 amountOutMinimum_H
) external {
) external payable {
// check that the deadline hasn't passed
if (block.timestamp >= deadline) revert OrderExpired();

// transfer the tokens from the user to the contract
IERC20(tokenIn_RU).transferFrom(msg.sender, address(this), amountIn_RU);
if (tokenIn_RU == address(0)) {
require(amountIn_RU == msg.value);
} else {
IERC20(tokenIn_RU).transferFrom(msg.sender, address(this), amountIn_RU);
}

// emit the exit event
emit Exit(tokenIn_RU, tokenOut_H, recipient_H, deadline, amountIn_RU, amountOutMinimum_H);
}

/// @notice Expresses an intent to exit the rollup with native Ether.
/// @dev See `submitExit` above for dev details on how exits work.
/// @dev tokenIn_RU is set to address(0), native rollup Ether.
/// amountIn_RU is set to msg.value.
/// @param tokenOut_H - The address of the token the user expects to receive on host.
/// @param recipient_H - The address of the recipient of tokenOut_H on host.
/// @param deadline - The deadline by which the exit order must be fulfilled.
/// @param amountOutMinimum_H - The minimum amount of tokenOut_H the user expects to receive on host.
/// @custom:reverts Expired if the deadline has passed.
/// @custom:emits Exit if the exit transaction succeeds.
function submitEthExit(address tokenOut_H, address recipient_H, uint256 deadline, uint256 amountOutMinimum_H)
external
payable
{
// check that the deadline hasn't passed
if (block.timestamp >= deadline) revert OrderExpired();

// emit the exit event
emit Exit(address(0), tokenOut_H, recipient_H, deadline, msg.value, amountOutMinimum_H);
}

/// @notice Transfer the entire balance of tokens to the recipient.
/// @dev Called by the Builder within the same block as `submitExit` transactions to claim the amounts of `tokenIn`.
/// @notice Transfer the entire balance of ERC20 tokens to the recipient.
/// @dev Called by the Builder within the same block as users' `swap` transactions
/// to claim the amounts of `tokenIn`.
/// @dev Builder MUST ensure that no other account calls `sweep` before them.
/// @param token - The token to transfer.
/// @param recipient - The address to receive the tokens.
/// @param tokens - The addresses of the tokens to transfer.
/// TODO: should there be more granular control for the builder to specify a different recipient for each token?
function sweep(address recipient, address[] calldata tokens) public {
for (uint256 i = 0; i < tokens.length; i++) {
IERC20 token = IERC20(tokens[i]);
token.transfer(recipient, token.balanceOf(address(this)));
function sweep(address token, address recipient) public {
uint256 balance;
if (token == address(0)) {
balance = address(this).balance;
payable(recipient).transfer(balance);
} else {
balance = IERC20(token).balanceOf(address(this));
IERC20(token).transfer(recipient, balance);
}
emit Sweep(recipient);
}

/// @notice Transfer the entire balance of native Ether to the recipient.
/// @dev Called by the Builder within the same block as `submitExit` transactions to claim the amounts of native Ether.
/// @dev Builder MUST ensure that no other account calls `sweepETH` before them.
/// @param recipient - The address to receive the native Ether.
function sweepEth(address payable recipient) public {
recipient.transfer(address(this).balance);
emit Sweep(recipient);
emit Sweep(token, recipient, balance);
}
}