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

L2 custom gateway and adapter #2

Merged
merged 3 commits into from
May 6, 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
4 changes: 3 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@
wrap_comments = true

[rpc_endpoints]
arbitrum = "https://arbitrum-mainnet.infura.io/v3/${API_KEY_INFURA}"
# arbitrum = "https://arbitrum-mainnet.infura.io/v3/${API_KEY_INFURA}"
# arbitrum = "https://rpc.ankr.com/arbitrum"
arbitrum = "https://arbitrum.llamarpc.com"
avalanche = "https://avalanche-mainnet.infura.io/v3/${API_KEY_INFURA}"
bnb_smart_chain = "https://bsc-dataseed.binance.org"
gnosis_chain = "https://rpc.gnosischain.com"
Expand Down
53 changes: 3 additions & 50 deletions src/L1XERC20Adapter.sol
Original file line number Diff line number Diff line change
@@ -1,33 +1,18 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.25 <0.9.0;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";

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

import { IXERC20Adapter } from "src/interfaces/IXERC20Adapter.sol";
import { XERC20BaseAdapter } from "src/XERC20BaseAdapter.sol";
import { L1XERC20Gateway } from "src/L1XERC20Gateway.sol";

contract L1XERC20Adapter is IXERC20Adapter, ERC165, Ownable {
address internal xerc20;
address internal gatewayAddress;

constructor(address _xerc20, address _gatewayAddress) {
// TODO: maybe we should check whether the token is actually an XERC20
xerc20 = _xerc20;
gatewayAddress = _gatewayAddress;
}
contract L1XERC20Adapter is XERC20BaseAdapter {
constructor(address _xerc20, address _gatewayAddress) XERC20BaseAdapter(_xerc20, _gatewayAddress) { }

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

function getXERC20() external view returns (address) {
return xerc20;
}

function registerTokenOnL2(
address l2TokenAddress,
uint256 maxSubmissionCostForGateway,
Expand All @@ -52,36 +37,4 @@ contract L1XERC20Adapter is IXERC20Adapter, ERC165, Ownable {
gatewayAddress, maxGasForRouter, gasPriceBid, maxSubmissionCostForRouter, creditBackAddress
);
}

// Standard ERC20 view functions

function name() external view returns (string memory) {
return ERC20(xerc20).name();
}

function symbol() external view returns (string memory) {
return ERC20(xerc20).symbol();
}

function decimals() external view returns (uint8) {
return ERC20(xerc20).decimals();
}

function totalSupply() external view returns (uint256) {
return ERC20(xerc20).totalSupply();
}

function balanceOf(address account) external view returns (uint256) {
return ERC20(xerc20).balanceOf(account);
}

function allowance(address owner, address spender) external view returns (uint256) {
return ERC20(xerc20).allowance(owner, spender);
}

// ERC165

function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
return interfaceId == type(IXERC20Adapter).interfaceId || super.supportsInterface(interfaceId);
}
}
18 changes: 18 additions & 0 deletions src/L2XERC20Adapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.25 <0.9.0;

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

contract L2XERC20Adapter is XERC20BaseAdapter {
address public l1Address;

constructor(
address _xerc20,
address _gatewayAddress,
address _l1AdapterOrToken
)
XERC20BaseAdapter(_xerc20, _gatewayAddress)
{
l1Address = _l1AdapterOrToken;
}
}
31 changes: 31 additions & 0 deletions src/L2XERC20Gateway.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.25 <0.9.0;

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

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

/**
* @title Gateway for xERC20 bridging functionality
*/
contract L2XERC20Gateway is XERC20BaseGateway, L2CustomGateway {
constructor(address _l1Counterpart, address _l2Router) {
initialize(_l1Counterpart, _l2Router);
}

function outboundEscrowTransfer(
address _l2TokenOrAdapter,
address _from,
uint256 _amount
)
internal
override
returns (uint256 amountBurnt)
{
return _outboundEscrowTransfer(_l2TokenOrAdapter, _from, _amount);
}

function inboundEscrowTransfer(address _l2TokenOrAdapter, address _dest, uint256 _amount) internal override {
_inboundEscrowTransfer(_l2TokenOrAdapter, _dest, _amount);
}
}
55 changes: 55 additions & 0 deletions src/XERC20BaseAdapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.25 <0.9.0;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";

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

