Skip to content

Commit

Permalink
Merge pull request #4 from BootNodeDev/inbound-transfer-tests
Browse files Browse the repository at this point in the history
Inbound transfer tests
  • Loading branch information
tmsrjs authored May 7, 2024
2 parents 836e8fe + 949dd6e commit 2777af8
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 81 deletions.
45 changes: 45 additions & 0 deletions test/L1XERC20BaseGatewayTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.25 <0.9.0;

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

import { XERC20 } from "xerc20/contracts/XERC20.sol";

import { L1XERC20Adapter } from "src/L1XERC20Adapter.sol";
import { L1XERC20Gateway } from "src/L1XERC20Gateway.sol";

abstract contract L1XERC20BaseGatewayTest is Test {
XERC20 internal xerc20;
L1XERC20Adapter internal adapter;
L1XERC20Gateway internal l1Gateway;
address internal l1GatewayRouter;

address internal l1Inbox;

address internal _owner = makeAddr("owner");
address internal _user = makeAddr("user");
address internal _dest = makeAddr("dest");

uint256 internal amountToBridge = 25;

function _setUp() internal {
assert(l1GatewayRouter != address(0));
vm.label(l1GatewayRouter, "l1GatewayRouter");
assert(l1Inbox != address(0));
vm.label(l1Inbox, "l1Inbox");

l1Gateway = new L1XERC20Gateway(l1GatewayRouter, l1Inbox);

xerc20 = new XERC20("NonArbitrumEnabled", "NON", _owner);
vm.prank(_owner);
xerc20.setLimits(address(l1Gateway), 420 ether, 69 ether);

vm.prank(_owner);
adapter = new L1XERC20Adapter(address(xerc20), address(l1Gateway));
}

////
// Event declarations for assertions
////
event Transfer(address indexed from, address indexed to, uint256 value);
}
53 changes: 40 additions & 13 deletions test/L1XERC20Gateway.t.sol
Original file line number Diff line number Diff line change
@@ -1,29 +1,56 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.25 <0.9.0;

import { Test } from "forge-std/Test.sol";
// solhint-disable-next-line
import { console2 } from "forge-std/console2.sol";

import { XERC20 } from "xerc20/contracts/XERC20.sol";
import { IInbox } from "@arbitrum/nitro-contracts/src/bridge/IInbox.sol";
import { InboxMock } from "@arbitrum/tokenbridge/test/InboxMock.sol";

import { L1XERC20Adapter } from "src/L1XERC20Adapter.sol";
import { L1XERC20Gateway } from "src/L1XERC20Gateway.sol";

