Skip to content

Commit

Permalink
Merge pull request #5 from BootNodeDev/arbitrum-compatible-xerc20-tests
Browse files Browse the repository at this point in the history
Arbitrum compatible xerc20 tests
  • Loading branch information
tmsrjs authored May 9, 2024
2 parents 2777af8 + c27b750 commit 4094826
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 22 deletions.
Binary file modified bun.lockb
Binary file not shown.
76 changes: 76 additions & 0 deletions src/L1ArbitrumEnabledXERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.25 <0.9.0;

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

interface IL1CustomGateway {
function registerTokenToL2(
address _l2Address,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
address _creditBackAddress
)
external
payable
returns (uint256);

function router() external returns (address);
}

interface IL1GatewayRouter {
function setGateway(
address _gateway,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
address _creditBackAddress
)
external
payable
returns (uint256);
}

contract L1ArbitrumEnabledXERC20 is XERC20 {
address internal gatewayAddress;

constructor(
string memory _name,
string memory _symbol,
address _owner,
address _gatewayAddress
)
XERC20(_name, _symbol, _owner)
{
gatewayAddress = _gatewayAddress;
}

function isArbitrumEnabled() external pure returns (uint8) {
return uint8(0xb1);
}

function registerTokenOnL2(
address l2TokenAddress,
uint256 maxSubmissionCostForGateway,
uint256 maxSubmissionCostForRouter,
uint256 maxGasForGateway,
uint256 maxGasForRouter,
uint256 gasPriceBid,
uint256 valueForGateway,
uint256 valueForRouter,
address creditBackAddress
)
public
payable
onlyOwner
{
IL1CustomGateway gateway = IL1CustomGateway(gatewayAddress);

gateway.registerTokenToL2{ value: valueForGateway }(
l2TokenAddress, maxGasForGateway, gasPriceBid, maxSubmissionCostForGateway, creditBackAddress
);
IL1GatewayRouter(gateway.router()).setGateway{ value: valueForRouter }(
gatewayAddress, maxGasForRouter, gasPriceBid, maxSubmissionCostForRouter, creditBackAddress
);
}
}
19 changes: 19 additions & 0 deletions src/L2ArbitrumEnabledXERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.25 <0.9.0;

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

