Skip to content

Commit

Permalink
test: setup tests
Browse files Browse the repository at this point in the history
  • Loading branch information
georgeroman committed Nov 14, 2024
1 parent e347ef6 commit 6c3e3f0
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 25 deletions.
1 change: 1 addition & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
solady/=lib/solady/src/
40 changes: 37 additions & 3 deletions src/OpReceiverProxy.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;

import {ERC20} from "solady/tokens/ERC20.sol";

import {IHyperlaneMailbox} from "./interfaces/IHyperlaneMailbox.sol";

import {Utils} from "./Utils.sol";

contract OpReceiverProxy is Utils {
contract OpReceiverProxy is ERC20, Utils {
// Errors

error Unauthorized();
Expand Down Expand Up @@ -40,8 +42,42 @@ contract OpReceiverProxy is Utils {
opSenderProxy = _opSenderProxy;
}

// ERC20 overrides

function name() public pure override returns (string memory) {
return "Fast ETH";
}

function symbol() public pure override returns (string memory) {
return "FASTETH";
}

// Public methods

function deposit(address to) external payable {
uint256 shares = 0;
if (totalSupply() == 0) {
shares = msg.value;
} else {
shares = (msg.value * totalSupply()) / address(this).balance;
}

_mint(to, shares);
}

function withdraw(uint256 shares) public {
uint256 amount = (address(this).balance * shares) / totalSupply();

_burn(msg.sender, amount);
_send(msg.sender, amount);
}

function withdrawAll() external {
withdraw(balanceOf(msg.sender));
}

// Restricted methods

function handle(
uint32 senderChainId,
bytes32 senderAddress,
Expand Down Expand Up @@ -76,8 +112,6 @@ contract OpReceiverProxy is Utils {
}
}

receive() external payable {}

fallback() external payable {
// Only `OP_GATEWAY` is authorized to call this
if (msg.sender != OP_GATEWAY) {
Expand Down
49 changes: 27 additions & 22 deletions src/OpSenderProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,10 @@ pragma solidity ^0.8.28;
import {IHyperlaneMailbox} from "./interfaces/IHyperlaneMailbox.sol";
import {IOpBridge} from "./interfaces/IOpBridge.sol";

import {Utils} from "./Utils.sol";

contract OpSenderProxy is Utils {
contract OpSenderProxy {
// Constants

IOpBridge public constant OP_BRIDGE =
IOpBridge(0x4200000000000000000000000000000000000010);

address public immutable OP_BRIDGE;
uint32 public immutable RECEIVER_CHAIN_ID;
address public immutable HYPERLANE_MAILBOX;

Expand All @@ -23,10 +19,12 @@ contract OpSenderProxy is Utils {
// Constructor

constructor(
address _opBridge,
uint32 _receiverChainId,
address _hyperlaneMailbox,
address _opReceiverProxy
) {
OP_BRIDGE = _opBridge;
RECEIVER_CHAIN_ID = _receiverChainId;
HYPERLANE_MAILBOX = _hyperlaneMailbox;

Expand All @@ -35,32 +33,39 @@ contract OpSenderProxy is Utils {

// Public methods

function withdraw(uint256 amount, address to) external payable {
function withdraw(address to) external payable {
// Associate the withdrawal to a unique id
uint256 id = nextId++;

// Get the Hyperlane fee for a cross-chain message
uint256 hyperlaneFee = IHyperlaneMailbox(HYPERLANE_MAILBOX)
.quoteDispatch(
RECEIVER_CHAIN_ID,
bytes32(uint256(uint160(opReceiverProxy))),
// Mock the data to be passed, given that we don't know the amount yet
abi.encode(id, to, 0)
);

// Get the amount left to bridge (initial amount without the Hyperlane fee)
uint256 amountLeftToBridge = msg.value - hyperlaneFee;

// Endoce the data to be passed to the receiver chain
bytes memory data = abi.encode(id, to, amount);
bytes memory data = abi.encode(id, to, amountLeftToBridge);

// Trigger a Hyperlane cross-chain message to the `opReceiverProxy` contract
IHyperlaneMailbox(HYPERLANE_MAILBOX).dispatch{value: hyperlaneFee}(
RECEIVER_CHAIN_ID,
bytes32(uint256(uint160(opReceiverProxy))),
data
);

// Trigger a canonical withdrawal to the `opReceiverProxy` contract
OP_BRIDGE.withdrawTo(
IOpBridge(OP_BRIDGE).withdrawTo{value: amountLeftToBridge}(
0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000,
opReceiverProxy,
amount,
amountLeftToBridge,
0,
data
);

// Trigger a Hyperlane cross-chain message to the `opReceiverProxy` contract
IHyperlaneMailbox(HYPERLANE_MAILBOX).dispatch{
value: IHyperlaneMailbox(HYPERLANE_MAILBOX).quoteDispatch(
RECEIVER_CHAIN_ID,
bytes32(uint256(uint160(opReceiverProxy))),
data
)
}(RECEIVER_CHAIN_ID, bytes32(uint256(uint160(opReceiverProxy))), data);

// Refund any ETH leftover back to the caller
_send(msg.sender, address(this).balance);
}
}
63 changes: 63 additions & 0 deletions test/OpReceiverProxyTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
pragma solidity ^0.8.28;

import {Test} from "forge-std/Test.sol";

import {HyperlaneMailboxMock} from "./mocks/HyperlaneMailboxMock.sol";
import {OpBridgeMock} from "./mocks/OpBridgeMock.sol";
import {OpReceiverProxy} from "../src/OpReceiverProxy.sol";
import {IHyperlaneMailbox} from "../src/interfaces/IHyperlaneMailbox.sol";
import {IOpBridge} from "../src/interfaces/IOpBridge.sol";

contract OpReceiverProxyTest is Test {
address public user;

IOpBridge public opBridge;
IHyperlaneMailbox public hyperlaneMailbox;

uint32 public senderChainId;
uint256 public feeBps;
address public opSenderProxy;

OpReceiverProxy public opReceiverProxy;

function setUp() public {
user = address(1);
vm.deal(user, 1000 ether);

opBridge = new OpBridgeMock();
hyperlaneMailbox = new HyperlaneMailboxMock();

senderChainId = 1;
feeBps = 5e15;
opSenderProxy = address(2);

opReceiverProxy = new OpReceiverProxy(
senderChainId,
address(hyperlaneMailbox),
feeBps,
opSenderProxy
);
}

function test_handle() public {
uint256 amount = 1 ether;

vm.deal(address(opReceiverProxy), 10 ether);

uint256 balanceBefore = user.balance;

vm.prank(address(hyperlaneMailbox));
opReceiverProxy.handle(
senderChainId,
bytes32(uint256(uint160(opSenderProxy))),
abi.encode(0, user, amount)
);

uint256 balanceAfter = user.balance;

assertEq(
balanceAfter - balanceBefore,
amount - (amount * opReceiverProxy.FEE_BPS()) / 1e18
);
}
}
46 changes: 46 additions & 0 deletions test/OpSenderProxyTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
pragma solidity ^0.8.28;

import {Test} from "forge-std/Test.sol";

import {HyperlaneMailboxMock} from "./mocks/HyperlaneMailboxMock.sol";
import {OpBridgeMock} from "./mocks/OpBridgeMock.sol";
import {OpSenderProxy} from "../src/OpSenderProxy.sol";
import {IHyperlaneMailbox} from "../src/interfaces/IHyperlaneMailbox.sol";
import {IOpBridge} from "../src/interfaces/IOpBridge.sol";

contract OpSenderProxyTest is Test {
address public user;

IOpBridge public opBridge;
IHyperlaneMailbox public hyperlaneMailbox;

uint32 public receiverChainId;
address public opReceiverProxy;

OpSenderProxy public opSenderProxy;

function setUp() public {
user = address(1);
vm.deal(user, 1000 ether);

opBridge = new OpBridgeMock();
hyperlaneMailbox = new HyperlaneMailboxMock();

receiverChainId = 1;
opReceiverProxy = address(2);

opSenderProxy = new OpSenderProxy(
address(opBridge),
receiverChainId,
address(hyperlaneMailbox),
opReceiverProxy
);
}

function test_withdraw() public {
uint256 amount = 1 ether;

vm.prank(user);
opSenderProxy.withdraw{value: amount}(user);
}
}
22 changes: 22 additions & 0 deletions test/mocks/HyperlaneMailboxMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;

import {IHyperlaneMailbox} from "../../src/interfaces/IHyperlaneMailbox.sol";

contract HyperlaneMailboxMock is IHyperlaneMailbox {
function dispatch(
uint32, // receiverChainId
bytes32, // receiverAddress
bytes calldata // data
) external payable returns (bytes32 id) {
return bytes32(block.number);
}

function quoteDispatch(
uint32, // receiverChainId
bytes32, // receiverAddress
bytes calldata // data
) external pure returns (uint256 fee) {
return 0.001 ether;
}
}
16 changes: 16 additions & 0 deletions test/mocks/OpBridgeMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;

import {IOpBridge} from "../../src/interfaces/IOpBridge.sol";

contract OpBridgeMock is IOpBridge {
function withdrawTo(
address, // l2Token
address, // to
uint256, // amount
uint32, // minGasLimit
bytes calldata // extraData
) external payable {
// Do nothing
}
}

0 comments on commit 6c3e3f0

Please sign in to comment.