contract L1XERC20GatewayTest is Test {
XERC20 internal xerc20;
L1XERC20Adapter internal adapter;
L1XERC20Gateway internal gateway;

address internal _owner = makeAddr("owner");
import { L1XERC20BaseGatewayTest } from "test/L1XERC20BaseGatewayTest.t.sol";

contract L1XERC20GatewayTest is L1XERC20BaseGatewayTest {
function setUp() public {
xerc20 = new XERC20("NonArbitrumEnabled", "NON", _owner);
gateway = new L1XERC20Gateway(makeAddr("router"), makeAddr("inbox"));
adapter = new L1XERC20Adapter(address(xerc20), address(gateway));
l1GatewayRouter = makeAddr("l1GatewayRouter");
l1Inbox = address(new InboxMock());

_setUp();
}

function test_AddressIsAdapter() public view {
assertEq(gateway.addressIsAdapter(address(xerc20)), false);
assertEq(gateway.addressIsAdapter(address(adapter)), true);
assertEq(l1Gateway.addressIsAdapter(address(xerc20)), false);
assertEq(l1Gateway.addressIsAdapter(address(adapter)), true);
}

function test_FinalizeInboundTransfer() public {
InboxMock(address(l1Inbox)).setL2ToL1Sender(l1Gateway.counterpartGateway());

uint256 exitNum = 7;
bytes memory callHookData = "";
bytes memory data = abi.encode(exitNum, callHookData);

vm.expectEmit(true, true, true, true, address(xerc20));
emit Transfer(address(0), _dest, amountToBridge);

vm.expectEmit(true, true, true, true, address(l1Gateway));
emit WithdrawalFinalized(address(adapter), _user, _dest, exitNum, amountToBridge);

uint256 balanceBefore = xerc20.balanceOf(_dest);

vm.prank(address(IInbox(l1Gateway.inbox()).bridge()));
l1Gateway.finalizeInboundTransfer(address(adapter), _user, _dest, amountToBridge, data);

assertEq(adapter.balanceOf(_dest), xerc20.balanceOf(_dest));
assertEq(xerc20.balanceOf(_dest), balanceBefore + amountToBridge);
}

////
// Event declarations for assertions
////
event WithdrawalFinalized(
address l1Token, address indexed _from, address indexed _to, uint256 indexed _exitNum, uint256 _amount
);
}
59 changes: 37 additions & 22 deletions test/forking/L2XERC20Gateway.t.sol → test/L2XERC20Gateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ import { L2XERC20Adapter } from "src/L2XERC20Adapter.sol";
import { L2XERC20Gateway } from "src/L2XERC20Gateway.sol";

contract L2XERC20GatewayTest is Test {
uint256 internal arbitrumFork;

ArbSysMock public arbSysMock = new ArbSysMock();

XERC20 internal xerc20;
L2XERC20Adapter internal adapter;

address internal _owner = makeAddr("owner");
address internal _minter = makeAddr("minter");
address internal _user = makeAddr("user");
address internal _dest = makeAddr("dest");

uint256 internal amountToBridge = 25;

address internal l2GatewayRouter = 0x5288c571Fd7aD117beA99bF60FE0846C4E84F933;
L2XERC20Gateway internal l2Gateway;
Expand All @@ -35,8 +35,6 @@ contract L2XERC20GatewayTest is Test {
function setUp() public {
vm.label(l2GatewayRouter, "l2GatewayRouter");

arbitrumFork = vm.createFork("arbitrum", 202_675_145);

l2Gateway = new L2XERC20Gateway(l1Counterpart, l2GatewayRouter);

xerc20 = new XERC20("NonArbitrumEnabled", "NON", _owner);
Expand All @@ -45,43 +43,59 @@ contract L2XERC20GatewayTest is Test {
adapter = new L2XERC20Adapter(address(xerc20), address(l2Gateway), l1Token);

vm.prank(_owner);
xerc20.setLimits(_minter, 42 ether, 0);
xerc20.setLimits(address(l2Gateway), 420 ether, 69 ether);

vm.prank(_minter);
xerc20.mint(_user, 10 ether);
_registerToken();
}

function test_OutboundTransfer() public {
_registerToken();

vm.prank(_owner);
xerc20.setLimits(address(l2Gateway), 0, 42 ether);
deal(address(xerc20), _user, 10 ether);

vm.prank(_user);
xerc20.approve(address(l2Gateway), 1 ether);

address dest = makeAddr("dest");
xerc20.approve(address(l2Gateway), amountToBridge);

vm.expectEmit(true, true, true, true, address(xerc20));
emit Transfer(_user, address(0), 1 ether);
emit Transfer(_user, address(0), amountToBridge);

// withdrawal params
bytes memory data = new bytes(0);
bytes memory data = "";

uint256 expectedId = 0;
bytes memory expectedData = l2Gateway.getOutboundCalldata(l1Token, _user, dest, 1 ether, data);
vm.expectEmit(true, true, true, true);
bytes memory expectedData = l2Gateway.getOutboundCalldata(l1Token, _user, _dest, amountToBridge, data);
vm.expectEmit(true, true, true, true, address(l2Gateway));
emit TxToL1(_user, l1Counterpart, expectedId, expectedData);

vm.expectEmit(true, true, true, true, address(l2Gateway));
emit WithdrawalInitiated(l1Token, _user, dest, expectedId, 0, 1 ether);
emit WithdrawalInitiated(l1Token, _user, _dest, expectedId, 0, amountToBridge);

uint256 balanceBefore = xerc20.balanceOf(_user);

vm.etch(0x0000000000000000000000000000000000000064, address(arbSysMock).code);
vm.prank(_user);
l2Gateway.outboundTransfer(l1Token, dest, 1 ether, 0, 0, bytes(""));
l2Gateway.outboundTransfer(l1Token, _dest, amountToBridge, 0, 0, data);

assertEq(adapter.balanceOf(_user), xerc20.balanceOf(_user));
assertEq(xerc20.balanceOf(_user), 9 ether);
assertEq(xerc20.balanceOf(_user), balanceBefore - amountToBridge);
}

function test_FinalizeInboundTransfer() public {
bytes memory gatewayData = "";
bytes memory callHookData = "";
bytes memory data = abi.encode(gatewayData, callHookData);

vm.expectEmit(true, true, true, true, address(xerc20));
emit Transfer(address(0), _dest, amountToBridge);

vm.expectEmit(true, true, true, true, address(l2Gateway));
emit DepositFinalized(l1Token, _user, _dest, amountToBridge);

uint256 balanceBefore = xerc20.balanceOf(_dest);

vm.prank(AddressAliasHelper.applyL1ToL2Alias(l1Counterpart));
l2Gateway.finalizeInboundTransfer(l1Token, _user, _dest, amountToBridge, data);

assertEq(adapter.balanceOf(_dest), xerc20.balanceOf(_dest));
assertEq(xerc20.balanceOf(_dest), balanceBefore + amountToBridge);
}

////
Expand Down Expand Up @@ -112,5 +126,6 @@ contract L2XERC20GatewayTest is Test {
uint256 _exitNum,
uint256 _amount
);
event DepositFinalized(address indexed l1Token, address indexed _from, address indexed _receiver, uint256 _amount);
event TxToL1(address indexed _from, address indexed _receiver, uint256 indexed _id, bytes _data);
}
64 changes: 18 additions & 46 deletions test/forking/L1XERC20Gateway.t.sol
Original file line number Diff line number Diff line change
@@ -1,32 +1,16 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.25 <0.9.0;

import { Test } from "forge-std/Test.sol";
// solhint-disable-next-line
import { console2 } from "forge-std/console2.sol";

import { L1GatewayRouter } from "@arbitrum/tokenbridge/ethereum/gateway/L1GatewayRouter.sol";

import { XERC20 } from "xerc20/contracts/XERC20.sol";
import { L1XERC20BaseGatewayTest } from "test/L1XERC20BaseGatewayTest.t.sol";

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

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

contract L1XERC20GatewayTest is Test {
contract L1XERC20GatewayTest is L1XERC20BaseGatewayTest {
uint256 internal mainnetFork;

XERC20 internal xerc20;
L1XERC20Adapter internal adapter;

address internal _owner = makeAddr("owner");
address internal _minter = makeAddr("minter");
address internal _user = makeAddr("user");

address internal l1GatewayRouter = 0x72Ce9c846789fdB6fC1f34aC4AD25Dd9ef7031ef;
address internal l1Inbox = 0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f;
L1XERC20Gateway internal l1Gateway;

address internal l2TokenAddress = makeAddr("l2TokenAddress");

uint256 public maxSubmissionCost = 20;
Expand All @@ -36,28 +20,21 @@ contract L1XERC20GatewayTest is Test {
uint256 public retryableCost = maxSubmissionCost + nativeTokenTotalFee;

function setUp() public {
vm.label(l1GatewayRouter, "l1GatewayRouter");

mainnetFork = vm.createSelectFork("mainnet", 19_690_420);

l1Gateway = new L1XERC20Gateway(l1GatewayRouter, l1Inbox);

xerc20 = new XERC20("NonArbitrumEnabled", "NON", _owner);
vm.deal(_owner, 100 ether);

vm.prank(_owner);
adapter = new L1XERC20Adapter(address(xerc20), address(l1Gateway));

vm.prank(_owner);
xerc20.setLimits(_minter, 42 ether, 0);

// WARNING: tests will only pass when setting block.basefee to 0
// or when running with --gas-report, which makes it seem like there's
// a bug in forge when using this flag.
// This is safe since block.basefee is only used by the Inbox to check
// if enough funds were submitted in order to send/redeem the
// cross-chain message.
vm.fee(0);

l1GatewayRouter = 0x72Ce9c846789fdB6fC1f34aC4AD25Dd9ef7031ef;
l1Inbox = 0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f;

_setUp();

deal(_owner, 100 ether);
}

function test_RegisterTokenOnL2() public {
Expand Down Expand Up @@ -93,38 +70,33 @@ contract L1XERC20GatewayTest is Test {
makeAddr("creditBackAddr")
);

vm.prank(_minter);
xerc20.mint(_user, 10 ether);

vm.prank(_owner);
xerc20.setLimits(address(l1Gateway), 0, 42 ether);

deal(address(xerc20), _user, 10 ether);
vm.prank(_user);
xerc20.approve(address(l1Gateway), 1 ether);
xerc20.approve(address(l1Gateway), amountToBridge);

address dest = makeAddr("dest");
L1GatewayRouter router = L1GatewayRouter(l1GatewayRouter);

vm.expectEmit(true, true, true, true, address(xerc20));
emit Transfer(_user, address(0), 1 ether);
emit Transfer(_user, address(0), amountToBridge);

vm.expectEmit(true, true, true, true, address(l1Gateway));
emit DepositInitiated(address(adapter), _user, dest, 1_487_345, 1 ether);
emit DepositInitiated(address(adapter), _user, _dest, 1_487_345, amountToBridge);

uint256 balanceBefore = xerc20.balanceOf(_user);

vm.deal(_user, 10 ether);
deal(_user, 10 ether);
vm.prank(_user);
router.outboundTransferCustomRefund{ value: 3 ether }(
address(adapter), dest, dest, 1 ether, maxGas, gasPriceBid, abi.encode(maxSubmissionCost, "")
address(adapter), _dest, _dest, amountToBridge, maxGas, gasPriceBid, abi.encode(maxSubmissionCost, "")
);

assertEq(adapter.balanceOf(_user), xerc20.balanceOf(_user));
assertEq(xerc20.balanceOf(_user), 9 ether);
assertEq(xerc20.balanceOf(_user), balanceBefore - amountToBridge);
}

////
// Event declarations for assertions
////
event Transfer(address indexed from, address indexed to, uint256 value);
event DepositInitiated(
address l1Token, address indexed _from, address indexed _to, uint256 indexed _sequenceNumber, uint256 _amount
);
Expand Down

0 comments on commit 2777af8

Please sign in to comment.