diff --git a/packages/contracts-bedrock/lib/automate b/packages/contracts-bedrock/lib/automate new file mode 160000 index 0000000000000..0117585fea20f --- /dev/null +++ b/packages/contracts-bedrock/lib/automate @@ -0,0 +1 @@ +Subproject commit 0117585fea20ff0cd24fd17bf74a6debaa4d57d2 diff --git a/packages/contracts-bedrock/src/L1/L1StandardBridge.sol b/packages/contracts-bedrock/src/L1/L1StandardBridge.sol index a16a136c4cef0..21c75f04fd90c 100644 --- a/packages/contracts-bedrock/src/L1/L1StandardBridge.sol +++ b/packages/contracts-bedrock/src/L1/L1StandardBridge.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { StandardBridge } from "src/universal/StandardBridge.sol"; import { ISemver } from "src/universal/ISemver.sol"; import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol"; @@ -57,16 +58,6 @@ contract L1StandardBridge is StandardBridge, ISemver { /// @custom:semver 2.2.0 string public constant version = "2.2.0"; - /// @notice The address of L1 USDC address. - // solhint-disable-next-line var-name-mixedcase - address public immutable l1USDC; - - /// @notice The address of L2 USDC address. - address public immutable l2USDC; - - /// @notice The address of caller from Circle. - address public circleCaller; - /// @notice Address of the SuperchainConfig contract. SuperchainConfig public superchainConfig; @@ -74,31 +65,28 @@ contract L1StandardBridge is StandardBridge, ISemver { SystemConfig public systemConfig; /// @notice Constructs the L1StandardBridge contract. - constructor( - address _l1USDC, - address _l2USDC, - address _otherBridge, - ) StandardBridge() { + constructor(address _otherBridge, address _l1USDC, address _l2USDC) StandardBridge() { initialize({ _messenger: CrossDomainMessenger(address(0)), _superchainConfig: SuperchainConfig(address(0)), _systemConfig: SystemConfig(address(0)), - _otherBridge + _otherBridgeAddress: _otherBridge, + _l1USDC: _l1USDC, + _l2USDC: _l2USDC }); - - l1USDC = _l1USDC; - l2USDC = _l2USDC; } /// @notice Initializer. /// @param _messenger Contract for the CrossDomainMessenger on this network. /// @param _superchainConfig Contract for the SuperchainConfig on this network. - /// @param _otherBridge Contract for the other StandardBridge contract. + /// @param _otherBridgeAddress Contract for the other StandardBridge contract. function initialize( CrossDomainMessenger _messenger, SuperchainConfig _superchainConfig, SystemConfig _systemConfig, - StandardBridge _otherBridge + address _otherBridgeAddress, + address _l1USDC, + address _l2USDC ) public initializer @@ -107,7 +95,9 @@ contract L1StandardBridge is StandardBridge, ISemver { systemConfig = _systemConfig; __StandardBridge_init({ _messenger: _messenger, - _otherBridge + _otherBridge: StandardBridge(payable(_otherBridgeAddress)), + _l1USDC: _l1USDC, + _l2USDC: _l2USDC }); } @@ -121,14 +111,13 @@ contract L1StandardBridge is StandardBridge, ISemver { (addr_, decimals_) = systemConfig.gasPayingToken(); } - /// @inheritdoc IUSDCBurnableSourceBridge - function burnAllLockedUSDC() external override { - require(msg.sender == guardian(), "SuperchainConfig: only guardian can burn all USDC"); - // @note Only bridged USDC will be burned. We may refund the rest if possible. - uint256 _balance = totalBridgedUSDC; - totalBridgedUSDC = 0; - - IFiatToken(l1USDC).burn(_balance); + /// @notice Burns all locked USDC if the pbridge is already paused + function burnAllLockedUSDC() external { + require(paused() == true, "Bridge should be paused before burning all locked USDC"); + require(msg.sender == superchainConfig.guardian(), "SuperchainConfig: only guardian can burn all USDC"); + // uint256 _balance = totalBridgedUSDC; + deposits[l1USDC][l2USDC] = 0; + // IERC20(l1USDC).burn(_balance); // check if this needs to be done } /// @custom:legacy @@ -149,7 +138,6 @@ contract L1StandardBridge is StandardBridge, ISemver { ) external virtual - onlyUSDCtoken { _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, msg.sender, _amount, _minGasLimit, _extraData); } @@ -174,7 +162,6 @@ contract L1StandardBridge is StandardBridge, ISemver { ) external virtual - onlyUSDCtoken(l1Token, l2Token) { _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, _to, _amount, _minGasLimit, _extraData); } diff --git a/packages/contracts-bedrock/src/L2/L2StandardBridge.sol b/packages/contracts-bedrock/src/L2/L2StandardBridge.sol index 64fd5afd39cfb..2d326f81aef2f 100644 --- a/packages/contracts-bedrock/src/L2/L2StandardBridge.sol +++ b/packages/contracts-bedrock/src/L2/L2StandardBridge.sol @@ -55,16 +55,18 @@ contract L2StandardBridge is StandardBridge, ISemver { string public constant version = "1.10.0"; /// @notice Constructs the L2StandardBridge contract. - constructor() StandardBridge() { - initialize({ _otherBridge: StandardBridge(payable(address(0))) }); + constructor(address _l1USDC, address _l2USDC) StandardBridge() { + initialize({ _otherBridge: StandardBridge(payable(address(0))), _l1USDC: _l1USDC, _l2USDC: _l2USDC }); } /// @notice Initializer. /// @param _otherBridge Contract for the corresponding bridge on the other chain. - function initialize(StandardBridge _otherBridge) public initializer { + function initialize(StandardBridge _otherBridge, address _l1USDC, address _l2USDC) public initializer { __StandardBridge_init({ _messenger: CrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER), - _otherBridge: _otherBridge + _otherBridge: _otherBridge, + _l1USDC: _l1USDC, + _l2USDC: _l2USDC }); } @@ -90,7 +92,6 @@ contract L2StandardBridge is StandardBridge, ISemver { payable virtual onlyEOA - onlyUSDCtoken { require(isCustomGasToken() == false, "L2StandardBridge: not supported with custom gas token"); _initiateWithdrawal(_l2Token, msg.sender, msg.sender, _amount, _minGasLimit, _extraData); @@ -114,7 +115,6 @@ contract L2StandardBridge is StandardBridge, ISemver { external payable virtual - onlyUSDCtoken { require(isCustomGasToken() == false, "L2StandardBridge: not supported with custom gas token"); _initiateWithdrawal(_l2Token, msg.sender, _to, _amount, _minGasLimit, _extraData); @@ -145,10 +145,8 @@ contract L2StandardBridge is StandardBridge, ISemver { ) internal { - - address l1Token = (_l2Token).l1Token(); + address l1Token = l1USDC; _initiateBridgeERC20(_l2Token, l1Token, _from, _to, _amount, _minGasLimit, _extraData); - } /// @notice Emits the legacy WithdrawalInitiated event followed by the ERC20BridgeInitiated diff --git a/packages/contracts-bedrock/src/universal/StandardBridge.sol b/packages/contracts-bedrock/src/universal/StandardBridge.sol index f95b02c3776d5..e7e93ce8af910 100644 --- a/packages/contracts-bedrock/src/universal/StandardBridge.sol +++ b/packages/contracts-bedrock/src/universal/StandardBridge.sol @@ -23,6 +23,13 @@ abstract contract StandardBridge is Initializable { /// @notice Mapping that stores deposits for a given pair of local and remote tokens. mapping(address => mapping(address => uint256)) public deposits; + /// @notice The address of L1 USDC address. + // solhint-disable-next-line var-name-mixedcase + address public immutable l1USDC; + + /// @notice The address of L2 USDC address. + address public immutable l2USDC; + /// @notice Messenger contract on this domain. /// @custom:network-specific CrossDomainMessenger public messenger; @@ -90,13 +97,17 @@ abstract contract StandardBridge is Initializable { /// @param _otherBridge Contract for the other StandardBridge contract. function __StandardBridge_init( CrossDomainMessenger _messenger, - StandardBridge _otherBridge + StandardBridge _otherBridge, + address _l1USDC, + address _l2USDC ) internal onlyInitializing { messenger = _messenger; otherBridge = _otherBridge; + l1USDC = _l1USDC; + l2USDC = _l2USDC; } /// @notice Returns the address of the custom gas token and the token's decimals. @@ -199,6 +210,10 @@ abstract contract StandardBridge is Initializable { onlyOtherBridge { require(paused() == false, "StandardBridge: paused"); + require( + _isCorrectTokenPair(_localToken, _remoteToken), + "StandardBridge: wrong remote token for Optimism Mintable ERC20 local token" + ); deposits[_localToken][_remoteToken] = deposits[_localToken][_remoteToken] - _amount; IERC20(_localToken).safeTransfer(_to, _amount); @@ -228,6 +243,11 @@ abstract contract StandardBridge is Initializable { internal { require(msg.value == 0, "StandardBridge: cannot send value"); + require(paused() == false, "StandardBridge: paused"); + require( + _isCorrectTokenPair(_localToken, _remoteToken), + "StandardBridge: wrong remote token for Optimism Mintable ERC20 local token" + ); IERC20(_localToken).safeTransferFrom(_from, address(this), _amount); deposits[_localToken][_remoteToken] = deposits[_localToken][_remoteToken] + _amount; @@ -253,6 +273,21 @@ abstract contract StandardBridge is Initializable { }); } + /** + * @notice Checks if the "other token" is the correct pair token for the OptimismMintableERC20. + * Calls can be saved in the future by combining this logic with + * `_isOptimismMintableERC20`. + * + * @param _mintableToken OptimismMintableERC20 to check against. + * @param _otherToken Pair token to check. + * + * @return True if the other token is the correct pair token for the OptimismMintableERC20. + */ + function _isCorrectTokenPair(address _mintableToken, address _otherToken) internal view returns (bool) { + return + ((_mintableToken == l1USDC && _otherToken == l2USDC) || (_mintableToken == l2USDC && _otherToken == l1USDC)); + } + /// @notice Emits the ERC20BridgeInitiated event and if necessary the appropriate legacy /// event when an ERC20 bridge is initiated to the other chain. /// @param _localToken Address of the ERC20 on this chain.