Skip to content
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
188 changes: 94 additions & 94 deletions src/Passage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,79 +4,79 @@ pragma solidity ^0.8.24;
// import IERC20 from OpenZeppelin
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

// @notice A contract deployed to Host chain that allows tokens to enter the rollup,
// and enables Builders to fulfill requests to exchange tokens on the Rollup for tokens on the Host.
/// @notice A contract deployed to Host chain that allows tokens to enter the rollup,
/// and enables Builders to fulfill requests to exchange tokens on the Rollup for tokens on the Host.
contract HostPassage {
// @notice Thrown when attempting to fulfill an exit order with a deadline that has passed.
error Expired();
/// @notice Thrown when attempting to fulfill an exit order with a deadline that has passed.
error OrderExpired();

// @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.
/// @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(address indexed token, address indexed rollupRecipient, 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 hostRecipient - The recipient of the token on host.
// @param amount - The amount of the token transferred to the recipient.
/// @notice Emitted when an exit order is fulfilled by the Builder.
/// @param token - The address of the token transferred to the recipient.
/// @param hostRecipient - The recipient of the token on host.
/// @param amount - The amount of the token transferred to the recipient.
event ExitFilled(address indexed token, address indexed hostRecipient, uint256 amount);

// @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.
// @param deadline - The deadline by which the exit order must be fulfilled.
// Corresponds to deadline in the RollupPassage contract.
// If the ExitOrder is a combination of multiple orders, the deadline SHOULD be the latest of all orders.
/// @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.
/// @param deadline - The deadline by which the exit order must be fulfilled.
/// Corresponds to deadline in the RollupPassage contract.
/// If the ExitOrder is a combination of multiple orders, the deadline SHOULD be the latest of all orders.
struct ExitOrder {
address token;
address recipient;
uint256 amount;
uint256 deadline;
}

// @notice Allows native Ether to enter the rollup by being sent directly to the contract.
/// @notice Allows native Ether to enter the rollup by being sent directly to the contract.
fallback() external payable {
enter(msg.sender);
}

// @notice Allows native Ether to enter the rollup by being sent directly to the contract.
/// @notice Allows native Ether to enter the rollup by being sent directly to the contract.
receive() external payable {
enter(msg.sender);
}

// @notice Allows native Ether to enter the rollup.
// @dev Permanently burns the entire msg.value by locking it in this contract.
// @param rollupRecipient - The recipient of the Ether on the rollup.
// @custom:emits Enter indicatig the amount of Ether to mint on the rollup & its recipient.
/// @notice Allows native Ether to enter the rollup.
/// @dev Permanently burns the entire msg.value by locking it in this contract.
/// @param rollupRecipient - The recipient of the Ether on the rollup.
/// @custom:emits Enter indicatig the amount of Ether to mint on the rollup & its recipient.
function enter(address rollupRecipient) public payable {
emit Enter(address(0), rollupRecipient, msg.value);
}

// @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 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?
/// @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 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 {
for (uint256 i = 0; i < orders.length; i++) {
ExitOrder memory order = orders[i];
// check that the deadline hasn't passed
if (block.timestamp >= order.deadline) revert Expired();
if (block.timestamp > order.deadline) revert OrderExpired();
// transfer tokens to the recipient
IERC20(order.token).transferFrom(msg.sender, order.recipient, order.amount);
// emit
Expand All @@ -85,13 +85,13 @@ contract HostPassage {
}
}

// A contract deployed to the Rollup that allows users to atomically exchange tokens on the Rollup for tokens on the Host.
/// @notice A contract deployed to the Rollup that allows users to atomically exchange tokens on the Rollup for tokens on the Host.
contract RollupPassage {
// @notice Thrown when an exit tranaction is submitted with a deadline that has passed.
error Expired();
/// @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 submitted & successfully processed, indicating it was also fulfilled on host.
/// @dev See `submitExit` for parameter docs.
event Exit(
address indexed tokenIn_RU,
address indexed tokenOut_H,
Expand All @@ -101,31 +101,31 @@ contract RollupPassage {
uint256 amountOutMinimum_H
);

// @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.
/// @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);

// @notice Expresses an intent 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.
// Exits may "swap" native rollup Ether for host WETH -
// two assets that represent the same underlying token and should have roughly the same value -
// or they may be a more "true" swap of rollup USDC for host WETH.
// Fees paid to the Builders for fulfilling the exit orders
// can be included within the "exchange rate" between tokenIn and tokenOut.
// @dev The Builder claims the tokenIn_RU from the contract by submitting a transaction to `sweep` the tokens within the same block.
// @dev The Rollup STF MUST NOT apply `submitExit` transactions to the rollup state
// UNLESS a sufficient ExitFilled event is emitted on host within the same block.
// @param tokenIn_RU - The address of the token the user supplies as the input on the rollup for the trade.
// @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 amountIn_RU - The amount of tokenIn_RU the user supplies as the input on the rollup for the trade.
// @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.
/// @notice Expresses an intent 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.
/// Exits may "swap" native rollup Ether for host WETH -
/// two assets that represent the same underlying token and should have roughly the same value -
/// or they may be a more "true" swap of rollup USDC for host WETH.
/// Fees paid to the Builders for fulfilling the exit orders
/// can be included within the "exchange rate" between tokenIn and tokenOut.
/// @dev The Builder claims the tokenIn_RU from the contract by submitting a transaction to `sweep` the tokens within the same block.
/// @dev The Rollup STF MUST NOT apply `submitExit` transactions to the rollup state
/// UNLESS a sufficient ExitFilled event is emitted on host within the same block.
/// @param tokenIn_RU - The address of the token the user supplies as the input on the rollup for the trade.
/// @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 amountIn_RU - The amount of tokenIn_RU the user supplies as the input on the rollup for the trade.
/// @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(
address tokenIn_RU,
address tokenOut_H,
Expand All @@ -135,7 +135,7 @@ contract RollupPassage {
uint256 amountOutMinimum_H
) external {
// check that the deadline hasn't passed
if (block.timestamp >= deadline) revert Expired();
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);
Expand All @@ -144,33 +144,33 @@ contract RollupPassage {
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.
/// @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 submitExit(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 Expired();
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`.
// @dev Builder MUST ensure that no other account calls `sweep` before them.
// @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?
/// @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`.
/// @dev Builder MUST ensure that no other account calls `sweep` before them.
/// @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]);
Expand All @@ -179,10 +179,10 @@ contract RollupPassage {
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.
/// @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);
Expand Down
Loading