From 449656399c6e119841fea8d3c7020fbb086e68ba Mon Sep 17 00:00:00 2001 From: AgusDuha <81362284+agusduha@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:04:47 -0300 Subject: [PATCH] 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 --- packages/contracts-bedrock/semver-lock.json | 14 ++++----- .../snapshots/abi/SuperchainTokenBridge.json | 5 ++++ .../snapshots/abi/SuperchainWETH.json | 19 ++++++++++++ .../src/L2/OptimismSuperchainERC20.sol | 7 ++--- .../src/L2/SuperchainERC20.sol | 20 ++++++++----- .../src/L2/SuperchainTokenBridge.sol | 11 +++++-- .../src/L2/SuperchainWETH.sol | 15 +++++++--- .../{ICrosschainERC20.sol => IERC7802.sol} | 6 ++-- .../src/L2/interfaces/ISuperchainERC20.sol | 6 ++-- .../L2/interfaces/ISuperchainTokenBridge.sol | 1 + .../src/L2/interfaces/ISuperchainWETH.sol | 5 ++-- .../test/L2/SuperchainERC20.t.sol | 24 ++++++++++++--- .../test/L2/SuperchainTokenBridge.t.sol | 30 +++++++++++++++++-- .../test/L2/SuperchainWETH.t.sol | 18 +++++++++++ 14 files changed, 145 insertions(+), 36 deletions(-) rename packages/contracts-bedrock/src/L2/interfaces/{ICrosschainERC20.sol => IERC7802.sol} (89%) diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 8082c799f7553..1e14f5d286b59 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -108,8 +108,8 @@ "sourceCodeHash": "0xa76133db7f449ae742f9ba988ad86ccb5672475f61298b9fefe411b63b63e9f6" }, "src/L2/OptimismSuperchainERC20.sol": { - "initCodeHash": "0x24d85d246858d1aff78ae86c614dd0dc0f63b3326b2b662e3462c3a6f9b7965e", - "sourceCodeHash": "0xcb705d26e63e733051c8bd442ea69ce637a00c16d646ccc37b687b20941366fe" + "initCodeHash": "0x5bc5824030ecdb531e1f615d207cb73cdaa702e198769445d0ddbe717271eba9", + "sourceCodeHash": "0x0819c9411a155dca592d19b60c4176954202e4fe5d632a4ffbf88d465461252c" }, "src/L2/OptimismSuperchainERC20Beacon.sol": { "initCodeHash": "0x23dba3ceb9e58646695c306996c9e15251ac79acc6339c1a93d10a4c79da6dab", @@ -125,15 +125,15 @@ }, "src/L2/SuperchainERC20.sol": { "initCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", - "sourceCodeHash": "0xba47f404e66e010ce417410476f26c704f2be4ce584cb79210bc5536a82ddb1f" + "sourceCodeHash": "0xcf39c16893cace1e7d61350bfff05a27f3ce8da8eb0ac02cb5ac7bf603f163fa" }, "src/L2/SuperchainTokenBridge.sol": { - "initCodeHash": "0xef7590c30630a75f105384e339e52758569c25a5aa0a5934c521e004b8f86220", - "sourceCodeHash": "0x4f539e9d9096d31e861982b8f751fa2d7de0849590523375cf92e175294d1036" + "initCodeHash": "0x1cd2afdae6dd1b6ebc17f1d529e7d74c9b8b21b02db8589b8e389e2d5523d775", + "sourceCodeHash": "0x617aa994f659c5d8ebd54128d994f86f5b175ceca095b024b8524a7898e8ae62" }, "src/L2/SuperchainWETH.sol": { - "initCodeHash": "0xc72cb486b815a65daa8bd5d0af8c965b6708cf8caf03de0a18023a63a6e6c604", - "sourceCodeHash": "0x39fff1d4702a2fec3dcba29c7f9604eabf20d32e9c5bf4377edeb620624aa467" + "initCodeHash": "0x5aef986a7c9c102b1e9b3068e2a2b66adce0a71dd5f39e03694622bf494f8d97", + "sourceCodeHash": "0xa62101a23b860e97f393027c898082a1c73d50679eceb6c6793844af29702359" }, "src/L2/WETH.sol": { "initCodeHash": "0x17ea1b1c5d5a622d51c2961fde886a5498de63584e654ed1d69ee80dddbe0b17", diff --git a/packages/contracts-bedrock/snapshots/abi/SuperchainTokenBridge.json b/packages/contracts-bedrock/snapshots/abi/SuperchainTokenBridge.json index 36358db1b307a..a75bb2ba7234b 100644 --- a/packages/contracts-bedrock/snapshots/abi/SuperchainTokenBridge.json +++ b/packages/contracts-bedrock/snapshots/abi/SuperchainTokenBridge.json @@ -153,6 +153,11 @@ "name": "InvalidCrossDomainSender", "type": "error" }, + { + "inputs": [], + "name": "InvalidERC7802", + "type": "error" + }, { "inputs": [], "name": "Unauthorized", diff --git a/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json b/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json index fe53a4cbfb5ef..32d84df7d889e 100644 --- a/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json +++ b/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json @@ -143,6 +143,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "symbol", diff --git a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol index 1e7cdb4a71902..c323d8b7577be 100644 --- a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol +++ b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.25; import { IOptimismSuperchainERC20 } from "src/L2/interfaces/IOptimismSuperchainERC20.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 { Initializable } from "@openzeppelin/contracts-v5/proxy/utils/Initializable.sol"; 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 /// also enables the inverse conversion path. /// 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. /// @param to Address of the account tokens are being minted for. /// @param amount Amount of tokens minted. @@ -59,8 +58,8 @@ contract OptimismSuperchainERC20 is SuperchainERC20, Initializable, ERC165 { } /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.8 - string public constant override version = "1.0.0-beta.8"; + /// @custom:semver 1.0.0-beta.9 + string public constant override version = "1.0.0-beta.9"; /// @notice Constructs the OptimismSuperchainERC20 contract. constructor() { diff --git a/packages/contracts-bedrock/src/L2/SuperchainERC20.sol b/packages/contracts-bedrock/src/L2/SuperchainERC20.sol index f18f91ad24138..b9e6bbfbf7807 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainERC20.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainERC20.sol @@ -1,21 +1,21 @@ // SPDX-License-Identifier: MIT 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 { Predeploys } from "src/libraries/Predeploys.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"; /// @title SuperchainERC20 -/// @notice SuperchainERC20 is a standard extension of the base ERC20 token contract that unifies ERC20 token -/// bridging to make it fungible across the Superchain. This construction allows the SuperchainTokenBridge to -/// burn and mint tokens. -abstract contract SuperchainERC20 is ERC20, ICrosschainERC20, ISemver { +/// @notice A standard ERC20 extension implementing IERC7802 for unified cross-chain fungibility across +/// the Superchain. Allows the SuperchainTokenBridge to mint and burn tokens as needed. +abstract contract SuperchainERC20 is ERC20, IERC7802, ISemver { /// @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) { - return "1.0.0-beta.4"; + return "1.0.0-beta.5"; } /// @notice Allows the SuperchainTokenBridge to mint tokens. @@ -39,4 +39,10 @@ abstract contract SuperchainERC20 is ERC20, ICrosschainERC20, ISemver { 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; + } } diff --git a/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol b/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol index 284beb79ec0ea..104f8ef950430 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol @@ -7,6 +7,7 @@ import { ZeroAddress, Unauthorized } from "src/libraries/errors/CommonErrors.sol // Interfaces 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"; /// @custom:proxied true @@ -20,6 +21,9 @@ contract SuperchainTokenBridge { /// SuperchainTokenBridge. 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. /// @param token Address of the token sent. /// @param from Address of the sender. @@ -42,8 +46,8 @@ contract SuperchainTokenBridge { address internal constant MESSENGER = Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER; /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.2 - string public constant version = "1.0.0-beta.2"; + /// @custom:semver 1.0.0-beta.3 + string public constant version = "1.0.0-beta.3"; /// @notice Sends tokens to a target address on another chain. /// @dev Tokens are burned on the source chain. @@ -63,6 +67,8 @@ contract SuperchainTokenBridge { { if (_to == address(0)) revert ZeroAddress(); + if (!IERC165(_token).supportsInterface(type(IERC7802).interfaceId)) revert InvalidERC7802(); + ISuperchainERC20(_token).crosschainBurn(msg.sender, _amount); bytes memory message = abi.encodeCall(this.relayERC20, (_token, msg.sender, _to, _amount)); @@ -82,6 +88,7 @@ contract SuperchainTokenBridge { (address crossDomainMessageSender, uint256 source) = IL2ToL2CrossDomainMessenger(MESSENGER).crossDomainMessageContext(); + if (crossDomainMessageSender != address(this)) revert InvalidCrossDomainSender(); ISuperchainERC20(_token).crosschainMint(_to, _amount); diff --git a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol index e03b689ba1501..29e179eba82ce 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol @@ -12,7 +12,8 @@ import { Preinstalls } from "src/libraries/Preinstalls.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { IL1Block } from "src/L2/interfaces/IL1Block.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"; /// @custom:proxied true @@ -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 /// within the superchain. SuperchainWETH can be converted into native ETH on chains that /// do not use a custom gas token. -contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver { +contract SuperchainWETH is WETH98, IERC7802, ISemver { /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.9 - string public constant version = "1.0.0-beta.9"; + /// @custom:semver 1.0.0-beta.10 + string public constant version = "1.0.0-beta.10"; /// @inheritdoc WETH98 function deposit() public payable override { @@ -91,4 +92,10 @@ contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver { 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; + } } diff --git a/packages/contracts-bedrock/src/L2/interfaces/ICrosschainERC20.sol b/packages/contracts-bedrock/src/L2/interfaces/IERC7802.sol similarity index 89% rename from packages/contracts-bedrock/src/L2/interfaces/ICrosschainERC20.sol rename to packages/contracts-bedrock/src/L2/interfaces/IERC7802.sol index 9fbfe8c65f337..469230e822d1b 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/ICrosschainERC20.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IERC7802.sol @@ -1,9 +1,11 @@ // SPDX-License-Identifier: MIT 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. -interface ICrosschainERC20 { +interface IERC7802 is IERC165 { /// @notice Emitted when a crosschain transfer mints tokens. /// @param to Address of the account tokens are being minted for. /// @param amount Amount of tokens minted. diff --git a/packages/contracts-bedrock/src/L2/interfaces/ISuperchainERC20.sol b/packages/contracts-bedrock/src/L2/interfaces/ISuperchainERC20.sol index ce066a27b88c4..029b13d5520ae 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/ISuperchainERC20.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/ISuperchainERC20.sol @@ -2,15 +2,17 @@ pragma solidity ^0.8.0; // 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 { ISemver } from "src/universal/interfaces/ISemver.sol"; /// @title ISuperchainERC20 /// @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 -interface ISuperchainERC20 is ICrosschainERC20, IERC20, ISemver { +interface ISuperchainERC20 is IERC7802, IERC20, ISemver { error Unauthorized(); + function supportsInterface(bytes4 _interfaceId) external view returns (bool); + function __constructor__() external; } diff --git a/packages/contracts-bedrock/src/L2/interfaces/ISuperchainTokenBridge.sol b/packages/contracts-bedrock/src/L2/interfaces/ISuperchainTokenBridge.sol index f2a61d02d555f..af9d7d8d8411d 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/ISuperchainTokenBridge.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/ISuperchainTokenBridge.sol @@ -9,6 +9,7 @@ interface ISuperchainTokenBridge is ISemver { error ZeroAddress(); error Unauthorized(); error InvalidCrossDomainSender(); + error InvalidERC7802(); event SendERC20( address indexed token, address indexed from, address indexed to, uint256 amount, uint256 destination diff --git a/packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol b/packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol index 1935b4d5b0b5e..fa10b237dbb53 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol @@ -2,15 +2,16 @@ pragma solidity ^0.8.0; 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"; -interface ISuperchainWETH is IWETH98, ICrosschainERC20, ISemver { +interface ISuperchainWETH is IWETH98, IERC7802, ISemver { error Unauthorized(); error NotCustomGasToken(); function balanceOf(address src) external view returns (uint256); function withdraw(uint256 _amount) external; + function supportsInterface(bytes4 _interfaceId) external view returns (bool); function __constructor__() external; } diff --git a/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol b/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol index f814c06c76dfa..a9aa3b501618d 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol @@ -6,11 +6,11 @@ import { Test } from "forge-std/Test.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; -import { IERC20Solady as IERC20 } from "src/vendor/interfaces/IERC20Solady.sol"; // Target contract 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 { MockSuperchainERC20Implementation } from "test/mocks/SuperchainERC20Implementation.sol"; @@ -62,7 +62,7 @@ contract SuperchainERC20Test is Test { // Look for the emit of the `CrosschainMint` event vm.expectEmit(address(superchainERC20)); - emit ICrosschainERC20.CrosschainMint(_to, _amount); + emit IERC7802.CrosschainMint(_to, _amount); // Call the `mint` function with the bridge caller vm.prank(SUPERCHAIN_TOKEN_BRIDGE); @@ -105,7 +105,7 @@ contract SuperchainERC20Test is Test { // Look for the emit of the `CrosschainBurn` event vm.expectEmit(address(superchainERC20)); - emit ICrosschainERC20.CrosschainBurn(_from, _amount); + emit IERC7802.CrosschainBurn(_from, _amount); // Call the `burn` function with the bridge caller vm.prank(SUPERCHAIN_TOKEN_BRIDGE); @@ -115,4 +115,20 @@ contract SuperchainERC20Test is Test { assertEq(superchainERC20.totalSupply(), _totalSupplyBefore - _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)); + } } diff --git a/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol b/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol index 2ff6cecfd12b8..1dc01ba0008e3 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol @@ -13,6 +13,7 @@ import { ISuperchainTokenBridge } from "src/L2/interfaces/ISuperchainTokenBridge import { ISuperchainERC20 } from "src/L2/interfaces/ISuperchainERC20.sol"; import { IOptimismSuperchainERC20Factory } from "src/L2/interfaces/IOptimismSuperchainERC20Factory.sol"; import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol"; +import { IERC7802 } from "src/L2/interfaces/IERC7802.sol"; /// @title SuperchainTokenBridgeTest /// @notice Contract for testing the SuperchainTokenBridge contract. @@ -60,6 +61,32 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer { 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` /// event. function testFuzz_sendERC20_succeeds( @@ -137,7 +164,6 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer { /// @notice Tests the `relayERC20` function reverts when the `crossDomainMessageSender` that sent the message is not /// the same SuperchainTokenBridge. function testFuzz_relayERC20_notCrossDomainSender_reverts( - address _token, address _crossDomainMessageSender, uint256 _source, address _to, @@ -159,7 +185,7 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer { // Call the `relayERC20` function with the sender caller 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. diff --git a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol index 9a65544dc7ee2..4d4109b6885a9 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol @@ -12,6 +12,8 @@ import { Preinstalls } from "src/libraries/Preinstalls.sol"; // Interfaces import { IETHLiquidity } from "src/L2/interfaces/IETHLiquidity.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 /// @notice Contract for testing the SuperchainWETH contract. @@ -457,4 +459,20 @@ contract SuperchainWETH_Test is CommonTest { // Assert 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)); + } }