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

refactor: upgradeable adapters #122

Merged
merged 12 commits into from
Jul 29, 2024
19 changes: 15 additions & 4 deletions src/contracts/L1OpUSDCBridgeAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,26 @@ contract L1OpUSDCBridgeAdapter is IL1OpUSDCBridgeAdapter, OpUSDCBridgeAdapter {
* @param _usdc The address of the USDC Contract to be used by the adapter
* @param _messenger The address of the L1 messenger
* @param _linkedAdapter The address of the linked adapter
* @param _owner The address of the owner of the contract
* @dev The constructor is only used to initialize the OpUSDCBridgeAdapter immutable variables
*/
constructor(
address _usdc,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure this contract inherits the UUPSUpgradeable from openzeppelin-upgradeable

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or does it not need it because we inherit in the base contract?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it is inherited on the base contract

address _messenger,
address _linkedAdapter,
address _owner
) OpUSDCBridgeAdapter(_usdc, _messenger, _linkedAdapter, _owner) {}
address _linkedAdapter
) OpUSDCBridgeAdapter(_usdc, _messenger, _linkedAdapter) {}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add the __gap to the L1 and L2 adapters as well, it will make maintanence should a chain op ever want to upgrade much easier

/**
* @notice Sets the owner of the contract
* @param _owner The address of the owner
* @dev This function needs only used during the deployment of the proxy contract, and it is disabled for the
* implementation contract
*/
function initialize(address _owner) external virtual override initializer {
__Ownable_init(_owner);
string memory _name = 'L1OpUSDCBridgeAdapter';
string memory _version = '1.0.0';
__EIP712_init(_name, _version);
}

/*///////////////////////////////////////////////////////////////
MIGRATION
Expand Down
17 changes: 10 additions & 7 deletions src/contracts/L1OpUSDCFactory.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import {ERC1967Proxy} from '@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol';
import {L1OpUSDCBridgeAdapter} from 'contracts/L1OpUSDCBridgeAdapter.sol';
import {IL1OpUSDCFactory} from 'interfaces/IL1OpUSDCFactory.sol';
import {IL2OpUSDCDeploy} from 'interfaces/IL2OpUSDCDeploy.sol';

import {IUSDC} from 'interfaces/external/IUSDC.sol';
import {CrossChainDeployments} from 'libraries/CrossChainDeployments.sol';
import {OpUSDCBridgeAdapter} from 'src/contracts/universal/OpUSDCBridgeAdapter.sol';

/**
* @title L1OpUSDCFactory
Expand All @@ -30,8 +31,8 @@ contract L1OpUSDCFactory is IL1OpUSDCFactory {
/// @dev Used to check the first init tx doesn't match it since it is already defined in the L2 factory contract
bytes4 internal constant _INITIALIZE_SELECTOR = 0x3357162b;

/// @notice The L2 Adapter is the second contract to be deployed on the L2 factory so its nonce is 2
uint256 internal constant _L2_ADAPTER_DEPLOYMENT_NONCE = 2;
/// @notice The L2 Adapter proxy is the third of the L2 deployments so at that moment the nonce is 3
uint256 internal constant _L2_ADAPTER_DEPLOYMENT_NONCE = 3;

/// @inheritdoc IL1OpUSDCFactory
IUSDC public immutable USDC;
Expand Down Expand Up @@ -85,9 +86,9 @@ contract L1OpUSDCFactory is IL1OpUSDCFactory {
if (bytes4(_l2Deployments.usdcInitTxs[0]) == _INITIALIZE_SELECTOR) revert IL1OpUSDCFactory_NoInitializeTx();

// Update the salt counter so the L2 factory is deployed with a different salt to a different address and get it
uint256 _currentNonce = ++deploymentsSaltCounter;
uint256 _currentNonce = deploymentsSaltCounter += 2;

// Precalculate the l1 adapter
// Precalculate the l1 adapter proxy address
_l1Adapter = CrossChainDeployments.precalculateCreateAddress(address(this), _currentNonce);

// Get the L1 USDC naming and decimals to ensure they are the same on the L2, guaranteeing the same standard
Expand All @@ -112,8 +113,10 @@ contract L1OpUSDCFactory is IL1OpUSDCFactory {

// Precalculate the L2 adapter address
_l2Adapter = CrossChainDeployments.precalculateCreateAddress(_l2Factory, _L2_ADAPTER_DEPLOYMENT_NONCE);
// Deploy the L1 adapter
address(new L1OpUSDCBridgeAdapter(address(USDC), _l1Messenger, _l2Adapter, _l1AdapterOwner));

// Deploy L1 Adapter implementation and proxy, initializing it with the owner
address _l1AdapterImpl = address(new L1OpUSDCBridgeAdapter(address(USDC), _l1Messenger, _l2Adapter));
new ERC1967Proxy(_l1AdapterImpl, abi.encodeCall(OpUSDCBridgeAdapter.initialize, _l1AdapterOwner));

emit ProtocolDeployed(_l1Adapter, _l2Factory, _l2Adapter);
}
Expand Down
24 changes: 18 additions & 6 deletions src/contracts/L2OpUSDCBridgeAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ contract L2OpUSDCBridgeAdapter is IL2OpUSDCBridgeAdapter, OpUSDCBridgeAdapter {
bytes4 internal constant _UPDATE_MASTER_MINTER_SELECTOR = 0xaa20e1e4;

/// @inheritdoc IL2OpUSDCBridgeAdapter
FallbackProxyAdmin public immutable FALLBACK_PROXY_ADMIN;
// solhint-disable-next-line var-name-mixedcase
FallbackProxyAdmin public FALLBACK_PROXY_ADMIN;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

linter

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer immutable, just change it to fallback_proxy_admin, wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it behaves as immutable, just that I need to set it on the initialize() function instead of on constructor. I added a note and the ignore flag.
Wdyt?


/// @inheritdoc IL2OpUSDCBridgeAdapter
address public roleCaller;
Expand All @@ -58,13 +59,24 @@ contract L2OpUSDCBridgeAdapter is IL2OpUSDCBridgeAdapter, OpUSDCBridgeAdapter {
constructor(
address _usdc,
address _messenger,
address _linkedAdapter,
address _owner
) OpUSDCBridgeAdapter(_usdc, _messenger, _linkedAdapter, _owner) {
FALLBACK_PROXY_ADMIN = new FallbackProxyAdmin(_usdc);
}
address _linkedAdapter
) OpUSDCBridgeAdapter(_usdc, _messenger, _linkedAdapter) {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

be sure to add __gap variable to reserve slots for new implementations on all three contracts

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, good catch!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uint256[50] private __gap; seems enough, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea should be fine

/* solhint-enable no-unused-vars */

