Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arbitrum compatible xerc20 tests #5

Merged
merged 7 commits into from
May 9, 2024
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
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));
}
}