Skip to content
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

refactor: reorganize contracts #68

Merged
merged 1 commit into from
Jul 20, 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
9 changes: 6 additions & 3 deletions script/Zenith.s.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {Script} from "forge-std/Script.sol";
// deploy contracts
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";
import {HostOrders, RollupOrders} from "../src/orders/Orders.sol";
import {Passage} from "../src/passage/Passage.sol";
import {RollupPassage} from "../src/passage/RollupPassage.sol";
// utils
import {Script} from "forge-std/Script.sol";

contract ZenithScript is Script {
// deploy:
Expand Down
2 changes: 1 addition & 1 deletion src/Transact.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

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

/// @notice A contract deployed to Host chain that enables transactions from L1 to be sent on an L2.
contract Transactor {
Expand Down
38 changes: 38 additions & 0 deletions src/UsesPermit2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {IOrders} from "./orders/IOrders.sol";
import {ISignatureTransfer} from "permit2/src/interfaces/ISignatureTransfer.sol";

abstract contract UsesPermit2 {
/// @param permit - the permit2 single token transfer details. includes a `deadline` and an unordered `nonce`.
/// @param signer - the signer of the permit2 info; the owner of the tokens.
/// @param signature - the signature over the permit + witness.
struct Permit2 {
ISignatureTransfer.PermitTransferFrom permit;
address owner;
bytes signature;
}

/// @param permit - the permit2 batch token transfer details. includes a `deadline` and an unordered `nonce`.
/// @param signer - the signer of the permit2 info; the owner of the tokens.
/// @param signature - the signature over the permit + witness.
struct Permit2Batch {
ISignatureTransfer.PermitBatchTransferFrom permit;
address owner;
bytes signature;
}

/// @notice Struct to hold the pre-hashed witness field and the witness type string.
struct Witness {
bytes32 witnessHash;
string witnessTypeString;
}

/// @notice The Permit2 contract address.
address immutable permit2Contract;

constructor(address _permit2) {
permit2Contract = _permit2;
}
}
File renamed without changes.
60 changes: 60 additions & 0 deletions src/orders/OrderDestination.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {OrdersPermit2} from "./OrdersPermit2.sol";
import {IOrders} from "./IOrders.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

/// @notice Contract capable of processing fulfillment of intent-based Orders.
abstract contract OrderDestination is IOrders, OrdersPermit2 {
/// @notice Emitted when Order Outputs are sent to their recipients.
/// @dev NOTE that here, Output.chainId denotes the *origin* chainId.
event Filled(Output[] outputs);

/// @notice Fill any number of Order(s), by transferring their Output(s).
/// @dev Filler may aggregate multiple Outputs with the same (`chainId`, `recipient`, `token`) into a single Output with the summed `amount`.
/// @dev NOTE that here, Output.chainId denotes the *origin* chainId.
/// @param outputs - The Outputs to be transferred.
/// @custom:emits Filled
function fill(Output[] memory outputs) external payable {
// transfer outputs
_transferOutputs(outputs);

// emit
emit Filled(outputs);
}

/// @notice Fill any number of Order(s), by transferring their Output(s) via permit2 signed batch transfer.
/// @dev Can only provide ERC20 tokens as Outputs.
/// @dev Filler may aggregate multiple Outputs with the same (`chainId`, `recipient`, `token`) into a single Output with the summed `amount`.
/// @dev the permit2 signer is the Filler providing the Outputs.
/// @dev the permit2 `permitted` tokens MUST match provided Outputs.
/// @dev Filler MUST submit `fill` and `intitiate` within an atomic bundle.
/// @dev NOTE that here, Output.chainId denotes the *origin* chainId.
/// @param outputs - The Outputs to be transferred. signed over via permit2 witness.
/// @param permit2 - the permit2 details, signer, and signature.
/// @custom:emits Filled
function fillPermit2(Output[] memory outputs, OrdersPermit2.Permit2Batch calldata permit2) external {
// transfer all tokens to the Output recipients via permit2 (includes check on nonce & deadline)
_permitWitnessTransferFrom(
outputWitness(outputs), _fillTransferDetails(outputs, permit2.permit.permitted), permit2
);

// emit
emit Filled(outputs);
}

/// @notice Transfer the Order Outputs to their recipients.
function _transferOutputs(Output[] memory outputs) internal {
uint256 value = msg.value;
for (uint256 i; i < outputs.length; i++) {
if (outputs[i].token == address(0)) {
// this line should underflow if there's an attempt to spend more ETH than is attached to the transaction
value -= outputs[i].amount;
payable(outputs[i].recipient).transfer(outputs[i].amount);
} else {
IERC20(outputs[i].token).transferFrom(msg.sender, outputs[i].recipient, outputs[i].amount);
}
}
}
}
66 changes: 2 additions & 64 deletions src/Orders.sol → src/orders/OrderOrigin.sol
Original file line number Diff line number Diff line change
@@ -1,64 +1,10 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {OrdersPermit2, UsesPermit2} from "./permit2/UsesPermit2.sol";
import {IOrders} from "./interfaces/IOrders.sol";
import {OrdersPermit2} from "./OrdersPermit2.sol";
import {IOrders} from "./IOrders.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

/// @notice Contract capable of processing fulfillment of intent-based Orders.
abstract contract OrderDestination is IOrders, OrdersPermit2 {
/// @notice Emitted when Order Outputs are sent to their recipients.
/// @dev NOTE that here, Output.chainId denotes the *origin* chainId.
event Filled(Output[] outputs);

/// @notice Fill any number of Order(s), by transferring their Output(s).
/// @dev Filler may aggregate multiple Outputs with the same (`chainId`, `recipient`, `token`) into a single Output with the summed `amount`.
/// @dev NOTE that here, Output.chainId denotes the *origin* chainId.
/// @param outputs - The Outputs to be transferred.
/// @custom:emits Filled
function fill(Output[] memory outputs) external payable {
// transfer outputs
_transferOutputs(outputs);

// emit
emit Filled(outputs);
}

/// @notice Fill any number of Order(s), by transferring their Output(s) via permit2 signed batch transfer.
/// @dev Can only provide ERC20 tokens as Outputs.
/// @dev Filler may aggregate multiple Outputs with the same (`chainId`, `recipient`, `token`) into a single Output with the summed `amount`.
/// @dev the permit2 signer is the Filler providing the Outputs.
/// @dev the permit2 `permitted` tokens MUST match provided Outputs.
/// @dev Filler MUST submit `fill` and `intitiate` within an atomic bundle.
/// @dev NOTE that here, Output.chainId denotes the *origin* chainId.
/// @param outputs - The Outputs to be transferred. signed over via permit2 witness.
/// @param permit2 - the permit2 details, signer, and signature.
/// @custom:emits Filled
function fillPermit2(Output[] memory outputs, OrdersPermit2.Permit2Batch calldata permit2) external {
// transfer all tokens to the Output recipients via permit2 (includes check on nonce & deadline)
_permitWitnessTransferFrom(
outputWitness(outputs), _fillTransferDetails(outputs, permit2.permit.permitted), permit2
);

// emit
emit Filled(outputs);
}

/// @notice Transfer the Order Outputs to their recipients.
function _transferOutputs(Output[] memory outputs) internal {
uint256 value = msg.value;
for (uint256 i; i < outputs.length; i++) {
if (outputs[i].token == address(0)) {
// this line should underflow if there's an attempt to spend more ETH than is attached to the transaction
value -= outputs[i].amount;
payable(outputs[i].recipient).transfer(outputs[i].amount);
} else {
IERC20(outputs[i].token).transferFrom(msg.sender, outputs[i].recipient, outputs[i].amount);
}
}
}
}

/// @notice Contract capable of registering initiation of intent-based Orders.
abstract contract OrderOrigin is IOrders, OrdersPermit2 {
/// @notice Thrown when an Order is submitted with a deadline that has passed.
Expand Down Expand Up @@ -154,11 +100,3 @@ abstract contract OrderOrigin is IOrders, OrdersPermit2 {
}
}
}

contract HostOrders is OrderDestination {
constructor(address _permit2) UsesPermit2(_permit2) {}
}

contract RollupOrders is OrderOrigin, OrderDestination {
constructor(address _permit2) UsesPermit2(_permit2) {}
}
14 changes: 14 additions & 0 deletions src/orders/Orders.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {OrderDestination} from "./OrderDestination.sol";
import {OrderOrigin} from "./OrderOrigin.sol";
import {UsesPermit2} from "../UsesPermit2.sol";

contract HostOrders is OrderDestination {
constructor(address _permit2) UsesPermit2(_permit2) {}
}

contract RollupOrders is OrderOrigin, OrderDestination {
constructor(address _permit2) UsesPermit2(_permit2) {}
}
105 changes: 2 additions & 103 deletions src/permit2/UsesPermit2.sol → src/orders/OrdersPermit2.sol
Original file line number Diff line number Diff line change
@@ -1,41 +1,9 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {IOrders} from "./IOrders.sol";
import {UsesPermit2} from "../UsesPermit2.sol";
import {ISignatureTransfer} from "permit2/src/interfaces/ISignatureTransfer.sol";
import {IOrders} from "../interfaces/IOrders.sol";

abstract contract UsesPermit2 {
/// @param permit - the permit2 single token transfer details. includes a `deadline` and an unordered `nonce`.
/// @param signer - the signer of the permit2 info; the owner of the tokens.
/// @param signature - the signature over the permit + witness.
struct Permit2 {
ISignatureTransfer.PermitTransferFrom permit;
address owner;
bytes signature;
}

/// @param permit - the permit2 batch token transfer details. includes a `deadline` and an unordered `nonce`.
/// @param signer - the signer of the permit2 info; the owner of the tokens.
/// @param signature - the signature over the permit + witness.
struct Permit2Batch {
ISignatureTransfer.PermitBatchTransferFrom permit;
address owner;
bytes signature;
}

/// @notice Struct to hold the pre-hashed witness field and the witness type string.
struct Witness {
bytes32 witnessHash;
string witnessTypeString;
}

/// @notice The Permit2 contract address.
address immutable permit2Contract;

constructor(address _permit2) {
permit2Contract = _permit2;
}
}

abstract contract OrdersPermit2 is UsesPermit2 {
string constant _OUTPUT_WITNESS_TYPESTRING =
Expand Down Expand Up @@ -128,72 +96,3 @@ abstract contract OrdersPermit2 is UsesPermit2 {
}
}
}