contract L2ArbitrumEnabledXERC20 is XERC20 {
address public l1Address;

constructor(
string memory _name,
string memory _symbol,
address _owner,
address _l1Address
)
XERC20(_name, _symbol, _owner)
{
l1Address = _l1Address;
}
}
11 changes: 9 additions & 2 deletions test/L1XERC20BaseGatewayTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,29 @@ abstract contract L1XERC20BaseGatewayTest is Test {

uint256 internal amountToBridge = 25;

function _setUp() internal {
function setUp() public virtual {
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);
_createXERC20();
vm.prank(_owner);
xerc20.setLimits(address(l1Gateway), 420 ether, 69 ether);

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

////
// Helpers
////
function _createXERC20() internal virtual {
xerc20 = new XERC20("NonArbitrumEnabled", "NON", _owner);
}

////
// Event declarations for assertions
////
Expand Down
5 changes: 2 additions & 3 deletions test/L1XERC20Gateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import { L1XERC20Gateway } from "src/L1XERC20Gateway.sol";
import { L1XERC20BaseGatewayTest } from "test/L1XERC20BaseGatewayTest.t.sol";

contract L1XERC20GatewayTest is L1XERC20BaseGatewayTest {
function setUp() public {
function setUp() public override {
l1GatewayRouter = makeAddr("l1GatewayRouter");
l1Inbox = address(new InboxMock());

_setUp();
super.setUp();
}

function test_AddressIsAdapter() public view {
Expand All @@ -43,7 +43,6 @@ contract L1XERC20GatewayTest is L1XERC20BaseGatewayTest {
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);
}

Expand Down
23 changes: 17 additions & 6 deletions test/L2XERC20Gateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,18 @@ contract L2XERC20GatewayTest is Test {
address internal l1Counterpart = makeAddr("l1Counterpart");
address internal l1Token = makeAddr("l1Token");

function setUp() public {
address internal bridgeable;

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

l2Gateway = new L2XERC20Gateway(l1Counterpart, l2GatewayRouter);

xerc20 = new XERC20("NonArbitrumEnabled", "NON", _owner);
_createXERC20();

vm.prank(_owner);
adapter = new L2XERC20Adapter(address(xerc20), address(l2Gateway), l1Token);
_setBridgeable();

vm.prank(_owner);
xerc20.setLimits(address(l2Gateway), 420 ether, 69 ether);
Expand Down Expand Up @@ -74,7 +77,6 @@ contract L2XERC20GatewayTest is Test {
vm.prank(_user);
l2Gateway.outboundTransfer(l1Token, _dest, amountToBridge, 0, 0, data);

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

Expand All @@ -94,19 +96,28 @@ contract L2XERC20GatewayTest is Test {
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);
}

////
// Internal helper functions (shamelessly stolen from @arbitrum)
// Helpers
////

function _createXERC20() internal virtual {
xerc20 = new XERC20("NonArbitrumEnabled", "NON", _owner);
}

function _setBridgeable() internal virtual {
bridgeable = address(adapter);
}

//// shamelessly stolen from @arbitrum
function _registerToken() internal virtual returns (address) {
address[] memory l1Tokens = new address[](1);
l1Tokens[0] = l1Token;

address[] memory l2Tokens = new address[](1);
l2Tokens[0] = address(adapter);
l2Tokens[0] = bridgeable;

vm.prank(AddressAliasHelper.applyL1ToL2Alias(l1Counterpart));
l2Gateway.registerTokenFromL1(l1Tokens, l2Tokens);
Expand Down
24 changes: 24 additions & 0 deletions test/L2XERC20GatewayArbitrumCompatibleToken.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.25 <0.9.0;

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

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

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

import { L2XERC20GatewayTest } from "test/L2XERC20Gateway.t.sol";

contract L2XERC20GatewayArbitrumCompatibleTokenTest is L2XERC20GatewayTest {
L2ArbitrumEnabledXERC20 internal arbEnabledToken;

function _createXERC20() internal override {
arbEnabledToken = new L2ArbitrumEnabledXERC20("ArbitrumEnabledToken", "AET", _owner, l1Token);
xerc20 = XERC20(address(arbEnabledToken));
}

function _setBridgeable() internal override {
bridgeable = address(xerc20);
}
}
25 changes: 14 additions & 11 deletions test/forking/L1XERC20Gateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ pragma solidity >=0.8.25 <0.9.0;
import { console2 } from "forge-std/console2.sol";

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

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

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

address internal l2TokenAddress = makeAddr("l2TokenAddress");
Expand All @@ -19,7 +20,9 @@ contract L1XERC20GatewayTest is L1XERC20BaseGatewayTest {
uint256 public nativeTokenTotalFee = gasPriceBid * maxGas;
uint256 public retryableCost = maxSubmissionCost + nativeTokenTotalFee;

function setUp() public {
address internal bridgeable;

function setUp() public virtual override {
mainnetFork = vm.createSelectFork("mainnet", 19_690_420);
// WARNING: tests will only pass when setting block.basefee to 0
// or when running with --gas-report, which makes it seem like there's
Expand All @@ -32,14 +35,15 @@ contract L1XERC20GatewayTest is L1XERC20BaseGatewayTest {
l1GatewayRouter = 0x72Ce9c846789fdB6fC1f34aC4AD25Dd9ef7031ef;
l1Inbox = 0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f;

_setUp();

deal(_owner, 100 ether);

super.setUp();
bridgeable = address(adapter);
}

function test_RegisterTokenOnL2() public {
vm.prank(_owner);
adapter.registerTokenOnL2{ value: 3 ether }(
ICustomToken(bridgeable).registerTokenOnL2{ value: 3 ether }(
l2TokenAddress,
maxSubmissionCost,
maxSubmissionCost,
Expand All @@ -52,13 +56,13 @@ contract L1XERC20GatewayTest is L1XERC20BaseGatewayTest {
);

L1GatewayRouter router = L1GatewayRouter(l1GatewayRouter);
assertEq(router.getGateway(address(adapter)), address(l1Gateway));
assertEq(router.calculateL2TokenAddress(address(adapter)), l2TokenAddress);
assertEq(router.getGateway(bridgeable), address(l1Gateway));
assertEq(router.calculateL2TokenAddress(bridgeable), l2TokenAddress);
}

function test_OutboundTransferCustomRefund() public {
vm.prank(_owner);
adapter.registerTokenOnL2{ value: 3 ether }(
ICustomToken(bridgeable).registerTokenOnL2{ value: 3 ether }(
l2TokenAddress,
maxSubmissionCost,
maxSubmissionCost,
Expand All @@ -80,17 +84,16 @@ contract L1XERC20GatewayTest is L1XERC20BaseGatewayTest {
emit Transfer(_user, address(0), amountToBridge);

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

uint256 balanceBefore = xerc20.balanceOf(_user);

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

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

Expand Down
25 changes: 25 additions & 0 deletions test/forking/L1XERC20GatewayArbitrumCompatibleToken.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.25 <0.9.0;

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

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

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

import { L1XERC20GatewayForkingTest } from "test/forking/L1XERC20Gateway.t.sol";

contract L1XERC20GatewayArbitrumCompatibleTokenTest is L1XERC20GatewayForkingTest {
L1ArbitrumEnabledXERC20 internal arbEnabledToken;

function setUp() public override {
super.setUp();
bridgeable = address(arbEnabledToken);
}

function _createXERC20() internal override {
arbEnabledToken = new L1ArbitrumEnabledXERC20("ArbitrumEnabledToken", "AET", _owner, address(l1Gateway));
xerc20 = XERC20(address(arbEnabledToken));
}
}

0 comments on commit 4094826

Please sign in to comment.