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
58 changes: 45 additions & 13 deletions solidity/script/DeployHyperlane7683.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { ProxyAdmin } from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin

import { Hyperlane7683 } from "../src/Hyperlane7683.sol";

import { ICreateX } from "./utils/ICreateX.sol";

contract OwnableProxyAdmin is ProxyAdmin {
constructor(address _owner) {
_transferOwnership(_owner);
Expand All @@ -22,26 +24,16 @@ contract DeployHyperlane7683 is Script {
function run() public {
uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PK");

string memory ROUTER_SALT = vm.envString("HYPERLANE7683_SALT");
address mailbox = vm.envAddress("MAILBOX");
address permit2 = vm.envAddress("PERMIT2");
address proxyAdminOwner = vm.envOr("PROXY_ADMIN_OWNER", address(0));
address owner = vm.envAddress("ROUTER_OWNER");
uint256[] memory domains = vm.envUint("DOMAINS", ",");
uint32[] memory _domains = new uint32[](domains.length);
bytes32[] memory routers = new bytes32[](domains.length);
GasRouter.GasRouterConfig[] memory gasConfigs = new GasRouter.GasRouterConfig[](domains.length);

vm.startBroadcast(deployerPrivateKey);

ProxyAdmin proxyAdmin = new OwnableProxyAdmin{salt: keccak256(abi.encode(ROUTER_SALT))}(proxyAdminOwner);

address routerImpl = address(new Hyperlane7683{salt: keccak256(abi.encode(ROUTER_SALT))}(mailbox, permit2));
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy{salt: keccak256(abi.encode(ROUTER_SALT))}(
routerImpl,
address(proxyAdmin),
abi.encodeWithSelector(Hyperlane7683.initialize.selector, address(0), address(0), owner)
);
ProxyAdmin proxyAdmin = deployProxyAdmin();
address routerImpl = deployImplementation();
TransparentUpgradeableProxy proxy = deployProxy(routerImpl, address(proxyAdmin));

for (uint i = 0; i < domains.length; i++) {
routers[i] = TypeCasts.addressToBytes32(address(proxy));
Expand All @@ -61,4 +53,44 @@ contract DeployHyperlane7683 is Script {
console2.log("Implementation:", routerImpl);
console2.log("ProxyAdmin:", address(proxyAdmin));
}

function deployProxyAdmin() internal returns (ProxyAdmin proxyAdmin) {
string memory ROUTER_SALT = vm.envString("HYPERLANE7683_SALT");
address proxyAdminOwner = vm.envOr("PROXY_ADMIN_OWNER", address(0));
proxyAdmin = new OwnableProxyAdmin{salt: keccak256(abi.encode(ROUTER_SALT))}(proxyAdminOwner);
}

function deployImplementation() internal returns (address routerImpl) {
uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PK");
address createX = vm.envAddress("CREATEX_ADDRESS");
string memory ROUTER_SALT = vm.envString("HYPERLANE7683_SALT");
address mailbox = vm.envAddress("MAILBOX");
address permit2 = vm.envAddress("PERMIT2");
bytes32 salt = keccak256(abi.encodePacked("impl",ROUTER_SALT, vm.addr(deployerPrivateKey)));

bytes memory routerCreation = type(Hyperlane7683).creationCode;
bytes memory routerBytecode = abi.encodePacked(routerCreation, abi.encode(mailbox, permit2));

routerImpl = ICreateX(createX).deployCreate3(salt, routerBytecode);
}

function deployProxy(address routerImpl, address proxyAdmin) internal returns (TransparentUpgradeableProxy proxy) {
uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PK");
address createX = vm.envAddress("CREATEX_ADDRESS");
string memory ROUTER_SALT = vm.envString("HYPERLANE7683_SALT");
address owner = vm.envAddress("ROUTER_OWNER");

bytes32 salt = keccak256(abi.encodePacked("proxy", ROUTER_SALT, vm.addr(deployerPrivateKey)));

bytes memory proxyCreation = type(TransparentUpgradeableProxy).creationCode;
bytes memory proxyBytecode = abi.encodePacked(proxyCreation, abi.encode(
routerImpl,
proxyAdmin,
abi.encodeWithSelector(Hyperlane7683.initialize.selector, address(0), address(0), owner)
));

proxy = TransparentUpgradeableProxy(
payable(ICreateX(createX).deployCreate3(salt, proxyBytecode))
);
}
}
195 changes: 195 additions & 0 deletions solidity/script/utils/ICreateX.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.25;

/**
* @title CreateX Factory Interface Definition
* @author pcaversaccio (https://web.archive.org/web/20230921103111/https://pcaversaccio.com/)
* @custom:coauthor Matt Solomon (https://web.archive.org/web/20230921103335/https://mattsolomon.dev/)
*/
interface ICreateX {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* TYPES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

struct Values {
uint256 constructorAmount;
uint256 initCallAmount;
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

event ContractCreation(address indexed newContract, bytes32 indexed salt);
event ContractCreation(address indexed newContract);
event Create3ProxyContractCreation(address indexed newContract, bytes32 indexed salt);

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

error FailedContractCreation(address emitter);
error FailedContractInitialisation(address emitter, bytes revertData);
error InvalidSalt(address emitter);
error InvalidNonceValue(address emitter);
error FailedEtherTransfer(address emitter, bytes revertData);

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CREATE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

function deployCreate(bytes memory initCode) external payable returns (address newContract);

function deployCreateAndInit(
bytes memory initCode,
bytes memory data,
Values memory values,
address refundAddress
)
external
payable
returns (address newContract);

function deployCreateAndInit(
bytes memory initCode,
bytes memory data,
Values memory values
)
external
payable
returns (address newContract);

function deployCreateClone(address implementation, bytes memory data) external payable returns (address proxy);

function computeCreateAddress(address deployer, uint256 nonce) external view returns (address computedAddress);

function computeCreateAddress(uint256 nonce) external view returns (address computedAddress);

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CREATE2 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

function deployCreate2(bytes32 salt, bytes memory initCode) external payable returns (address newContract);

function deployCreate2(bytes memory initCode) external payable returns (address newContract);

function deployCreate2AndInit(
bytes32 salt,
bytes memory initCode,
bytes memory data,
Values memory values,
address refundAddress
)
external
payable
returns (address newContract);

function deployCreate2AndInit(
bytes32 salt,
bytes memory initCode,
bytes memory data,
Values memory values
)
external
payable
returns (address newContract);

function deployCreate2AndInit(
bytes memory initCode,
bytes memory data,
Values memory values,
address refundAddress
)
external
payable
returns (address newContract);

function deployCreate2AndInit(
bytes memory initCode,
bytes memory data,
Values memory values
)
external
payable
returns (address newContract);

function deployCreate2Clone(
bytes32 salt,
address implementation,
bytes memory data
)
external
payable
returns (address proxy);

function deployCreate2Clone(address implementation, bytes memory data) external payable returns (address proxy);

function computeCreate2Address(
bytes32 salt,
bytes32 initCodeHash,
address deployer
)
external
pure
returns (address computedAddress);

function computeCreate2Address(
bytes32 salt,
bytes32 initCodeHash
)
external
view
returns (address computedAddress);

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CREATE3 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

function deployCreate3(bytes32 salt, bytes memory initCode) external payable returns (address newContract);

function deployCreate3(bytes memory initCode) external payable returns (address newContract);

function deployCreate3AndInit(
bytes32 salt,
bytes memory initCode,
bytes memory data,
Values memory values,
address refundAddress
)
external
payable
returns (address newContract);

function deployCreate3AndInit(
bytes32 salt,
bytes memory initCode,
bytes memory data,
Values memory values
)
external
payable
returns (address newContract);

function deployCreate3AndInit(
bytes memory initCode,
bytes memory data,
Values memory values,
address refundAddress
)
external
payable
returns (address newContract);

function deployCreate3AndInit(
bytes memory initCode,
bytes memory data,
Values memory values
)
external
payable
returns (address newContract);

function computeCreate3Address(bytes32 salt, address deployer) external pure returns (address computedAddress);

function computeCreate3Address(bytes32 salt) external view returns (address computedAddress);
}
47 changes: 33 additions & 14 deletions solidity/src/Base7683.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity 0.8.25;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { TypeCasts } from "@hyperlane-xyz/libs/TypeCasts.sol";
import { IPermit2, ISignatureTransfer } from "@uniswap/permit2/src/interfaces/IPermit2.sol";

Expand Down Expand Up @@ -76,6 +77,7 @@ abstract contract Base7683 is IOriginSettler, IDestinationSettler {
error OrderFillNotExpired();
error InvalidDomain();
error InvalidSender();
error InvalidAmount();

// ============ Constructor ============

Expand Down Expand Up @@ -120,7 +122,7 @@ abstract contract Base7683 is IOriginSettler, IDestinationSettler {
/// @dev To be called by the user
/// @dev This method must emit the Open event
/// @param order The OnchainCrossChainOrder definition
function open(OnchainCrossChainOrder calldata order) external {
function open(OnchainCrossChainOrder calldata order) external payable {
(ResolvedCrossChainOrder memory resolvedOrder, OrderData memory orderData) = _resolvedOrder(
order.orderDataType,
msg.sender,
Expand All @@ -135,9 +137,13 @@ abstract contract Base7683 is IOriginSettler, IDestinationSettler {
orderStatus[orderId] = OrderStatus.OPENED;
senderNonce[msg.sender] += 1;

IERC20(TypeCasts.bytes32ToAddress(orderData.inputToken)).safeTransferFrom(
msg.sender, address(this), orderData.amountIn
);
if (orderData.inputToken != TypeCasts.addressToBytes32(address(0))) {
IERC20(TypeCasts.bytes32ToAddress(orderData.inputToken)).safeTransferFrom(
msg.sender, address(this), orderData.amountIn
);
} else {
if (msg.value != orderData.amountIn) revert InvalidAmount();
}

emit Open(orderId, resolvedOrder);
}
Expand Down Expand Up @@ -182,7 +188,7 @@ abstract contract Base7683 is IOriginSettler, IDestinationSettler {
/// @param _originData Data emitted on the origin to parameterize the fill
/// @param _fillerData Data provided by the filler to inform the fill or express their preferences. It should
/// contain the bytes32 encoded address of the receiver which is the used at settlement time
function fill(bytes32 _orderId, bytes calldata _originData, bytes calldata _fillerData) external virtual {
function fill(bytes32 _orderId, bytes calldata _originData, bytes calldata _fillerData) external payable virtual {
OrderData memory orderData = OrderEncoder.decode(_originData);

if (_orderId != _getOrderId(orderData)) revert InvalidOrderId();
Expand All @@ -197,9 +203,14 @@ abstract contract Base7683 is IOriginSettler, IDestinationSettler {

emit Filled(_orderId, _originData, _fillerData);

IERC20(TypeCasts.bytes32ToAddress(orderData.outputToken)).safeTransferFrom(
msg.sender, TypeCasts.bytes32ToAddress(orderData.recipient), orderData.amountOut
);
if (orderData.outputToken != TypeCasts.addressToBytes32(address(0))) {
IERC20(TypeCasts.bytes32ToAddress(orderData.outputToken)).safeTransferFrom(
msg.sender, TypeCasts.bytes32ToAddress(orderData.recipient), orderData.amountOut
);
} else {
if (msg.value != orderData.amountOut) revert InvalidAmount();
Address.sendValue(payable(TypeCasts.bytes32ToAddress(orderData.recipient)), orderData.amountOut);
}
}

function settle(bytes32[] calldata _orderIds) external payable {
Expand Down Expand Up @@ -365,9 +376,13 @@ abstract contract Base7683 is IOriginSettler, IDestinationSettler {

emit Settled(_orderId, receiver);

IERC20(TypeCasts.bytes32ToAddress(orderData.inputToken)).safeTransfer(
receiver, orderData.amountIn
);
if (orderData.inputToken != TypeCasts.addressToBytes32(address(0))) {
IERC20(TypeCasts.bytes32ToAddress(orderData.inputToken)).safeTransfer(
receiver, orderData.amountIn
);
} else {
Address.sendValue(payable(receiver), orderData.amountIn);
}
}

/**
Expand All @@ -386,9 +401,13 @@ abstract contract Base7683 is IOriginSettler, IDestinationSettler {

emit Refunded(_orderId, orderSender);

IERC20(TypeCasts.bytes32ToAddress(orderData.inputToken)).safeTransfer(
orderSender, orderData.amountIn
);
if (orderData.inputToken != TypeCasts.addressToBytes32(address(0))) {
IERC20(TypeCasts.bytes32ToAddress(orderData.inputToken)).safeTransfer(
orderSender, orderData.amountIn
);
} else {
Address.sendValue(payable(orderSender), orderData.amountIn);
}
}

/**
Expand Down
4 changes: 2 additions & 2 deletions solidity/src/ERC7683/IERC7683.sol
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ interface IOriginSettler {
/// @dev To be called by the user
/// @dev This method must emit the Open event
/// @param order The OnchainCrossChainOrder definition
function open(OnchainCrossChainOrder calldata order) external;
function open(OnchainCrossChainOrder calldata order) external payable;

/// @notice Resolves a specific GaslessCrossChainOrder into a generic ResolvedCrossChainOrder
/// @dev Intended to improve standardized integration of various order types and settlement contracts
Expand Down Expand Up @@ -150,5 +150,5 @@ interface IDestinationSettler {
/// @param orderId Unique order identifier for this order
/// @param originData Data emitted on the origin to parameterize the fill
/// @param fillerData Data provided by the filler to inform the fill or express their preferences
function fill(bytes32 orderId, bytes calldata originData, bytes calldata fillerData) external;
function fill(bytes32 orderId, bytes calldata originData, bytes calldata fillerData) external payable;
}
Loading