Commit 94ab846a authored by AgusDuha's avatar AgusDuha Committed by GitHub

feat: implement IERC7802 (#12790)

* feat: add IERC7802 (#123)

* feat: add IERC7802

* fix: token address fuzz error

* feat: remove ERC165 contract inheritance

* feat: add IERC20 interface support (#124)

* feat: add IERC20 interface support

* fix: interfaces tests
parent 9bdcb026
...@@ -108,8 +108,8 @@ ...@@ -108,8 +108,8 @@
"sourceCodeHash": "0xa76133db7f449ae742f9ba988ad86ccb5672475f61298b9fefe411b63b63e9f6" "sourceCodeHash": "0xa76133db7f449ae742f9ba988ad86ccb5672475f61298b9fefe411b63b63e9f6"
}, },
"src/L2/OptimismSuperchainERC20.sol": { "src/L2/OptimismSuperchainERC20.sol": {
"initCodeHash": "0x24d85d246858d1aff78ae86c614dd0dc0f63b3326b2b662e3462c3a6f9b7965e", "initCodeHash": "0x5bc5824030ecdb531e1f615d207cb73cdaa702e198769445d0ddbe717271eba9",
"sourceCodeHash": "0xcb705d26e63e733051c8bd442ea69ce637a00c16d646ccc37b687b20941366fe" "sourceCodeHash": "0x0819c9411a155dca592d19b60c4176954202e4fe5d632a4ffbf88d465461252c"
}, },
"src/L2/OptimismSuperchainERC20Beacon.sol": { "src/L2/OptimismSuperchainERC20Beacon.sol": {
"initCodeHash": "0x23dba3ceb9e58646695c306996c9e15251ac79acc6339c1a93d10a4c79da6dab", "initCodeHash": "0x23dba3ceb9e58646695c306996c9e15251ac79acc6339c1a93d10a4c79da6dab",
...@@ -125,15 +125,15 @@ ...@@ -125,15 +125,15 @@
}, },
"src/L2/SuperchainERC20.sol": { "src/L2/SuperchainERC20.sol": {
"initCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "initCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
"sourceCodeHash": "0xba47f404e66e010ce417410476f26c704f2be4ce584cb79210bc5536a82ddb1f" "sourceCodeHash": "0xcf39c16893cace1e7d61350bfff05a27f3ce8da8eb0ac02cb5ac7bf603f163fa"
}, },
"src/L2/SuperchainTokenBridge.sol": { "src/L2/SuperchainTokenBridge.sol": {
"initCodeHash": "0xef7590c30630a75f105384e339e52758569c25a5aa0a5934c521e004b8f86220", "initCodeHash": "0x1cd2afdae6dd1b6ebc17f1d529e7d74c9b8b21b02db8589b8e389e2d5523d775",
"sourceCodeHash": "0x4f539e9d9096d31e861982b8f751fa2d7de0849590523375cf92e175294d1036" "sourceCodeHash": "0x617aa994f659c5d8ebd54128d994f86f5b175ceca095b024b8524a7898e8ae62"
}, },
"src/L2/SuperchainWETH.sol": { "src/L2/SuperchainWETH.sol": {
"initCodeHash": "0xc72cb486b815a65daa8bd5d0af8c965b6708cf8caf03de0a18023a63a6e6c604", "initCodeHash": "0x5aef986a7c9c102b1e9b3068e2a2b66adce0a71dd5f39e03694622bf494f8d97",
"sourceCodeHash": "0x39fff1d4702a2fec3dcba29c7f9604eabf20d32e9c5bf4377edeb620624aa467" "sourceCodeHash": "0xa62101a23b860e97f393027c898082a1c73d50679eceb6c6793844af29702359"
}, },
"src/L2/WETH.sol": { "src/L2/WETH.sol": {
"initCodeHash": "0x17ea1b1c5d5a622d51c2961fde886a5498de63584e654ed1d69ee80dddbe0b17", "initCodeHash": "0x17ea1b1c5d5a622d51c2961fde886a5498de63584e654ed1d69ee80dddbe0b17",
......
...@@ -153,6 +153,11 @@ ...@@ -153,6 +153,11 @@
"name": "InvalidCrossDomainSender", "name": "InvalidCrossDomainSender",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "InvalidERC7802",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "Unauthorized", "name": "Unauthorized",
......
...@@ -143,6 +143,25 @@ ...@@ -143,6 +143,25 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "bytes4",
"name": "_interfaceId",
"type": "bytes4"
}
],
"name": "supportsInterface",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "symbol", "name": "symbol",
......
...@@ -3,7 +3,6 @@ pragma solidity 0.8.25; ...@@ -3,7 +3,6 @@ pragma solidity 0.8.25;
import { IOptimismSuperchainERC20 } from "src/L2/interfaces/IOptimismSuperchainERC20.sol"; import { IOptimismSuperchainERC20 } from "src/L2/interfaces/IOptimismSuperchainERC20.sol";
import { Predeploys } from "src/libraries/Predeploys.sol"; import { Predeploys } from "src/libraries/Predeploys.sol";
import { ERC165 } from "@openzeppelin/contracts-v5/utils/introspection/ERC165.sol";
import { SuperchainERC20 } from "src/L2/SuperchainERC20.sol"; import { SuperchainERC20 } from "src/L2/SuperchainERC20.sol";
import { Initializable } from "@openzeppelin/contracts-v5/proxy/utils/Initializable.sol"; import { Initializable } from "@openzeppelin/contracts-v5/proxy/utils/Initializable.sol";
import { ZeroAddress, Unauthorized } from "src/libraries/errors/CommonErrors.sol"; import { ZeroAddress, Unauthorized } from "src/libraries/errors/CommonErrors.sol";
...@@ -16,7 +15,7 @@ import { ZeroAddress, Unauthorized } from "src/libraries/errors/CommonErrors.sol ...@@ -16,7 +15,7 @@ import { ZeroAddress, Unauthorized } from "src/libraries/errors/CommonErrors.sol
/// OptimismSuperchainERC20 token, turning it fungible and interoperable across the superchain. Likewise, it /// OptimismSuperchainERC20 token, turning it fungible and interoperable across the superchain. Likewise, it
/// also enables the inverse conversion path. /// also enables the inverse conversion path.
/// Moreover, it builds on top of the L2ToL2CrossDomainMessenger for both replay protection and domain binding. /// Moreover, it builds on top of the L2ToL2CrossDomainMessenger for both replay protection and domain binding.
contract OptimismSuperchainERC20 is SuperchainERC20, Initializable, ERC165 { contract OptimismSuperchainERC20 is SuperchainERC20, Initializable {
/// @notice Emitted whenever tokens are minted for an account. /// @notice Emitted whenever tokens are minted for an account.
/// @param to Address of the account tokens are being minted for. /// @param to Address of the account tokens are being minted for.
/// @param amount Amount of tokens minted. /// @param amount Amount of tokens minted.
...@@ -59,8 +58,8 @@ contract OptimismSuperchainERC20 is SuperchainERC20, Initializable, ERC165 { ...@@ -59,8 +58,8 @@ contract OptimismSuperchainERC20 is SuperchainERC20, Initializable, ERC165 {
} }
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.0.0-beta.8 /// @custom:semver 1.0.0-beta.9
string public constant override version = "1.0.0-beta.8"; string public constant override version = "1.0.0-beta.9";
/// @notice Constructs the OptimismSuperchainERC20 contract. /// @notice Constructs the OptimismSuperchainERC20 contract.
constructor() { constructor() {
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.25; pragma solidity 0.8.25;
import { ICrosschainERC20 } from "src/L2/interfaces/ICrosschainERC20.sol"; import { IERC7802, IERC165 } from "src/L2/interfaces/IERC7802.sol";
import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol";
import { Predeploys } from "src/libraries/Predeploys.sol"; import { Predeploys } from "src/libraries/Predeploys.sol";
import { ERC20 } from "@solady-v0.0.245/tokens/ERC20.sol"; import { ERC20 } from "@solady-v0.0.245/tokens/ERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Unauthorized } from "src/libraries/errors/CommonErrors.sol"; import { Unauthorized } from "src/libraries/errors/CommonErrors.sol";
/// @title SuperchainERC20 /// @title SuperchainERC20
/// @notice SuperchainERC20 is a standard extension of the base ERC20 token contract that unifies ERC20 token /// @notice A standard ERC20 extension implementing IERC7802 for unified cross-chain fungibility across
/// bridging to make it fungible across the Superchain. This construction allows the SuperchainTokenBridge to /// the Superchain. Allows the SuperchainTokenBridge to mint and burn tokens as needed.
/// burn and mint tokens. abstract contract SuperchainERC20 is ERC20, IERC7802, ISemver {
abstract contract SuperchainERC20 is ERC20, ICrosschainERC20, ISemver {
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.0.0-beta.4 /// @custom:semver 1.0.0-beta.5
function version() external view virtual returns (string memory) { function version() external view virtual returns (string memory) {
return "1.0.0-beta.4"; return "1.0.0-beta.5";
} }
/// @notice Allows the SuperchainTokenBridge to mint tokens. /// @notice Allows the SuperchainTokenBridge to mint tokens.
...@@ -39,4 +39,10 @@ abstract contract SuperchainERC20 is ERC20, ICrosschainERC20, ISemver { ...@@ -39,4 +39,10 @@ abstract contract SuperchainERC20 is ERC20, ICrosschainERC20, ISemver {
emit CrosschainBurn(_from, _amount); emit CrosschainBurn(_from, _amount);
} }
/// @inheritdoc IERC165
function supportsInterface(bytes4 _interfaceId) public view virtual returns (bool) {
return _interfaceId == type(IERC7802).interfaceId || _interfaceId == type(IERC20).interfaceId
|| _interfaceId == type(IERC165).interfaceId;
}
} }
...@@ -7,6 +7,7 @@ import { ZeroAddress, Unauthorized } from "src/libraries/errors/CommonErrors.sol ...@@ -7,6 +7,7 @@ import { ZeroAddress, Unauthorized } from "src/libraries/errors/CommonErrors.sol
// Interfaces // Interfaces
import { ISuperchainERC20 } from "src/L2/interfaces/ISuperchainERC20.sol"; import { ISuperchainERC20 } from "src/L2/interfaces/ISuperchainERC20.sol";
import { IERC7802, IERC165 } from "src/L2/interfaces/IERC7802.sol";
import { IL2ToL2CrossDomainMessenger } from "src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol"; import { IL2ToL2CrossDomainMessenger } from "src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol";
/// @custom:proxied true /// @custom:proxied true
...@@ -20,6 +21,9 @@ contract SuperchainTokenBridge { ...@@ -20,6 +21,9 @@ contract SuperchainTokenBridge {
/// SuperchainTokenBridge. /// SuperchainTokenBridge.
error InvalidCrossDomainSender(); error InvalidCrossDomainSender();
/// @notice Thrown when attempting to use a token that does not implement the ERC7802 interface.
error InvalidERC7802();
/// @notice Emitted when tokens are sent from one chain to another. /// @notice Emitted when tokens are sent from one chain to another.
/// @param token Address of the token sent. /// @param token Address of the token sent.
/// @param from Address of the sender. /// @param from Address of the sender.
...@@ -42,8 +46,8 @@ contract SuperchainTokenBridge { ...@@ -42,8 +46,8 @@ contract SuperchainTokenBridge {
address internal constant MESSENGER = Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER; address internal constant MESSENGER = Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER;
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.0.0-beta.2 /// @custom:semver 1.0.0-beta.3
string public constant version = "1.0.0-beta.2"; string public constant version = "1.0.0-beta.3";
/// @notice Sends tokens to a target address on another chain. /// @notice Sends tokens to a target address on another chain.
/// @dev Tokens are burned on the source chain. /// @dev Tokens are burned on the source chain.
...@@ -63,6 +67,8 @@ contract SuperchainTokenBridge { ...@@ -63,6 +67,8 @@ contract SuperchainTokenBridge {
{ {
if (_to == address(0)) revert ZeroAddress(); if (_to == address(0)) revert ZeroAddress();
if (!IERC165(_token).supportsInterface(type(IERC7802).interfaceId)) revert InvalidERC7802();
ISuperchainERC20(_token).crosschainBurn(msg.sender, _amount); ISuperchainERC20(_token).crosschainBurn(msg.sender, _amount);
bytes memory message = abi.encodeCall(this.relayERC20, (_token, msg.sender, _to, _amount)); bytes memory message = abi.encodeCall(this.relayERC20, (_token, msg.sender, _to, _amount));
...@@ -82,6 +88,7 @@ contract SuperchainTokenBridge { ...@@ -82,6 +88,7 @@ contract SuperchainTokenBridge {
(address crossDomainMessageSender, uint256 source) = (address crossDomainMessageSender, uint256 source) =
IL2ToL2CrossDomainMessenger(MESSENGER).crossDomainMessageContext(); IL2ToL2CrossDomainMessenger(MESSENGER).crossDomainMessageContext();
if (crossDomainMessageSender != address(this)) revert InvalidCrossDomainSender(); if (crossDomainMessageSender != address(this)) revert InvalidCrossDomainSender();
ISuperchainERC20(_token).crosschainMint(_to, _amount); ISuperchainERC20(_token).crosschainMint(_to, _amount);
......
...@@ -12,7 +12,8 @@ import { Preinstalls } from "src/libraries/Preinstalls.sol"; ...@@ -12,7 +12,8 @@ import { Preinstalls } from "src/libraries/Preinstalls.sol";
import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol";
import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; import { IL1Block } from "src/L2/interfaces/IL1Block.sol";
import { IETHLiquidity } from "src/L2/interfaces/IETHLiquidity.sol"; import { IETHLiquidity } from "src/L2/interfaces/IETHLiquidity.sol";
import { ICrosschainERC20 } from "src/L2/interfaces/ICrosschainERC20.sol"; import { IERC7802, IERC165 } from "src/L2/interfaces/IERC7802.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErrors.sol"; import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErrors.sol";
/// @custom:proxied true /// @custom:proxied true
...@@ -21,10 +22,10 @@ import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErro ...@@ -21,10 +22,10 @@ import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErro
/// @notice SuperchainWETH is a version of WETH that can be freely transfrered between chains /// @notice SuperchainWETH is a version of WETH that can be freely transfrered between chains
/// within the superchain. SuperchainWETH can be converted into native ETH on chains that /// within the superchain. SuperchainWETH can be converted into native ETH on chains that
/// do not use a custom gas token. /// do not use a custom gas token.
contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver { contract SuperchainWETH is WETH98, IERC7802, ISemver {
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.0.0-beta.9 /// @custom:semver 1.0.0-beta.10
string public constant version = "1.0.0-beta.9"; string public constant version = "1.0.0-beta.10";
/// @inheritdoc WETH98 /// @inheritdoc WETH98
function deposit() public payable override { function deposit() public payable override {
...@@ -91,4 +92,10 @@ contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver { ...@@ -91,4 +92,10 @@ contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver {
emit CrosschainBurn(_from, _amount); emit CrosschainBurn(_from, _amount);
} }
/// @inheritdoc IERC165
function supportsInterface(bytes4 _interfaceId) public view virtual returns (bool) {
return _interfaceId == type(IERC7802).interfaceId || _interfaceId == type(IERC20).interfaceId
|| _interfaceId == type(IERC165).interfaceId;
}
} }
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
/// @title ICrosschainERC20 import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
/// @title IERC7802
/// @notice Defines the interface for crosschain ERC20 transfers. /// @notice Defines the interface for crosschain ERC20 transfers.
interface ICrosschainERC20 { interface IERC7802 is IERC165 {
/// @notice Emitted when a crosschain transfer mints tokens. /// @notice Emitted when a crosschain transfer mints tokens.
/// @param to Address of the account tokens are being minted for. /// @param to Address of the account tokens are being minted for.
/// @param amount Amount of tokens minted. /// @param amount Amount of tokens minted.
......
...@@ -2,15 +2,17 @@ ...@@ -2,15 +2,17 @@
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
// Interfaces // Interfaces
import { ICrosschainERC20 } from "src/L2/interfaces/ICrosschainERC20.sol"; import { IERC7802 } from "src/L2/interfaces/IERC7802.sol";
import { IERC20Solady as IERC20 } from "src/vendor/interfaces/IERC20Solady.sol"; import { IERC20Solady as IERC20 } from "src/vendor/interfaces/IERC20Solady.sol";
import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol";
/// @title ISuperchainERC20 /// @title ISuperchainERC20
/// @notice This interface is available on the SuperchainERC20 contract. /// @notice This interface is available on the SuperchainERC20 contract.
/// @dev This interface is needed for the abstract SuperchainERC20 implementation but is not part of the standard /// @dev This interface is needed for the abstract SuperchainERC20 implementation but is not part of the standard
interface ISuperchainERC20 is ICrosschainERC20, IERC20, ISemver { interface ISuperchainERC20 is IERC7802, IERC20, ISemver {
error Unauthorized(); error Unauthorized();
function supportsInterface(bytes4 _interfaceId) external view returns (bool);
function __constructor__() external; function __constructor__() external;
} }
...@@ -9,6 +9,7 @@ interface ISuperchainTokenBridge is ISemver { ...@@ -9,6 +9,7 @@ interface ISuperchainTokenBridge is ISemver {
error ZeroAddress(); error ZeroAddress();
error Unauthorized(); error Unauthorized();
error InvalidCrossDomainSender(); error InvalidCrossDomainSender();
error InvalidERC7802();
event SendERC20( event SendERC20(
address indexed token, address indexed from, address indexed to, uint256 amount, uint256 destination address indexed token, address indexed from, address indexed to, uint256 amount, uint256 destination
......
...@@ -2,15 +2,16 @@ ...@@ -2,15 +2,16 @@
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import { IWETH98 } from "src/universal/interfaces/IWETH98.sol"; import { IWETH98 } from "src/universal/interfaces/IWETH98.sol";
import { ICrosschainERC20 } from "src/L2/interfaces/ICrosschainERC20.sol"; import { IERC7802 } from "src/L2/interfaces/IERC7802.sol";
import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol";
interface ISuperchainWETH is IWETH98, ICrosschainERC20, ISemver { interface ISuperchainWETH is IWETH98, IERC7802, ISemver {
error Unauthorized(); error Unauthorized();
error NotCustomGasToken(); error NotCustomGasToken();
function balanceOf(address src) external view returns (uint256); function balanceOf(address src) external view returns (uint256);
function withdraw(uint256 _amount) external; function withdraw(uint256 _amount) external;
function supportsInterface(bytes4 _interfaceId) external view returns (bool);
function __constructor__() external; function __constructor__() external;
} }
...@@ -6,11 +6,11 @@ import { Test } from "forge-std/Test.sol"; ...@@ -6,11 +6,11 @@ import { Test } from "forge-std/Test.sol";
// Libraries // Libraries
import { Predeploys } from "src/libraries/Predeploys.sol"; import { Predeploys } from "src/libraries/Predeploys.sol";
import { IERC20Solady as IERC20 } from "src/vendor/interfaces/IERC20Solady.sol";
// Target contract // Target contract
import { SuperchainERC20 } from "src/L2/SuperchainERC20.sol"; import { SuperchainERC20 } from "src/L2/SuperchainERC20.sol";
import { ICrosschainERC20 } from "src/L2/interfaces/ICrosschainERC20.sol"; import { IERC7802, IERC165 } from "src/L2/interfaces/IERC7802.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ISuperchainERC20 } from "src/L2/interfaces/ISuperchainERC20.sol"; import { ISuperchainERC20 } from "src/L2/interfaces/ISuperchainERC20.sol";
import { MockSuperchainERC20Implementation } from "test/mocks/SuperchainERC20Implementation.sol"; import { MockSuperchainERC20Implementation } from "test/mocks/SuperchainERC20Implementation.sol";
...@@ -62,7 +62,7 @@ contract SuperchainERC20Test is Test { ...@@ -62,7 +62,7 @@ contract SuperchainERC20Test is Test {
// Look for the emit of the `CrosschainMint` event // Look for the emit of the `CrosschainMint` event
vm.expectEmit(address(superchainERC20)); vm.expectEmit(address(superchainERC20));
emit ICrosschainERC20.CrosschainMint(_to, _amount); emit IERC7802.CrosschainMint(_to, _amount);
// Call the `mint` function with the bridge caller // Call the `mint` function with the bridge caller
vm.prank(SUPERCHAIN_TOKEN_BRIDGE); vm.prank(SUPERCHAIN_TOKEN_BRIDGE);
...@@ -105,7 +105,7 @@ contract SuperchainERC20Test is Test { ...@@ -105,7 +105,7 @@ contract SuperchainERC20Test is Test {
// Look for the emit of the `CrosschainBurn` event // Look for the emit of the `CrosschainBurn` event
vm.expectEmit(address(superchainERC20)); vm.expectEmit(address(superchainERC20));
emit ICrosschainERC20.CrosschainBurn(_from, _amount); emit IERC7802.CrosschainBurn(_from, _amount);
// Call the `burn` function with the bridge caller // Call the `burn` function with the bridge caller
vm.prank(SUPERCHAIN_TOKEN_BRIDGE); vm.prank(SUPERCHAIN_TOKEN_BRIDGE);
...@@ -115,4 +115,20 @@ contract SuperchainERC20Test is Test { ...@@ -115,4 +115,20 @@ contract SuperchainERC20Test is Test {
assertEq(superchainERC20.totalSupply(), _totalSupplyBefore - _amount); assertEq(superchainERC20.totalSupply(), _totalSupplyBefore - _amount);
assertEq(superchainERC20.balanceOf(_from), _fromBalanceBefore - _amount); assertEq(superchainERC20.balanceOf(_from), _fromBalanceBefore - _amount);
} }
/// @notice Tests that the `supportsInterface` function returns true for the `IERC7802` interface.
function test_supportInterface_succeeds() public view {
assertTrue(superchainERC20.supportsInterface(type(IERC165).interfaceId));
assertTrue(superchainERC20.supportsInterface(type(IERC7802).interfaceId));
assertTrue(superchainERC20.supportsInterface(type(IERC20).interfaceId));
}
/// @notice Tests that the `supportsInterface` function returns false for any other interface than the
/// `IERC7802` one.
function testFuzz_supportInterface_returnFalse(bytes4 _interfaceId) public view {
vm.assume(_interfaceId != type(IERC165).interfaceId);
vm.assume(_interfaceId != type(IERC7802).interfaceId);
vm.assume(_interfaceId != type(IERC20).interfaceId);
assertFalse(superchainERC20.supportsInterface(_interfaceId));
}
} }
...@@ -13,6 +13,7 @@ import { ISuperchainTokenBridge } from "src/L2/interfaces/ISuperchainTokenBridge ...@@ -13,6 +13,7 @@ import { ISuperchainTokenBridge } from "src/L2/interfaces/ISuperchainTokenBridge
import { ISuperchainERC20 } from "src/L2/interfaces/ISuperchainERC20.sol"; import { ISuperchainERC20 } from "src/L2/interfaces/ISuperchainERC20.sol";
import { IOptimismSuperchainERC20Factory } from "src/L2/interfaces/IOptimismSuperchainERC20Factory.sol"; import { IOptimismSuperchainERC20Factory } from "src/L2/interfaces/IOptimismSuperchainERC20Factory.sol";
import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
import { IERC7802 } from "src/L2/interfaces/IERC7802.sol";
/// @title SuperchainTokenBridgeTest /// @title SuperchainTokenBridgeTest
/// @notice Contract for testing the SuperchainTokenBridge contract. /// @notice Contract for testing the SuperchainTokenBridge contract.
...@@ -60,6 +61,32 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer { ...@@ -60,6 +61,32 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer {
superchainTokenBridge.sendERC20(address(superchainERC20), ZERO_ADDRESS, _amount, _chainId); superchainTokenBridge.sendERC20(address(superchainERC20), ZERO_ADDRESS, _amount, _chainId);
} }
/// @notice Tests the `sendERC20` function reverts when the `token` does not support the IERC7802 interface.
function testFuzz_sendERC20_notSupportedIERC7802_reverts(
address _token,
address _sender,
address _to,
uint256 _amount,
uint256 _chainId
)
public
{
vm.assume(_to != ZERO_ADDRESS);
assumeAddressIsNot(_token, AddressType.Precompile, AddressType.ForgeAddress);
// Mock the call over the `supportsInterface` function to return false
vm.mockCall(
_token, abi.encodeCall(ISuperchainERC20.supportsInterface, (type(IERC7802).interfaceId)), abi.encode(false)
);
// Expect the revert with `InvalidERC7802` selector
vm.expectRevert(ISuperchainTokenBridge.InvalidERC7802.selector);
// Call the `sendERC20` function
vm.prank(_sender);
superchainTokenBridge.sendERC20(_token, _to, _amount, _chainId);
}
/// @notice Tests the `sendERC20` function burns the sender tokens, sends the message, and emits the `SendERC20` /// @notice Tests the `sendERC20` function burns the sender tokens, sends the message, and emits the `SendERC20`
/// event. /// event.
function testFuzz_sendERC20_succeeds( function testFuzz_sendERC20_succeeds(
...@@ -137,7 +164,6 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer { ...@@ -137,7 +164,6 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer {
/// @notice Tests the `relayERC20` function reverts when the `crossDomainMessageSender` that sent the message is not /// @notice Tests the `relayERC20` function reverts when the `crossDomainMessageSender` that sent the message is not
/// the same SuperchainTokenBridge. /// the same SuperchainTokenBridge.
function testFuzz_relayERC20_notCrossDomainSender_reverts( function testFuzz_relayERC20_notCrossDomainSender_reverts(
address _token,
address _crossDomainMessageSender, address _crossDomainMessageSender,
uint256 _source, uint256 _source,
address _to, address _to,
...@@ -159,7 +185,7 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer { ...@@ -159,7 +185,7 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer {
// Call the `relayERC20` function with the sender caller // Call the `relayERC20` function with the sender caller
vm.prank(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); vm.prank(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
superchainTokenBridge.relayERC20(_token, _crossDomainMessageSender, _to, _amount); superchainTokenBridge.relayERC20(address(superchainERC20), _crossDomainMessageSender, _to, _amount);
} }
/// @notice Tests the `relayERC20` mints the proper amount and emits the `RelayERC20` event. /// @notice Tests the `relayERC20` mints the proper amount and emits the `RelayERC20` event.
......
...@@ -12,6 +12,8 @@ import { Preinstalls } from "src/libraries/Preinstalls.sol"; ...@@ -12,6 +12,8 @@ import { Preinstalls } from "src/libraries/Preinstalls.sol";
// Interfaces // Interfaces
import { IETHLiquidity } from "src/L2/interfaces/IETHLiquidity.sol"; import { IETHLiquidity } from "src/L2/interfaces/IETHLiquidity.sol";
import { ISuperchainWETH } from "src/L2/interfaces/ISuperchainWETH.sol"; import { ISuperchainWETH } from "src/L2/interfaces/ISuperchainWETH.sol";
import { IERC7802, IERC165 } from "src/L2/interfaces/IERC7802.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// @title SuperchainWETH_Test /// @title SuperchainWETH_Test
/// @notice Contract for testing the SuperchainWETH contract. /// @notice Contract for testing the SuperchainWETH contract.
...@@ -457,4 +459,20 @@ contract SuperchainWETH_Test is CommonTest { ...@@ -457,4 +459,20 @@ contract SuperchainWETH_Test is CommonTest {
// Assert // Assert
assertEq(superchainWeth.balanceOf(_user), _wad); assertEq(superchainWeth.balanceOf(_user), _wad);
} }
/// @notice Tests that the `supportsInterface` function returns true for the `IERC7802` interface.
function test_supportInterface_succeeds() public view {
assertTrue(superchainWeth.supportsInterface(type(IERC165).interfaceId));
assertTrue(superchainWeth.supportsInterface(type(IERC7802).interfaceId));
assertTrue(superchainWeth.supportsInterface(type(IERC20).interfaceId));
}
/// @notice Tests that the `supportsInterface` function returns false for any other interface than the
/// `IERC7802` one.
function testFuzz_supportInterface_returnFalse(bytes4 _interfaceId) public view {
vm.assume(_interfaceId != type(IERC165).interfaceId);
vm.assume(_interfaceId != type(IERC7802).interfaceId);
vm.assume(_interfaceId != type(IERC20).interfaceId);
assertFalse(superchainWeth.supportsInterface(_interfaceId));
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment