Commit b4768da2 authored by Mark Tyneway's avatar Mark Tyneway

contracts-bedrock: standard bridge single impl

parent ec6dc47b
......@@ -419,14 +419,12 @@ contract Deploy is Deployer {
/// @notice Deploy the L1StandardBridge
function deployL1StandardBridge() broadcast() public returns (address) {
address l1CrossDomainMessengerProxy = mustGetAddress("L1CrossDomainMessengerProxy");
L1StandardBridge bridge = new L1StandardBridge({
_messenger: payable(l1CrossDomainMessengerProxy)
});
L1StandardBridge bridge = new L1StandardBridge();
require(address(bridge.MESSENGER()) == l1CrossDomainMessengerProxy);
require(address(bridge.MESSENGER()) == address(0));
require(address(bridge.messenger()) == address(0));
require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_STANDARD_BRIDGE);
require(address(bridge.otherBridge()) == Predeploys.L2_STANDARD_BRIDGE);
save("L1StandardBridge", address(bridge));
console.log("L1StandardBridge deployed at %s", address(bridge));
......@@ -543,9 +541,13 @@ contract Deploy is Deployer {
}
require(uint256(proxyAdmin.proxyType(l1StandardBridgeProxy)) == uint256(ProxyAdmin.ProxyType.CHUGSPLASH));
proxyAdmin.upgrade({
proxyAdmin.upgradeAndCall({
_proxy: payable(l1StandardBridgeProxy),
_implementation: l1StandardBridge
_implementation: l1StandardBridge,
_data: abi.encodeCall(
L1StandardBridge.initialize,
(L1CrossDomainMessenger(l1CrossDomainMessengerProxy))
)
});
string memory version = L1StandardBridge(payable(l1StandardBridgeProxy)).version();
......@@ -553,7 +555,9 @@ contract Deploy is Deployer {
L1StandardBridge bridge = L1StandardBridge(payable(l1StandardBridgeProxy));
require(address(bridge.MESSENGER()) == l1CrossDomainMessengerProxy);
require(address(bridge.messenger()) == l1CrossDomainMessengerProxy);
require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_STANDARD_BRIDGE);
require(address(bridge.otherBridge()) == Predeploys.L2_STANDARD_BRIDGE);
}
......
......@@ -4,6 +4,7 @@ pragma solidity 0.8.15;
import { Predeploys } from "../libraries/Predeploys.sol";
import { StandardBridge } from "../universal/StandardBridge.sol";
import { Semver } from "../universal/Semver.sol";
import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol";
/// @custom:proxied
/// @title L1StandardBridge
......@@ -76,13 +77,21 @@ contract L1StandardBridge is StandardBridge, Semver {
bytes extraData
);
/// @custom:semver 1.1.1
/// @custom:semver 1.2.0
/// @notice Constructs the L1StandardBridge contract.
/// @param _messenger Address of the L1CrossDomainMessenger.
constructor(address payable _messenger)
Semver(1, 1, 1)
StandardBridge(_messenger, payable(Predeploys.L2_STANDARD_BRIDGE))
{}
constructor()
Semver(1, 2, 0)
StandardBridge(StandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE)))
{
initialize(CrossDomainMessenger(address(0)));
}
/// @notice Initializer
function initialize(CrossDomainMessenger _messenger) public reinitializer(2) {
__StandardBridge_init({
_messenger: _messenger
});
}
/// @notice Allows EOAs to bridge ETH by sending directly to the bridge.
receive() external payable override onlyEOA {
......@@ -212,7 +221,7 @@ contract L1StandardBridge is StandardBridge, Semver {
/// @notice Retrieves the access of the corresponding L2 bridge contract.
/// @return Address of the corresponding L2 bridge contract.
function l2TokenBridge() external view returns (address) {
return address(OTHER_BRIDGE);
return address(_OTHER_BRIDGE);
}
/// @notice Internal function for initiating an ETH deposit.
......
......@@ -5,6 +5,7 @@ import { Predeploys } from "../libraries/Predeploys.sol";
import { StandardBridge } from "../universal/StandardBridge.sol";
import { Semver } from "../universal/Semver.sol";
import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol";
import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol";
/// @custom:proxied
/// @custom:predeploy 0x4200000000000000000000000000000000000010
......@@ -50,13 +51,22 @@ contract L2StandardBridge is StandardBridge, Semver {
bytes extraData
);
/// @custom:semver 1.1.1
/// @custom:semver 1.2.0
/// @notice Constructs the L2StandardBridge contract.
/// @param _otherBridge Address of the L1StandardBridge.
constructor(address payable _otherBridge)
Semver(1, 1, 1)
StandardBridge(payable(Predeploys.L2_CROSS_DOMAIN_MESSENGER), _otherBridge)
{}
constructor(StandardBridge _otherBridge)
Semver(1, 2, 0)
StandardBridge(_otherBridge)
{
initialize();
}
/// @notice Initializer
function initialize() public reinitializer(2) {
__StandardBridge_init({
_messenger: CrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER)
});
}
/// @notice Allows EOAs to bridge ETH by sending directly to the bridge.
receive() external payable override onlyEOA {
......@@ -138,7 +148,7 @@ contract L2StandardBridge is StandardBridge, Semver {
/// @notice Retrieves the access of the corresponding L1 bridge contract.
/// @return Address of the corresponding L1 bridge contract.
function l1TokenBridge() external view returns (address) {
return address(OTHER_BRIDGE);
return address(_OTHER_BRIDGE);
}
/// @custom:legacy
......
......@@ -9,23 +9,21 @@ import { SafeCall } from "../libraries/SafeCall.sol";
import { IOptimismMintableERC20, ILegacyMintableERC20 } from "./IOptimismMintableERC20.sol";
import { CrossDomainMessenger } from "./CrossDomainMessenger.sol";
import { OptimismMintableERC20 } from "./OptimismMintableERC20.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
/// @custom:upgradeable
/// @title StandardBridge
/// @notice StandardBridge is a base contract for the L1 and L2 standard ERC20 bridges. It handles
/// the core bridging logic, including escrowing tokens that are native to the local chain
/// and minting/burning tokens that are native to the remote chain.
abstract contract StandardBridge {
abstract contract StandardBridge is Initializable {
using SafeERC20 for IERC20;
/// @notice The L2 gas limit set when eth is depoisited using the receive() function.
uint32 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 200_000;
/// @notice Messenger contract on this domain.
CrossDomainMessenger public immutable MESSENGER;
/// @notice Corresponding bridge on the other domain.
StandardBridge public immutable OTHER_BRIDGE;
StandardBridge internal immutable _OTHER_BRIDGE;
/// @custom:legacy
/// @custom:spacer messenger
......@@ -40,10 +38,13 @@ abstract contract StandardBridge {
/// @notice Mapping that stores deposits for a given pair of local and remote tokens.
mapping(address => mapping(address => uint256)) public deposits;
/// @notice Messenger contract on this domain.
CrossDomainMessenger internal _MESSENGER;
/// @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades.
/// A gap size of 47 was chosen here, so that the first slot used in a child contract
/// A gap size of 46 was chosen here, so that the first slot used in a child contract
/// would be a multiple of 50.
uint256[47] private __gap;
uint256[46] private __gap;
/// @notice Emitted when an ETH bridge is initiated to the other chain.
/// @param from Address of the sender.
......@@ -115,29 +116,50 @@ abstract contract StandardBridge {
/// @notice Ensures that the caller is a cross-chain message from the other bridge.
modifier onlyOtherBridge() {
require(
msg.sender == address(MESSENGER) &&
MESSENGER.xDomainMessageSender() == address(OTHER_BRIDGE),
msg.sender == address(_MESSENGER) &&
_MESSENGER.xDomainMessageSender() == address(_OTHER_BRIDGE),
"StandardBridge: function can only be called from the other bridge"
);
_;
}
/// @param _messenger Address of CrossDomainMessenger on this network.
/// @param _otherBridge Address of the other StandardBridge contract.
constructor(address payable _messenger, address payable _otherBridge) {
MESSENGER = CrossDomainMessenger(_messenger);
OTHER_BRIDGE = StandardBridge(_otherBridge);
constructor(StandardBridge _otherBridge) {
_OTHER_BRIDGE = _otherBridge;
}
/// @notice Initializer.
/// @param _messenger Address of CrossDomainMessenger on this network.
// solhint-disable-next-line func-name-mixedcase
function __StandardBridge_init(CrossDomainMessenger _messenger) internal onlyInitializing {
_MESSENGER = _messenger;
}
/// @notice Allows EOAs to bridge ETH by sending directly to the bridge.
/// Must be implemented by contracts that inherit.
receive() external payable virtual;
/// @custom:legacy
/// @notice Legacy getter for messenger contract.
/// @notice Getter for messenger contract.
/// @return Messenger contract on this domain.
function messenger() external view returns (CrossDomainMessenger) {
return MESSENGER;
return _MESSENGER;
}
/// @notice Legacy getter for messenger contract.
/// @custom:legacy
function MESSENGER() external view returns (CrossDomainMessenger) {
return _MESSENGER;
}
/// @notice Getter for the remote domain bridge contract.
function otherBridge() external view returns (StandardBridge) {
return _OTHER_BRIDGE;
}
/// @notice Legacy getter for the remote domain bridge contract.
/// @custom:legacy
function OTHER_BRIDGE() external view returns (StandardBridge) {
return _OTHER_BRIDGE;
}
/// @notice Sends ETH to the sender's address on the other chain.
......@@ -245,7 +267,7 @@ abstract contract StandardBridge {
) public payable onlyOtherBridge {
require(msg.value == _amount, "StandardBridge: amount sent does not match amount required");
require(_to != address(this), "StandardBridge: cannot send to self");
require(_to != address(MESSENGER), "StandardBridge: cannot send to messenger");
require(_to != address(_MESSENGER), "StandardBridge: cannot send to messenger");
// Emit the correct events. By default this will be _amount, but child
// contracts may override this function in order to emit legacy events as well.
......@@ -314,8 +336,8 @@ abstract contract StandardBridge {
// contracts may override this function in order to emit legacy events as well.
_emitETHBridgeInitiated(_from, _to, _amount, _extraData);
MESSENGER.sendMessage{ value: _amount }(
address(OTHER_BRIDGE),
_MESSENGER.sendMessage{ value: _amount }(
address(_OTHER_BRIDGE),
abi.encodeWithSelector(
this.finalizeBridgeETH.selector,
_from,
......@@ -361,8 +383,8 @@ abstract contract StandardBridge {
// contracts may override this function in order to emit legacy events as well.
_emitERC20BridgeInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData);
MESSENGER.sendMessage(
address(OTHER_BRIDGE),
_MESSENGER.sendMessage(
address(_OTHER_BRIDGE),
abi.encodeWithSelector(
this.finalizeBridgeERC20.selector,
// Because this call will be executed on the remote chain, we reverse the order of
......
......@@ -7,6 +7,7 @@ import { L2OutputOracle } from "../src/L1/L2OutputOracle.sol";
import { L2ToL1MessagePasser } from "../src/L2/L2ToL1MessagePasser.sol";
import { L1StandardBridge } from "../src/L1/L1StandardBridge.sol";
import { L2StandardBridge } from "../src/L2/L2StandardBridge.sol";
import { StandardBridge } from "../src/universal/StandardBridge.sol";
import { L1ERC721Bridge } from "../src/L1/L1ERC721Bridge.sol";
import { L2ERC721Bridge } from "../src/L2/L2ERC721Bridge.sol";
import { OptimismMintableERC20Factory } from "../src/universal/OptimismMintableERC20Factory.sol";
......@@ -414,21 +415,24 @@ contract Bridge_Initializer is Messenger_Initializer {
abi.encode(true)
);
vm.startPrank(multisig);
proxy.setCode(address(new L1StandardBridge(payable(address(L1Messenger)))).code);
proxy.setCode(address(new L1StandardBridge()).code);
vm.clearMockedCalls();
address L1Bridge_Impl = proxy.getImplementation();
vm.stopPrank();
L1Bridge = L1StandardBridge(payable(address(proxy)));
L1Bridge.initialize({
_messenger: L1Messenger
});
vm.label(address(proxy), "L1StandardBridge_Proxy");
vm.label(address(L1Bridge_Impl), "L1StandardBridge_Impl");
// Deploy the L2StandardBridge, move it to the correct predeploy
// address and then initialize it
L2StandardBridge l2B = new L2StandardBridge(payable(proxy));
vm.etch(Predeploys.L2_STANDARD_BRIDGE, address(l2B).code);
vm.etch(Predeploys.L2_STANDARD_BRIDGE, address(new L2StandardBridge(StandardBridge(payable(proxy)))).code);
L2Bridge = L2StandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE));
L2Bridge.initialize();
// Set up the L2 mintable token factory
OptimismMintableERC20Factory factory = new OptimismMintableERC20Factory(
......
......@@ -14,7 +14,7 @@ import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
/// internal functions so they can be more easily tested directly.
contract StandardBridgeTester is StandardBridge {
constructor(address payable _messenger, address payable _otherBridge)
StandardBridge(_messenger, _otherBridge)
StandardBridge(StandardBridge(_otherBridge))
{}
function isOptimismMintableERC20(address _token) external view returns (bool) {
......
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