abstract contract XERC20BaseAdapter is IXERC20Adapter, ERC165, Ownable {
address internal xerc20;
address internal gatewayAddress;

constructor(address _xerc20, address _gatewayAddress) {
// TODO: maybe we should check whether the token is actually an XERC20
xerc20 = _xerc20;
gatewayAddress = _gatewayAddress;
}

function getXERC20() external view returns (address) {
return xerc20;
}

// Standard ERC20 view functions

function name() external view returns (string memory) {
return ERC20(xerc20).name();
}

function symbol() external view returns (string memory) {
return ERC20(xerc20).symbol();
}

function decimals() external view returns (uint8) {
return ERC20(xerc20).decimals();
}

function totalSupply() external view returns (uint256) {
return ERC20(xerc20).totalSupply();
}

function balanceOf(address account) external view returns (uint256) {
return ERC20(xerc20).balanceOf(account);
}

function allowance(address owner, address spender) external view returns (uint256) {
return ERC20(xerc20).allowance(owner, spender);
}

// ERC165

function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
return interfaceId == type(IXERC20Adapter).interfaceId || super.supportsInterface(interfaceId);
}
}
7 changes: 0 additions & 7 deletions src/interfaces/IXERC20Adapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,5 @@
pragma solidity >=0.8.25 <0.9.0;

interface IXERC20Adapter {
function isArbitrumEnabled() external view returns (uint8);
function getXERC20() external view returns (address);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
}
16 changes: 5 additions & 11 deletions test/L1XERC20AdapterTest.t.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.25 <0.9.0;

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

import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
Expand All @@ -11,20 +10,15 @@ import { XERC20 } from "xerc20/contracts/XERC20.sol";
import { L1XERC20Adapter } from "src/L1XERC20Adapter.sol";
import { IXERC20Adapter } from "src/interfaces/IXERC20Adapter.sol";

contract L1XERC20AdapterTest is Test {
XERC20 internal xerc20;
import { XERC20BaseAdapterTest } from "test/XERC20BaseAdapterTest.t.sol";

contract L1XERC20AdapterTest is XERC20BaseAdapterTest {
L1XERC20Adapter internal adapter;

address internal _owner = address(0x1);
address internal _minter = address(0x2);
address internal _user = address(0x3);
function setUp() public override {
super.setUp();

function setUp() public {
xerc20 = new XERC20("NonArbitrumEnabled", "NON", _owner);
adapter = new L1XERC20Adapter(address(xerc20), makeAddr("gateway"));

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

function test_IsArbitrumEnabled() public view {
Expand Down
70 changes: 70 additions & 0 deletions test/L2XERC20AdapterTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.25 <0.9.0;

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

import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";

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

import { L2XERC20Adapter } from "src/L2XERC20Adapter.sol";
import { IXERC20Adapter } from "src/interfaces/IXERC20Adapter.sol";

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

contract L2XERC20AdapterTest is XERC20BaseAdapterTest {
L2XERC20Adapter internal adapter;

address internal _l1Token = makeAddr("l1Token");

function setUp() public override {
super.setUp();

adapter = new L2XERC20Adapter(address(xerc20), makeAddr("gateway"), _l1Token);
}

function test_L1Address() public view {
assertEq(adapter.l1Address(), _l1Token);
}

function test_GetXERC20() public view {
assertEq(adapter.getXERC20(), address(xerc20));
}

function test_Name() public view {
assertEq(adapter.name(), xerc20.name());
}

function test_Symbol() public view {
assertEq(adapter.symbol(), xerc20.symbol());
}

function test_Decimals() public view {
assertEq(adapter.decimals(), xerc20.decimals());
}

function test_TotalSupply() public {
vm.prank(_minter);
xerc20.mint(_user, 10 ether);

assertEq(adapter.totalSupply(), 10 ether);
}

function test_BalanceOf() public {
vm.prank(_minter);
xerc20.mint(_user, 1 ether);

assertEq(adapter.balanceOf(_user), xerc20.balanceOf(_user));
}

function test_SupportsInterface() public view {
bytes4 iface = type(IERC165).interfaceId;
assertEq(adapter.supportsInterface(iface), true, "Interface should be supported");

iface = type(IXERC20Adapter).interfaceId;
assertEq(adapter.supportsInterface(iface), true, "Interface should be supported");

iface = bytes4(0);
assertEq(adapter.supportsInterface(iface), false, "Interface shouldn't be supported");
}
}
21 changes: 21 additions & 0 deletions test/XERC20BaseAdapterTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// 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";

abstract contract XERC20BaseAdapterTest is Test {
XERC20 internal xerc20;

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

function setUp() public virtual {
xerc20 = new XERC20("NonArbitrumEnabled", "NON", _owner);

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