Skip to content

Commit

Permalink
refactor: reorganize contracts (#68)
Browse files Browse the repository at this point in the history
  • Loading branch information
anna-carroll authored Jul 20, 2024
1 parent 3cbe2fe commit 37f386a
Show file tree
Hide file tree
Showing 18 changed files with 302 additions and 263 deletions.
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

0 comments on commit 37f386a

Please sign in to comment.