abstract contract PassagePermit2 is UsesPermit2 {
string constant _ENTER_WITNESS_TYPESTRING =
"EnterWitness witness)EnterWitness(uint256 rollupChainId,address rollupRecipient)TokenPermissions(address token,uint256 amount)";

bytes32 constant _ENTER_WITNESS_TYPEHASH = keccak256("EnterWitness(uint256 rollupChainId,address rollupRecipient)");

string constant _EXIT_WITNESS_TYPESTRING =
"ExitWitness witness)ExitWitness(address hostRecipient)TokenPermissions(address token,uint256 amount)";

bytes32 constant _EXIT_WITNESS_TYPEHASH = keccak256("ExitWitness(address hostRecipient)");

/// @notice Struct to hash Enter witness data into a 32-byte witness field, in an EIP-712 compliant way.
struct EnterWitness {
uint256 rollupChainId;
address rollupRecipient;
}

/// @notice Struct to hash Exit witness data into a 32-byte witness field, in an EIP-712 compliant way.
struct ExitWitness {
address hostRecipient;
}

/// @notice Encode & hash the rollupChainId and rollupRecipient for use as a permit2 witness.
/// @return _witness - the hashed witness and its typestring.
function enterWitness(uint256 rollupChainId, address rollupRecipient)
public
pure
returns (Witness memory _witness)
{
_witness.witnessHash =
keccak256(abi.encode(_ENTER_WITNESS_TYPEHASH, EnterWitness(rollupChainId, rollupRecipient)));
_witness.witnessTypeString = _ENTER_WITNESS_TYPESTRING;
}

/// @notice Hash the hostRecipient for use as a permit2 witness.
/// @return _witness - the hashed witness and its typestring.
function exitWitness(address hostRecipient) public pure returns (Witness memory _witness) {
_witness.witnessHash = keccak256(abi.encode(_EXIT_WITNESS_TYPEHASH, ExitWitness(hostRecipient)));
_witness.witnessTypeString = _EXIT_WITNESS_TYPESTRING;
}

/// @notice Transfer tokens using permit2.
/// @param _witness - the hashed witness and its typestring.
/// @param permit2 - the Permit2 information.
function _permitWitnessTransferFrom(Witness memory _witness, Permit2 calldata permit2) internal {
ISignatureTransfer(permit2Contract).permitWitnessTransferFrom(
permit2.permit,
_selfTransferDetails(permit2.permit.permitted.amount),
permit2.owner,
_witness.witnessHash,
_witness.witnessTypeString,
permit2.signature
);
}

/// @notice Construct TransferDetails transferring a balance to this contract, for passing to permit2.
/// @dev always transfers the full amount to address(this).
/// @param amount - the amount to transfer to this contract.
/// @return transferDetails - the SignatureTransferDetails generated.
function _selfTransferDetails(uint256 amount)
internal
view
returns (ISignatureTransfer.SignatureTransferDetails memory transferDetails)
{
transferDetails.to = address(this);
transferDetails.requestedAmount = amount;
}
}
Loading