/**
* @notice Sets the owner of the contract
* @param _owner The address of the owner
* @dev This function needs only used during the deployment of the proxy contract, and it is disabled for the
* implementation contract
*/
function initialize(address _owner) external virtual override initializer {
__Ownable_init(_owner);
string memory _name = 'L1OpUSDCBridgeAdapter';
string memory _version = '1.0.0';
__EIP712_init(_name, _version);
FALLBACK_PROXY_ADMIN = new FallbackProxyAdmin(USDC);
}

/*///////////////////////////////////////////////////////////////
MIGRATION
///////////////////////////////////////////////////////////////*/
Expand Down
12 changes: 7 additions & 5 deletions src/contracts/L2OpUSDCDeploy.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import {ERC1967Proxy} from '@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol';
import {L2OpUSDCBridgeAdapter} from 'contracts/L2OpUSDCBridgeAdapter.sol';
import {USDC_PROXY_CREATION_CODE} from 'contracts/utils/USDCProxyCreationCode.sol';
import {IL2OpUSDCDeploy} from 'interfaces/IL2OpUSDCDeploy.sol';
import {IUSDC} from 'interfaces/external/IUSDC.sol';
import {OpUSDCBridgeAdapter} from 'src/contracts/universal/OpUSDCBridgeAdapter.sol';

/**
* @title L2OpUSDCDeploy
Expand Down Expand Up @@ -37,13 +39,13 @@ contract L2OpUSDCDeploy is IL2OpUSDCDeploy {
// Deploy USDC proxy
bytes memory _usdcProxyCArgs = abi.encode(_usdcImplAddr);
bytes memory _usdcProxyInitCode = bytes.concat(USDC_PROXY_CREATION_CODE, _usdcProxyCArgs);
(address _usdcProxy) = _deployCreate(_usdcProxyInitCode);
address _usdcProxy = _deployCreate(_usdcProxyInitCode);
emit USDCProxyDeployed(_usdcProxy);

// Deploy L2 Adapter
bytes memory _l2AdapterCArgs = abi.encode(_usdcProxy, _L2_MESSENGER, _l1Adapter, _l2AdapterOwner);
bytes memory _l2AdapterInitCode = bytes.concat(type(L2OpUSDCBridgeAdapter).creationCode, _l2AdapterCArgs);
(address _l2Adapter) = _deployCreate(_l2AdapterInitCode);
// Deploy L2 Adapter implementation and proxy, initializing it with the owner
address _l2AdapterImpl = address(new L2OpUSDCBridgeAdapter(_usdcProxy, _L2_MESSENGER, _l1Adapter));
address _l2Adapter =
address(new ERC1967Proxy(_l2AdapterImpl, abi.encodeCall(OpUSDCBridgeAdapter.initialize, _l2AdapterOwner)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could be a problem when the contracts are verfied on etherscan, the name of the contract will be ERC1967Proxy ... I would wrap the ERC1967Proxy into something like contact AdapterProxy is ERC1967Proxy

image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we arent changing anything lets leave it as is, circle uses custom location for their proxy, we dont, it will be easier to read since we dont change anything logic wise

emit L2AdapterDeployed(_l2Adapter);

// Deploy the FallbackProxyAdmin internally in the L2 Adapter to keep it unique
Expand Down
35 changes: 22 additions & 13 deletions src/contracts/universal/OpUSDCBridgeAdapter.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

import {EIP712} from '@openzeppelin/contracts/utils/cryptography/EIP712.sol';
import {OwnableUpgradeable} from '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
import {UUPSUpgradeable} from '@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol';
import {EIP712Upgradeable} from '@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol';
import {MessageHashUtils} from '@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol';
import {SignatureChecker} from '@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol';
import {IOpUSDCBridgeAdapter} from 'interfaces/IOpUSDCBridgeAdapter.sol';

abstract contract OpUSDCBridgeAdapter is IOpUSDCBridgeAdapter, Ownable, EIP712 {
abstract contract OpUSDCBridgeAdapter is UUPSUpgradeable, OwnableUpgradeable, EIP712Upgradeable, IOpUSDCBridgeAdapter {
using MessageHashUtils for bytes32;
using SignatureChecker for address;

Expand All @@ -25,6 +25,9 @@ abstract contract OpUSDCBridgeAdapter is IOpUSDCBridgeAdapter, Ownable, EIP712 {
/// @inheritdoc IOpUSDCBridgeAdapter
address public immutable MESSENGER;

/// @notice Reserve 50 storage slots to be safe on future upgrades
uint256[50] internal __gap;

/// @inheritdoc IOpUSDCBridgeAdapter
Status public messengerStatus;

Expand All @@ -39,20 +42,21 @@ abstract contract OpUSDCBridgeAdapter is IOpUSDCBridgeAdapter, Ownable, EIP712 {
* @param _usdc The address of the USDC Contract to be used by the adapter
* @param _messenger The address of the messenger contract
* @param _linkedAdapter The address of the linked adapter
* @param _owner The address of the owner of the contract
*/
constructor(
address _usdc,
address _messenger,
address _linkedAdapter,
// solhint-disable-next-line no-unused-vars
address _owner
) Ownable(_owner) EIP712('OpUSDCBridgeAdapter', '1.0.0') {
// solhint-disable-next-line no-unused-vars
constructor(address _usdc, address _messenger, address _linkedAdapter) {
USDC = _usdc;
MESSENGER = _messenger;
LINKED_ADAPTER = _linkedAdapter;
_disableInitializers();
}

/**
* @notice Initialize the contract
* @param _owner The owner of the contract
*/
function initialize(address _owner) external virtual initializer {}

/*///////////////////////////////////////////////////////////////
MESSAGING
///////////////////////////////////////////////////////////////*/
Expand Down Expand Up @@ -108,14 +112,19 @@ abstract contract OpUSDCBridgeAdapter is IOpUSDCBridgeAdapter, Ownable, EIP712 {
userNonces[msg.sender][_nonce] = true;
}

/**
* @notice Checks the caller is the owner to authorize the upgrade
*/
function _authorizeUpgrade(address) internal virtual override onlyOwner {}

/**
* @notice Check the signature of a message
* @param _signer the address that signed the message
* @param _messageHash the hash of the message that was signed
* @param _signature the signature of the message
*/
function _checkSignature(address _signer, bytes32 _messageHash, bytes memory _signature) internal view {
// Uses the EIP712 typed data hash
// Uses the EIP712Upgradeable typed data hash
_messageHash = _hashTypedDataV4(_messageHash);

if (!_signer.isValidSignatureNow(_messageHash, _signature)) revert IOpUSDCBridgeAdapter_InvalidSignature();
Expand Down
2 changes: 2 additions & 0 deletions src/interfaces/IL2OpUSDCBridgeAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ interface IL2OpUSDCBridgeAdapter {
* such as mint and burn between others. Because of this, the FallbackProxyAdmin contract is used as a middleware,
* being controlled by the L2OpUSDCBridgeAdapter contract and allowing to call the admin functions through it while
* also being able to call the fallback function of the USDC proxy.
* @dev Declared with immutable notation even though it is not defined on the constructor because it is set on the
* `initialize` function which replicates the behavior of the constructor.
*/
// solhint-disable-next-line func-name-mixedcase
function FALLBACK_PROXY_ADMIN() external view returns (FallbackProxyAdmin _fallbackProxyAdmin);
Expand Down
32 changes: 17 additions & 15 deletions src/interfaces/IOpUSDCBridgeAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,6 @@
pragma solidity 0.8.25;

interface IOpUSDCBridgeAdapter {
/**
* @notice The struct to hold the data for a bridge message with signature
* @param to The target address on the destination chain
* @param amount The amount of tokens to send
* @param deadline The deadline for the message to be executed
* @param nonce The nonce of the user
* @param minGasLimit The minimum gas limit for the message to be executed
*/
struct BridgeMessage {
address to;
uint256 amount;
uint256 deadline;
uint256 nonce;
uint32 minGasLimit;
}
/*///////////////////////////////////////////////////////////////
ENUMS
///////////////////////////////////////////////////////////////*/
Expand All @@ -34,6 +19,23 @@ interface IOpUSDCBridgeAdapter {
Upgrading,
Deprecated
}

/**
* @notice The struct to hold the data for a bridge message with signature
* @param to The target address on the destination chain
* @param amount The amount of tokens to send
* @param deadline The deadline for the message to be executed
* @param nonce The nonce of the user
* @param minGasLimit The minimum gas limit for the message to be executed
*/
struct BridgeMessage {
address to;
uint256 amount;
uint256 deadline;
uint256 nonce;
uint32 minGasLimit;
}

/*///////////////////////////////////////////////////////////////
EVENTS
///////////////////////////////////////////////////////////////*/
Expand Down
24 changes: 12 additions & 12 deletions test/integration/Factories.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ contract Integration_Factories is IntegrationBase {
l1Factory.deploy(address(OPTIMISM_L1_MESSENGER), _owner, 'Optimism', _l2Deployments);

// Check the adapter was properly deployed on L1
assertEq(IOpUSDCBridgeAdapter(_l1Adapter).USDC(), address(MAINNET_USDC));
assertEq(IOpUSDCBridgeAdapter(_l1Adapter).MESSENGER(), address(OPTIMISM_L1_MESSENGER));
assertEq(IOpUSDCBridgeAdapter(_l1Adapter).LINKED_ADAPTER(), _l2Adapter);
assertEq(IOpUSDCBridgeAdapter(_l1Adapter).USDC(), address(MAINNET_USDC), '1');
assertEq(IOpUSDCBridgeAdapter(_l1Adapter).MESSENGER(), address(OPTIMISM_L1_MESSENGER), '2');
assertEq(IOpUSDCBridgeAdapter(_l1Adapter).LINKED_ADAPTER(), _l2Adapter, '3');
assertEq(Ownable(_l1Adapter).owner(), _owner);

bytes32 _salt = bytes32(l1Factory.deploymentsSaltCounter());
Expand All @@ -49,19 +49,19 @@ contract Integration_Factories is IntegrationBase {

// Check the adapter was properly deployed on L2
IUSDC _l2Usdc = IUSDC(IOpUSDCBridgeAdapter(_l2Adapter).USDC());
assertEq(IOpUSDCBridgeAdapter(_l2Adapter).MESSENGER(), address(L2_MESSENGER));
assertEq(IOpUSDCBridgeAdapter(_l2Adapter).LINKED_ADAPTER(), _l1Adapter);
assertEq(Ownable(_l2Adapter).owner(), _owner);
assertEq(IOpUSDCBridgeAdapter(_l2Adapter).MESSENGER(), address(L2_MESSENGER), '4');
assertEq(IOpUSDCBridgeAdapter(_l2Adapter).LINKED_ADAPTER(), _l1Adapter, '5');
assertEq(Ownable(_l2Adapter).owner(), _owner, '6');

// Check the L2 factory was deployed
assertGt(_l2Factory.code.length, 0);
assertGt(_l2Factory.code.length, 0, '7');

// Check the USDC was properly deployed on L2
assertEq(_l2Usdc.name(), 'Bridged USDC (Optimism)');
assertEq(_l2Usdc.symbol(), _usdcSymbol);
assertEq(_l2Usdc.decimals(), _usdcDecimals);
assertEq(_l2Usdc.currency(), _usdcCurrency);
assertGt(_l2Usdc.implementation().code.length, 0);
assertEq(_l2Usdc.name(), 'Bridged USDC (Optimism)', '8');
assertEq(_l2Usdc.symbol(), _usdcSymbol, '9');
assertEq(_l2Usdc.decimals(), _usdcDecimals, '10');
assertEq(_l2Usdc.currency(), _usdcCurrency, '11');
assertGt(_l2Usdc.implementation().code.length, 0, '12');

// Check the USDC permissions and allowances were properly set
assertEq(_l2Usdc.admin(), address(IL2OpUSDCBridgeAdapter(_l2Adapter).FALLBACK_PROXY_ADMIN()));
Expand Down
26 changes: 21 additions & 5 deletions test/integration/L1OpUSDCBridgeAdapter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import {IntegrationBase} from './IntegrationBase.sol';
import {IOpUSDCBridgeAdapter} from 'interfaces/IOpUSDCBridgeAdapter.sol';

contract Integration_Bridging is IntegrationBase {
string internal constant _NAME = 'L1OpUSDCBridgeAdapter';
string internal constant _VERSION = '1.0.0';

/**
* @notice Test the bridging process from L1 -> L2
*/
Expand Down Expand Up @@ -92,7 +95,16 @@ contract Integration_Bridging is IntegrationBase {
MAINNET_USDC.approve(address(l1Adapter), _amount);
uint256 _deadline = block.timestamp + 1 days;
bytes memory _signature = _generateSignature(
_signerAd, _amount, _deadline, _MIN_GAS_LIMIT, _USER_NONCE, _signerAd, _signerPk, address(l1Adapter)
_NAME,
_VERSION,
_signerAd,
_amount,
_deadline,
_MIN_GAS_LIMIT,
_USER_NONCE,
_signerAd,
_signerPk,
address(l1Adapter)
);

// Different address can execute the message
Expand Down Expand Up @@ -136,7 +148,7 @@ contract Integration_Bridging is IntegrationBase {
// Changing to `to` param to _user but we call it with _signerAd
uint256 _deadline = block.timestamp + 1 days;
bytes memory _signature = _generateSignature(
_user, _amount, _deadline, _MIN_GAS_LIMIT, _USER_NONCE, _signerAd, _signerPk, address(l1Adapter)
_NAME, _VERSION, _user, _amount, _deadline, _MIN_GAS_LIMIT, _USER_NONCE, _signerAd, _signerPk, address(l1Adapter)
);

// Cancel the signature
Expand All @@ -153,8 +165,8 @@ contract Integration_Bridging is IntegrationBase {
* @notice Test signature message reverts with incorrect signature
*/
function test_bridgeFromL1WithIncorrectSignature() public {
(address _signerAd, uint256 _signerPk) = makeAddrAndKey('signer');
vm.selectFork(mainnet);
(address _signerAd, uint256 _signerPk) = makeAddrAndKey('signer');

// We need to do this instead of `deal` because deal doesnt change `totalSupply` state
vm.prank(MAINNET_USDC.masterMinter());
Expand All @@ -167,13 +179,17 @@ contract Integration_Bridging is IntegrationBase {
// Changing to `to` param to _user but we call it with _signerAd
uint256 _deadline = block.timestamp + 1 days;
bytes memory _signature = _generateSignature(
_user, _amount, _deadline, _MIN_GAS_LIMIT, _USER_NONCE, _signerAd, _signerPk, address(l1Adapter)
_NAME, _VERSION, _user, _amount, _deadline, _MIN_GAS_LIMIT, _USER_NONCE, _signerAd, _signerPk, address(l1Adapter)
);

// Different address can execute the message
vm.startPrank(_user);
vm.expectRevert(IOpUSDCBridgeAdapter.IOpUSDCBridgeAdapter_InvalidSignature.selector);
/// NOTE: Didn't us `vm.expectRevert(IOpUSDCBridgeAdapter.IOpUSDCBridgeAdapter_InvalidSignature.selector)` because
/// it reverts with that error, but then the test fails because of a foundry issue with the error message
/// `contract signer does not exist`, which is not true.
vm.expectRevert();
l1Adapter.sendMessage(_signerAd, _signerAd, _amount, _signature, _USER_NONCE, _deadline, _MIN_GAS_LIMIT);

vm.stopPrank();
}
}
Expand Down
Loading