Commit 49d33b08 authored by smartcontracts's avatar smartcontracts Committed by GitHub

fix(ctb): universal contract comments/errs/events (#2940)

Standardizes the comments, errors, and events for all contracts within
the /universal folder.
parent 297af083
---
'@eth-optimism/contracts-bedrock': patch
---
Standardizes comments, errors, and events for contracts in the /universal package
......@@ -103,18 +103,18 @@ OVM_ETH_Test:test_transfer() (gas: 10726)
OVM_ETH_Test:test_transferFrom() (gas: 13008)
OptimismMintableERC20_Test:test_bridge() (gas: 9785)
OptimismMintableERC20_Test:test_burn() (gas: 52791)
OptimismMintableERC20_Test:test_burnRevertsFromNotBridge() (gas: 13211)
OptimismMintableERC20_Test:test_burnRevertsFromNotBridge() (gas: 13241)
OptimismMintableERC20_Test:test_erc165_supportsInterface() (gas: 7828)
OptimismMintableERC20_Test:test_l1Token() (gas: 9779)
OptimismMintableERC20_Test:test_l2Bridge() (gas: 9790)
OptimismMintableERC20_Test:test_mint() (gas: 65687)
OptimismMintableERC20_Test:test_mintRevertsFromNotBridge() (gas: 13235)
OptimismMintableERC20_Test:test_mintRevertsFromNotBridge() (gas: 13265)
OptimismMintableERC20_Test:test_remoteToken() (gas: 9784)
OptimismMintableTokenFactory_Test:test_bridge() (gas: 9707)
OptimismMintableTokenFactory_Test:test_createStandardL2Token() (gas: 1100125)
OptimismMintableTokenFactory_Test:test_createStandardL2TokenSameTwice() (gas: 2181161)
OptimismMintableTokenFactory_Test:test_createStandardL2TokenShouldRevertIfRemoteIsZero() (gas: 9374)
OptimismMintableTokenFactory_Test:test_initializeShouldRevert() (gas: 12696)
OptimismMintableTokenFactory_Test:test_createStandardL2Token() (gas: 1115359)
OptimismMintableTokenFactory_Test:test_createStandardL2TokenSameTwice() (gas: 2211629)
OptimismMintableTokenFactory_Test:test_createStandardL2TokenShouldRevertIfRemoteIsZero() (gas: 9407)
OptimismMintableTokenFactory_Test:test_initializeShouldRevert() (gas: 12732)
OptimismPortalUpgradeable_Test:test_cannotInitImpl() (gas: 10936)
OptimismPortalUpgradeable_Test:test_cannotInitProxy() (gas: 15934)
OptimismPortalUpgradeable_Test:test_initValuesOnProxy() (gas: 16034)
......@@ -137,12 +137,12 @@ OptimismPortal_Test:test_simple_isOutputFinalized() (gas: 23877)
Proxy_Test:test_clashingFunctionSignatures() (gas: 101427)
Proxy_Test:test_implementationKey() (gas: 20942)
Proxy_Test:test_implementationProxyCallIfNotAdmin() (gas: 30021)
Proxy_Test:test_implementationZeroAddress() (gas: 48006)
Proxy_Test:test_implementationZeroAddress() (gas: 48007)
Proxy_Test:test_itDelegatesToTheImplementation() (gas: 45173)
Proxy_Test:test_ownerKey() (gas: 19113)
Proxy_Test:test_ownerProxyCallIfNotAdmin() (gas: 34733)
Proxy_Test:test_payableUpgradeToAndCall() (gas: 53887)
Proxy_Test:test_revertUpgradeToAndCall() (gas: 104501)
Proxy_Test:test_revertUpgradeToAndCall() (gas: 104654)
Proxy_Test:test_upgradeToAndCall() (gas: 125238)
Proxy_Test:test_zeroAddressCaller() (gas: 14758)
ProxyAdmin_Test:test_chugsplashChangeProxyAdmin() (gas: 35647)
......@@ -155,7 +155,7 @@ ProxyAdmin_Test:test_delegateResolvedGetProxyAdmin() (gas: 17685)
ProxyAdmin_Test:test_delegateResolvedGetProxyImplementation() (gas: 62016)
ProxyAdmin_Test:test_delegateResolvedUpgrade() (gas: 58422)
ProxyAdmin_Test:test_delegateResolvedUpgradeAndCall() (gas: 97948)
ProxyAdmin_Test:test_erc1967ChangeProxyAdmin() (gas: 33862)
ProxyAdmin_Test:test_erc1967ChangeProxyAdmin() (gas: 33863)
ProxyAdmin_Test:test_erc1967GetProxyAdmin() (gas: 15672)
ProxyAdmin_Test:test_erc1967GetProxyImplementation() (gas: 52124)
ProxyAdmin_Test:test_erc1967Upgrade() (gas: 50036)
......
......@@ -41,7 +41,7 @@ contract OptimismMintableERC20_Test is Bridge_Initializer {
function test_mintRevertsFromNotBridge() external {
// NOT the bridge
vm.expectRevert("Only L2 Bridge can mint and burn");
vm.expectRevert("OptimismMintableERC20: only bridge can mint and burn");
vm.prank(address(alice));
L2Token.mint(alice, 100);
}
......@@ -61,7 +61,7 @@ contract OptimismMintableERC20_Test is Bridge_Initializer {
function test_burnRevertsFromNotBridge() external {
// NOT the bridge
vm.expectRevert("Only L2 Bridge can mint and burn");
vm.expectRevert("OptimismMintableERC20: only bridge can mint and burn");
vm.prank(address(alice));
L2Token.burn(alice, 100);
}
......
......@@ -17,7 +17,7 @@ contract OptimismMintableTokenFactory_Test is Bridge_Initializer {
}
function test_initializeShouldRevert() external {
vm.expectRevert("Already initialized.");
vm.expectRevert("OptimismMintableTokenFactory: already initialized");
L2TokenFactory.initialize(address(L1Bridge));
}
......@@ -73,7 +73,7 @@ contract OptimismMintableTokenFactory_Test is Bridge_Initializer {
function test_createStandardL2TokenShouldRevertIfRemoteIsZero() external {
address remote = address(0);
vm.expectRevert("Must provide L1 token address");
vm.expectRevert("OptimismMintableTokenFactory: must provide remote token address");
L2TokenFactory.createStandardL2Token(remote, "Beep", "BOOP");
}
}
......@@ -184,7 +184,7 @@ contract Proxy_Test is Test {
// Set the new SimpleStorage as the implementation
// and call. This reverts because the calldata doesn't
// match a function on the implementation.
vm.expectRevert();
vm.expectRevert("Proxy: delegatecall to new implementation contract failed");
vm.prank(alice);
proxy.upgradeToAndCall(
address(simpleStorage),
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
// solhint-disable max-line-length
/* Library Imports */
import { Lib_DefaultValues } from "../libraries/Lib_DefaultValues.sol";
import { CrossDomainHashing } from "../libraries/Lib_CrossDomainHashing.sol";
/* External Imports */
import {
OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
......@@ -18,22 +11,31 @@ import {
ReentrancyGuardUpgradeable
} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import { ExcessivelySafeCall } from "excessively-safe-call/src/ExcessivelySafeCall.sol";
// solhint-enable max-line-length
import { Lib_DefaultValues } from "../libraries/Lib_DefaultValues.sol";
import { CrossDomainHashing } from "../libraries/Lib_CrossDomainHashing.sol";
/**
* @title CrossDomainMessenger
* @dev The CrossDomainMessenger contract delivers messages between two layers.
* @notice CrossDomainMessenger is a base contract that provides the core logic for the L1 and L2
* cross-chain messenger contracts. It's designed to be a universal interface that only
* needs to be extended slightly to provide low-level message passing functionality on each
* chain it's deployed on. Currently only designed for message passing between two paired
* chains and does not support one-to-many interactions.
*/
abstract contract CrossDomainMessenger is
OwnableUpgradeable,
PausableUpgradeable,
ReentrancyGuardUpgradeable
{
/**********
* Events *
**********/
/**
* @notice Emitted whenever a message is sent to the other chain.
*
* @param target Address of the recipient of the message.
* @param sender Address of the sender of the message.
* @param message Message to trigger the recipient address with.
* @param messageNonce Unique nonce attached to the message.
* @param gasLimit Minimum gas limit that the message can be executed with.
*/
event SentMessage(
address indexed target,
address sender,
......@@ -42,73 +44,112 @@ abstract contract CrossDomainMessenger is
uint256 gasLimit
);
/**
* @notice Emitted whenever a message is successfully relayed on this chain.
*
* @param msgHash Hash of the message that was relayed.
*/
event RelayedMessage(bytes32 indexed msgHash);
/**
* @notice Emitted whenever a message fails to be relayed on this chain.
*
* @param msgHash Hash of the message that failed to be relayed.
*/
event FailedRelayedMessage(bytes32 indexed msgHash);
/*************
* Constants *
*************/
/**
* @notice Current message version identifier.
*/
uint16 public constant MESSAGE_VERSION = 1;
/**
* @notice Dynamic overhead applied to the base gas for a message.
*/
uint32 public constant MIN_GAS_DYNAMIC_OVERHEAD = 1;
/**
* @notice Constant overhead added to the base gas for a message.
*/
uint32 public constant MIN_GAS_CONSTANT_OVERHEAD = 100_000;
/// @notice Minimum amount of gas required prior to relaying a message.
/**
* @notice Minimum amount of gas required to relay a message.
*/
uint256 internal constant RELAY_GAS_REQUIRED = 45_000;
/// @notice Amount of gas held in reserve for accounting after relaying a message.
/**
* @notice Amount of gas held in reserve to guarantee that relay execution completes.
*/
uint256 internal constant RELAY_GAS_BUFFER = RELAY_GAS_REQUIRED - 5000;
/*************
* Variables *
*************/
/// @notice Mapping of message hash to boolean success value.
/**
* @notice Mapping of message hashes to boolean receipt values. Note that a message will only
* be present in this mapping if it failed to be relayed on this chain at least once.
* If a message is successfully relayed on the first attempt, then it will only be
* present within the successfulMessages mapping.
*/
mapping(bytes32 => bool) public successfulMessages;
/// @notice Current x-domain message sender.
/**
* @notice Address of the sender of the currently executing message on the other chain. If the
* value of this variable is the default value (0x00000000...dead) then no message is
* currently being executed. Use the xDomainMessageSender getter which will throw an
* error if this is the case.
*/
address internal xDomainMsgSender;
/// @notice Nonce for the next message to be sent.
/**
* @notice Nonce for the next message to be sent, without the message version applied. Use the
* messageNonce getter which will insert the message version into the nonce to give you
* the actual nonce to be used for the message.
*/
uint256 internal msgNonce;
/// @notice Address of the CrossDomainMessenger on the other chain.
/**
* @notice Address of the paired CrossDomainMessenger contract on the other chain.
*/
address public otherMessenger;
/// @notice Mapping of message hash to boolean receipt value.
/**
* @notice Mapping of message hashes to boolean receipt values. Note that a message will only
* be present in this mapping if it failed to be relayed on this chain at least once.
* If a message is successfully relayed on the first attempt, then it will only be
* present within the successfulMessages mapping.
*/
mapping(bytes32 => bool) public receivedMessages;
/// @notice Blocked system addresses that cannot be called (for security reasons).
/**
* @notice Mapping of blocked system addresses. Note that this is NOT a mapping of blocked user
* addresses and cannot be used to prevent users from sending or receiving messages.
* This is ONLY used to prevent the execution of messages to specific system addresses
* that could cause security issues, e.g., having the CrossDomainMessenger send
* messages to itself.
*/
mapping(address => bool) public blockedSystemAddresses;
/********************
* Public Functions *
********************/
/**
* Pause relaying.
* @notice Allows the owner of this contract to temporarily pause message relaying. Backup
* security mechanism just in case. Owner should be the same as the upgrade wallet to
* maintain the security model of the system as a whole.
*/
function pause() external onlyOwner {
_pause();
}
/**
* Unpause relaying.
* @notice Allows the owner of this contract to resume message relaying once paused.
*/
function unpause() external onlyOwner {
_unpause();
}
/**
* Retrieves the address of the x-domain message sender. Will throw an error
* if the sender is not currently set (equal to the default sender).
* This function is meant to be called on the remote side of a cross domain
* message so that the account that initiated the call can be known.
* @notice Retrieves the address of the contract or wallet that initiated the currently
* executing message on the other chain. Will throw an error if there is no message
* currently being executed. Allows the recipient of a call to see who triggered it.
*
* @return Address of the x-domain message sender.
* @return Address of the sender of the currently executing message on the other chain.
*/
function xDomainMessageSender() external view returns (address) {
require(
......@@ -120,22 +161,25 @@ abstract contract CrossDomainMessenger is
}
/**
* Retrieves the next message nonce. Adds the hash version to the nonce.
* @notice Retrieves the next message nonce. Message version will be added to the upper two
* bytes of the message nonce. Message version allows us to treat messages as having
* different structures.
*
* @return Next message nonce with added hash version.
* @return Nonce of the next message to be sent, with added message version.
*/
function messageNonce() public view returns (uint256) {
return CrossDomainHashing.addVersionToNonce(msgNonce, MESSAGE_VERSION);
}
/**
* Base amount of gas required to make sure that the message will be received without
* running out of gas. Amount of gas provided to the L2 call will be the gas requested by
* the user PLUS this gas value so that if the message is not successful, it can always be
* replayed on the other end.
* @notice Computes the amount of gas required to guarantee that a given message will be
* received on the other chain without running out of gas. Guaranteeing that a message
* will not run out of gas is important because this ensures that a message can always
* be replayed on the other chain if it fails to execute completely.
*
* @param _message Message to compute base gas for.
* @return Base gas required for message.
* @param _message Message to compute the amount of required gas for.
*
* @return Amount of gas required to guarantee message receipt.
*/
function baseGas(bytes memory _message) public pure returns (uint32) {
// TODO: Values here are meant to be good enough to get a devnet running. We need to do
......@@ -145,9 +189,11 @@ abstract contract CrossDomainMessenger is
}
/**
* @param _target Target contract address.
* @param _message Message to send to the target.
* @param _minGasLimit Gas limit for the provided message.
* @notice Sends a message to some target address on the other chain.
*
* @param _target Target contract or wallet address.
* @param _message Message to trigger the target address with.
* @param _minGasLimit Minimum gas limit that the message can be executed with.
*/
function sendMessage(
address _target,
......@@ -180,6 +226,18 @@ abstract contract CrossDomainMessenger is
}
}
/**
* @notice Relays a message that was sent by the other CrossDomainMessenger contract. Can only
* be executed via cross-chain call from the other messenger OR if the message was
* already received once and is currently being replayed.
*
* @param _nonce Nonce of the message being relayed.
* @param _sender Address of the user who sent the message.
* @param _target Address that the message is targeted at.
* @param _value ETH value to send with the message.
* @param _minGasLimit Minimum amount of gas that the message can be executed with.
* @param _message Message to send to the target.
*/
function relayMessage(
uint256 _nonce,
address _sender,
......@@ -240,22 +298,16 @@ abstract contract CrossDomainMessenger is
}
}
/**********************
* Internal Functions *
**********************/
function _isSystemMessageSender() internal view virtual returns (bool);
function _sendMessage(
address _to,
uint64 _gasLimit,
uint256 _value,
bytes memory _data
) internal virtual;
/**
* @notice Initializes the contract. The parent contract MUST handle
* preventing this from being called twice.
* @notice Intializer.
*
* @param _otherMessenger Address of the CrossDomainMessenger on the paired chain.
* @param _blockedSystemAddresses List of system addresses that need to be blocked to prevent
* certain security issues. Exact list depends on the network
* where this contract is deployed. See note attached to the
* blockedSystemAddresses variable in this contract for more
* detailed information about what this block list can and
* cannot be used for.
*/
function _initialize(address _otherMessenger, address[] memory _blockedSystemAddresses)
internal
......@@ -274,4 +326,27 @@ abstract contract CrossDomainMessenger is
__Pausable_init_unchained();
__ReentrancyGuard_init_unchained();
}
/**
* @notice Checks whether the message is coming from the other messenger. Implemented by child
* contracts because the logic for this depends on the network where the messenger is
* being deployed.
*/
function _isSystemMessageSender() internal view virtual returns (bool) {
revert("CrossDomainMessenger: child contract must implement");
}
/**
* @notice Sends a low-level message to the other messenger. Needs to be implemented by child
* contracts because the logic for this depends on the network where the messenger is
* being deployed.
*/
function _sendMessage(
address _to,
uint64 _gasLimit,
uint256 _value,
bytes memory _data
) internal virtual {
revert("CrossDomainMessenger: child contract must implement");
}
}
......@@ -6,32 +6,44 @@ import "./SupportedInterfaces.sol";
/**
* @title OptimismMintableERC20
* This contract represents the remote representation
* of an ERC20 token. It is linked to the address of
* a token in another domain and tokens can be locked
* in the StandardBridge which will mint tokens in the
* other domain.
* @notice OptimismMintableERC20 is a standard extension of the base ERC20 token contract designed
* to allow the StandardBridge contracts to mint and burn tokens. This makes it possible to
* use an OptimismMintablERC20 as the L2 representation of an L1 token, or vice-versa.
* Designed to be backwards compatible with the older StandardL2ERC20 token which was only
* meant for use on L2.
*/
contract OptimismMintableERC20 is ERC20 {
event Mint(address indexed _account, uint256 _amount);
event Burn(address indexed _account, uint256 _amount);
/**
* @notice Emitted whenever tokens are minted for an account.
*
* @param account Address of the account tokens are being minted for.
* @param amount Amount of tokens minted.
*/
event Mint(address indexed account, uint256 amount);
/**
* @notice The address of the token in the remote domain
* @notice Emitted whenever tokens are burned from an account.
*
* @param account Address of the account tokens are being burned from.
* @param amount Amount of tokens burned.
*/
event Burn(address indexed account, uint256 amount);
/**
* @notice Address of the corresponding version of this token on the remote chain.
*/
address public remoteToken;
/**
* @notice The address of the bridge responsible for
* minting. It is in the same domain.
* @notice Address of the StandardBridge on this network.
*/
address public bridge;
/**
* @param _bridge Address of the L2 standard bridge.
* @param _bridge Address of the L2 standard bridge.
* @param _remoteToken Address of the corresponding L1 token.
* @param _name ERC20 name.
* @param _symbol ERC20 symbol.
* @param _name ERC20 name.
* @param _symbol ERC20 symbol.
*/
constructor(
address _bridge,
......@@ -44,17 +56,16 @@ contract OptimismMintableERC20 is ERC20 {
}
/**
* @notice Returns the corresponding L1 token address.
* This is a legacy function and wraps the remoteToken value.
* @custom:legacy
* @notice Legacy getter for the remote token. Use remoteToken going forward.
*/
function l1Token() public view returns (address) {
return remoteToken;
}
/**
* @notice The address of the bridge contract
* responsible for minting tokens. This is a legacy
* getter function
* @custom:legacy
* @notice Legacy getter for the bridge. Use bridge going forward.
*/
function l2Bridge() public view returns (address) {
return bridge;
......@@ -64,15 +75,18 @@ contract OptimismMintableERC20 is ERC20 {
* @notice A modifier that only allows the bridge to call
*/
modifier onlyBridge() {
require(msg.sender == bridge, "Only L2 Bridge can mint and burn");
require(msg.sender == bridge, "OptimismMintableERC20: only bridge can mint and burn");
_;
}
/**
* @notice ERC165
* @notice ERC165 interface check function.
*
* @param _interfaceId Interface ID to check.
*
* @return Whether or not the interface is supported by this contract.
*/
// slither-disable-next-line external-function
function supportsInterface(bytes4 _interfaceId) public pure returns (bool) {
function supportsInterface(bytes4 _interfaceId) external pure returns (bool) {
bytes4 iface1 = type(IERC165).interfaceId;
bytes4 iface2 = type(IL1Token).interfaceId;
bytes4 iface3 = type(IRemoteToken).interfaceId;
......@@ -80,22 +94,24 @@ contract OptimismMintableERC20 is ERC20 {
}
/**
* @notice The bridge can mint tokens
* @notice Allows the StandardBridge on this network to mint tokens.
*
* @param _to Address to mint tokens to.
* @param _amount Amount of tokens to mint.
*/
// slither-disable-next-line external-function
function mint(address _to, uint256 _amount) public virtual onlyBridge {
function mint(address _to, uint256 _amount) external virtual onlyBridge {
_mint(_to, _amount);
emit Mint(_to, _amount);
}
/**
* @notice The bridge can burn tokens
* @notice Allows the StandardBridge on this network to burn tokens.
*
* @param _from Address to burn tokens from.
* @param _amount Amount of tokens to burn.
*/
// slither-disable-next-line external-function
function burn(address _from, uint256 _amount) public virtual onlyBridge {
function burn(address _from, uint256 _amount) external virtual onlyBridge {
_burn(_from, _amount);
emit Burn(_from, _amount);
}
}
......@@ -6,50 +6,77 @@ import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol";
import { Lib_PredeployAddresses } from "../libraries/Lib_PredeployAddresses.sol";
/**
* @custom:proxied
* @custom:predeployed 0x4200000000000000000000000000000000000012
* @title OptimismMintableTokenFactory
* @dev Factory contract for creating standard remote token representations of
* local ERC20s. This can be used to bridge native L1 ERC20s to L2 or native L2
* ERC20s to L1. The tokens created through this factory are meant to operate
* with the StandardBridge contract for deposits/withdrawals.
* This contract is a predeploy on L2 at 0x4200000000000000000000000000000000000012
* TODO: deploy to a deterministic address on L1 networks?
* TODO: should this be extended for L1/L2 with hardcoded values in
* the base contract's initialize?
* @notice OptimismMintableTokenFactory is a factory contract that generates OptimismMintableERC20
* contracts on the network it's deployed to. Simplifies the deployment process for users
* who may be less familiar with deploying smart contracts. Designed to be backwards
* compatible with the older StandardL2ERC20Factory contract.
*/
contract OptimismMintableTokenFactory {
event StandardL2TokenCreated(address indexed _remoteToken, address indexed _localToken);
/**
* @custom:legacy
* @notice Emitted whenever a new OptimismMintableERC20 is created. Legacy version of the newer
* OptimismMintableERC20Created event. We recommend relying on that event instead.
*
* @param remoteToken Address of the token on the remote chain.
* @param localToken Address of the created token on the local chain.
*/
event StandardL2TokenCreated(address indexed remoteToken, address indexed localToken);
/**
* @notice Emitted whenever a new OptimismMintableERC20 is created.
*
* @param localToken Address of the created token on the local chain.
* @param remoteToken Address of the corresponding token on the remote chain.
* @param deployer Address of the account that deployed the token.
*/
event OptimismMintableTokenCreated(
address indexed _localToken,
address indexed _remoteToken,
address _deployer
address indexed localToken,
address indexed remoteToken,
address deployer
);
/**
* @notice Address of the StandardBridge on this chain.
*/
address public bridge;
/**
* @dev Initialize the factory
* On L2 _bridge should be Lib_PredeployAddresses.L2_STANDARD_BRIDGE,
* On L1 _bridge should be the L1StandardBridge
* @notice Initializer.
*
* @param _bridge Address of the StandardBridge on this chain.
*/
function initialize(address _bridge) public {
require(bridge == address(0), "Already initialized.");
require(bridge == address(0), "OptimismMintableTokenFactory: already initialized");
bridge = _bridge;
}
/**
* @dev Creates an instance of the standard ERC20 token on L2.
* @param _remoteToken Address of the corresponding L1 token.
* @param _name ERC20 name.
* @param _symbol ERC20 symbol.
* @return Address of the new token.
* @notice Creates an instance of the OptimismMintableERC20 contract. Legacy version of the
* newer createOptimismMintableERC20 function, which has a more intuitive name.
*
* @param _remoteToken Address of the token on the remote chain.
* @param _name ERC20 name.
* @param _symbol ERC20 symbol.
*
* @return Address of the newly created token.
*/
function createStandardL2Token(
address _remoteToken,
string memory _name,
string memory _symbol
) external returns (address) {
require(_remoteToken != address(0), "Must provide L1 token address");
require(bridge != address(0), "Must initialize first");
require(
_remoteToken != address(0),
"OptimismMintableTokenFactory: must provide remote token address"
);
require(
bridge != address(0),
"OptimismMintableTokenFactory: must initialize contract first"
);
OptimismMintableERC20 localToken = new OptimismMintableERC20(
bridge,
......@@ -58,7 +85,7 @@ contract OptimismMintableTokenFactory {
_symbol
);
// Legacy Purposes
// Emit the old event too for legacy support.
emit StandardL2TokenCreated(_remoteToken, address(localToken));
emit OptimismMintableTokenCreated(_remoteToken, address(localToken), msg.sender);
......
......@@ -3,22 +3,22 @@ pragma solidity ^0.8.9;
/**
* @title Proxy
* @notice Proxy is a transparent proxy that passes through the call
* if the caller is the owner or if the caller is `address(0)`,
* meaning that the call originated from an offchain simulation.
* @notice Proxy is a transparent proxy that passes through the call if the caller is the owner or
* if the caller is address(0), meaning that the call originated from an off-chain
* simulation.
*/
contract Proxy {
/**
* @notice An event that is emitted each time the implementation is changed.
* This event is part of the EIP 1967 spec.
* @notice An event that is emitted each time the implementation is changed. This event is part
* of the EIP-1967 specification.
*
* @param implementation The address of the implementation contract
*/
event Upgraded(address indexed implementation);
/**
* @notice An event that is emitted each time the owner is upgraded.
* This event is part of the EIP 1967 spec.
* @notice An event that is emitted each time the owner is upgraded. This event is part of the
* EIP-1967 specification.
*
* @param previousAdmin The previous owner of the contract
* @param newAdmin The new owner of the contract
......@@ -40,12 +40,12 @@ contract Proxy {
0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @notice set the initial owner during contract deployment. The
* owner is stored at the eip1967 owner storage slot so that
* storage collision with the implementation is not possible.
* @notice Sets the initial admin during contract deployment. Admin address is stored at the
* EIP-1967 admin storage slot so that accidental storage collision with the
* implementation is not possible.
*
* @param _admin Address of the initial contract owner. The owner has
* the ability to access the transparent proxy interface.
* @param _admin Address of the initial contract admin. Admin as the ability to access the
* transparent proxy interface.
*/
constructor(address _admin) {
_changeAdmin(_admin);
......@@ -58,11 +58,10 @@ contract Proxy {
}
/**
* @notice A modifier that reverts if not called by the owner
* or by `address(0)` to allow `eth_call` to interact
* with the proxy without needing to use low level storage
* inspection. It is assumed that nobody controls the private
* key for `address(0)`.
* @notice A modifier that reverts if not called by the owner or by address(0) to allow
* eth_call to interact with this proxy without needing to use low-level storage
* inspection. We assume that nobody is able to trigger calls from address(0) during
* normal EVM execution.
*/
modifier proxyCallIfNotAdmin() {
if (msg.sender == _getAdmin() || msg.sender == address(0)) {
......@@ -74,23 +73,21 @@ contract Proxy {
}
/**
* @notice Set the implementation contract address. The code at this
* address will execute when this contract is called.
* @notice Set the implementation contract address. The code at the given address will execute
* when this contract is called.
*
* @param _implementation The address of the implementation contract
* @param _implementation Address of the implementation contract.
*/
function upgradeTo(address _implementation) external proxyCallIfNotAdmin {
_setImplementation(_implementation);
}
/**
* @notice Set the implementation and call a function in a single
* transaction. This is useful to ensure atomic `initialize()`
* based upgrades.
* @notice Set the implementation and call a function in a single transaction. Useful to ensure
* atomic execution of initialization-based upgrades.
*
* @param _implementation The address of the implementation contract
* @param _data The calldata to delegatecall the new
* implementation with
* @param _implementation Address of the implementation contract.
* @param _data Calldata to delegatecall the new implementation with.
*/
function upgradeToAndCall(address _implementation, bytes calldata _data)
external
......@@ -100,7 +97,7 @@ contract Proxy {
{
_setImplementation(_implementation);
(bool success, bytes memory returndata) = _implementation.delegatecall(_data);
require(success);
require(success, "Proxy: delegatecall to new implementation contract failed");
return returndata;
}
......@@ -146,7 +143,7 @@ contract Proxy {
/**
* @notice Queries the implementation address.
*
* @return implementation address.
* @return Implementation address.
*/
function _getImplementation() internal view returns (address) {
address implementation;
......@@ -172,7 +169,7 @@ contract Proxy {
/**
* @notice Queries the owner of the proxy contract.
*
* @return owner address.
* @return Owner address.
*/
function _getAdmin() internal view returns (address) {
address owner;
......@@ -187,7 +184,6 @@ contract Proxy {
*/
function _doProxyCall() internal {
address implementation = _getImplementation();
require(implementation != address(0), "Proxy: implementation not initialized");
assembly {
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import { Proxy } from "./Proxy.sol";
import { Owned } from "@rari-capital/solmate/src/auth/Owned.sol";
import { Proxy } from "./Proxy.sol";
import { Lib_AddressManager } from "../legacy/Lib_AddressManager.sol";
import { L1ChugSplashProxy } from "../legacy/L1ChugSplashProxy.sol";
// Define static interfaces of these proxies so that we can easily
// use staticcall on the getters we need.
interface IStatic_ERC1967Proxy {
/**
* @title IStaticERC1967Proxy
* @notice IStaticERC1967Proxy is a static version of the ERC1967 proxy interface.
*/
interface IStaticERC1967Proxy {
function implementation() external view returns (address);
function admin() external view returns (address);
}
interface IStatic_L1ChugSplashProxy {
/**
* @title IStaticL1ChugSplashProxy
* @notice IStaticL1ChugSplashProxy is a static version of the ChugSplash proxy interface.
*/
interface IStaticL1ChugSplashProxy {
function getImplementation() external view returns (address);
function getOwner() external view returns (address);
......@@ -22,20 +28,17 @@ interface IStatic_L1ChugSplashProxy {
/**
* @title ProxyAdmin
* @dev This is an auxiliary contract meant to be assigned as the admin of an ERC1967 Proxy,
* based on the OpenZeppelin implementation. It has backwards compatibility logic to work with
* the various types of proxies that have been deployed by Optimism.
* @notice This is an auxiliary contract meant to be assigned as the admin of an ERC1967 Proxy,
* based on the OpenZeppelin implementation. It has backwards compatibility logic to work
* with the various types of proxies that have been deployed by Optimism in the past.
*/
contract ProxyAdmin is Owned {
/**
* @notice The proxy types that the ProxyAdmin can manage.
*
* @custom:value ERC1967 Represents an ERC1967 compliant transparent proxy
* interface, this is the default.
* @custom:value Chugsplash Represents the Chugsplash proxy interface,
* this is legacy.
* @custom:value ResolvedDelegate Represents the ResolvedDelegate proxy
* interface, this is legacy.
* @custom:value ERC1967 Represents an ERC1967 compliant transparent proxy interface.
* @custom:value Chugsplash Represents the Chugsplash proxy interface (legacy).
* @custom:value ResolvedDelegate Represents the ResolvedDelegate proxy (legacy).
*/
enum ProxyType {
ERC1967,
......@@ -45,7 +48,7 @@ contract ProxyAdmin is Owned {
/**
* @custom:legacy
* @notice A mapping of proxy types, used for backwards compatibility.
* @notice A mapping of proxy types, used for backwards compatibility.
*/
mapping(address => ProxyType) public proxyType;
......@@ -53,14 +56,14 @@ contract ProxyAdmin is Owned {
* @custom:legacy
* @notice A reverse mapping of addresses to names held in the AddressManager. This must be
* manually kept up to date with changes in the AddressManager for this contract
* to be able to work as an admin for the Lib_ResolvedDelegateProxy type.
* to be able to work as an admin for the ResolvedDelegateProxy type.
*/
mapping(address => string) public implementationName;
/**
* @custom:legacy
* @notice The address of the address manager, this is required to manage the
* Lib_ResolvedDelegateProxy type.
* ResolvedDelegateProxy type.
*/
Lib_AddressManager public addressManager;
......@@ -71,36 +74,37 @@ contract ProxyAdmin is Owned {
bool internal upgrading = false;
/**
* @notice Set the owner of the ProxyAdmin via constructor argument.
* @param _owner Address of the initial owner of this contract.
*/
constructor(address owner) Owned(owner) {}
constructor(address _owner) Owned(_owner) {}
/**
* @notice
* @notice Sets the proxy type for a given address. Only required for non-standard (legacy)
* proxy types.
*
* @param _address The address of the proxy.
* @param _type The type of the proxy.
* @param _address Address of the proxy.
* @param _type Type of the proxy.
*/
function setProxyType(address _address, ProxyType _type) external onlyOwner {
proxyType[_address] = _type;
}
/**
* @notice Set the proxy type in the mapping. This needs to be kept up to date by the owner of
* the contract.
* @notice Sets the implementation name for a given address. Only required for
* ResolvedDelegateProxy type proxies that have an implementation name.
*
* @param _address The address to be named.
* @param _name The name of the address.
* @param _address Address of the ResolvedDelegateProxy.
* @param _name Name of the implementation for the proxy.
*/
function setImplementationName(address _address, string memory _name) external onlyOwner {
implementationName[_address] = _name;
}
/**
* @notice Set the address of the address manager. This is required to manage the legacy
* `Lib_ResolvedDelegateProxy`.
* @notice Set the address of the AddressManager. This is required to manage legacy
* ResolvedDelegateProxy type proxy contracts.
*
* @param _address The address of the address manager.
* @param _address Address of the AddressManager.
*/
function setAddressManager(Lib_AddressManager _address) external onlyOwner {
addressManager = _address;
......@@ -108,11 +112,12 @@ contract ProxyAdmin is Owned {
/**
* @custom:legacy
* @notice Set an address in the address manager. This is required because only the owner of
* the AddressManager can set the addresses in it.
* @notice Set an address in the address manager. Since only the owner of the AddressManager
* can directly modify addresses and the ProxyAdmin will own the AddressManager, this
* gives the owner of the ProxyAdmin the ability to modify addresses directly.
*
* @param _name The name of the address to set in the address manager.
* @param _address The address to set in the address manager.
* @param _name Name to set within the AddressManager.
* @param _address Address to attach to the given name.
*/
function setAddress(string memory _name, address _address) external onlyOwner {
addressManager.setAddress(_name, _address);
......@@ -120,10 +125,11 @@ contract ProxyAdmin is Owned {
/**
* @custom:legacy
* @notice Legacy function used by the old Chugsplash proxy to determine if an upgrade is
* happening.
* @notice Legacy function used to tell ChugSplashProxy contracts if an upgrade is happening.
*
* @return Whether or not there is an upgrade going on
* @return Whether or not there is an upgrade going on. May not actually tell you whether an
* upgrade is going on, since we don't currently plan to use this variable for anything
* other than a legacy indicator to fix a UX bug in the ChugSplash proxy.
*/
function isUpgrading() external view returns (bool) {
return upgrading;
......@@ -140,40 +146,40 @@ contract ProxyAdmin is Owned {
}
/**
* @dev Returns the current implementation of `proxy`.
* This contract must be the admin of `proxy`.
* @notice Returns the implementation of the given proxy address.
*
* @param proxy The Proxy to return the implementation of.
* @return The address of the implementation.
* @param _proxy Address of the proxy to get the implementation of.
*
* @return Address of the implementation of the proxy.
*/
function getProxyImplementation(Proxy proxy) external view returns (address) {
ProxyType proxyType = proxyType[address(proxy)];
function getProxyImplementation(Proxy _proxy) external view returns (address) {
ProxyType proxyType = proxyType[address(_proxy)];
if (proxyType == ProxyType.ERC1967) {
return IStatic_ERC1967Proxy(address(proxy)).implementation();
return IStaticERC1967Proxy(address(_proxy)).implementation();
} else if (proxyType == ProxyType.Chugsplash) {
return IStatic_L1ChugSplashProxy(address(proxy)).getImplementation();
return IStaticL1ChugSplashProxy(address(_proxy)).getImplementation();
} else if (proxyType == ProxyType.ResolvedDelegate) {
return addressManager.getAddress(implementationName[address(proxy)]);
return addressManager.getAddress(implementationName[address(_proxy)]);
} else {
revert("ProxyAdmin: unknown proxy type");
}
}
/**
* @dev Returns the current admin of `proxy`.
* This contract must be the admin of `proxy`.
* @notice Returns the admin of the given proxy address.
*
* @param _proxy Address of the proxy to get the admin of.
*
* @param proxy The Proxy to return the admin of.
* @return The address of the admin.
* @return Address of the admin of the proxy.
*/
function getProxyAdmin(Proxy proxy) external view returns (address) {
ProxyType proxyType = proxyType[address(proxy)];
function getProxyAdmin(Proxy _proxy) external view returns (address) {
ProxyType proxyType = proxyType[address(_proxy)];
if (proxyType == ProxyType.ERC1967) {
return IStatic_ERC1967Proxy(address(proxy)).admin();
return IStaticERC1967Proxy(address(_proxy)).admin();
} else if (proxyType == ProxyType.Chugsplash) {
return IStatic_L1ChugSplashProxy(address(proxy)).getOwner();
return IStaticL1ChugSplashProxy(address(_proxy)).getOwner();
} else if (proxyType == ProxyType.ResolvedDelegate) {
return addressManager.owner();
} else {
......@@ -182,71 +188,70 @@ contract ProxyAdmin is Owned {
}
/**
* @dev Changes the admin of `proxy` to `newAdmin`. This contract must be the current admin
* of `proxy`.
* @notice Updates the admin of the given proxy address.
*
* @param proxy The proxy that will have its admin updated.
* @param newAdmin The address of the admin to update to.
* @param _proxy Address of the proxy to update.
* @param _newAdmin Address of the new proxy admin.
*/
function changeProxyAdmin(Proxy proxy, address newAdmin) external onlyOwner {
ProxyType proxyType = proxyType[address(proxy)];
function changeProxyAdmin(Proxy _proxy, address _newAdmin) external onlyOwner {
ProxyType proxyType = proxyType[address(_proxy)];
if (proxyType == ProxyType.ERC1967) {
proxy.changeAdmin(newAdmin);
_proxy.changeAdmin(_newAdmin);
} else if (proxyType == ProxyType.Chugsplash) {
L1ChugSplashProxy(payable(proxy)).setOwner(newAdmin);
L1ChugSplashProxy(payable(_proxy)).setOwner(_newAdmin);
} else if (proxyType == ProxyType.ResolvedDelegate) {
addressManager.transferOwnership(newAdmin);
addressManager.transferOwnership(_newAdmin);
} else {
revert("ProxyAdmin: unknown proxy type");
}
}
/**
* @dev Upgrades `proxy` to `implementation`. This contract must be the admin of `proxy`.
* @notice Changes a proxy's implementation contract.
*
* @param proxy The address of the proxy.
* @param implementation The address of the implementation.
* @param _proxy Address of the proxy to upgrade.
* @param _implementation Address of the new implementation address.
*/
function upgrade(Proxy proxy, address implementation) public onlyOwner {
ProxyType proxyType = proxyType[address(proxy)];
function upgrade(Proxy _proxy, address _implementation) public onlyOwner {
ProxyType proxyType = proxyType[address(_proxy)];
if (proxyType == ProxyType.ERC1967) {
proxy.upgradeTo(implementation);
_proxy.upgradeTo(_implementation);
} else if (proxyType == ProxyType.Chugsplash) {
L1ChugSplashProxy(payable(proxy)).setStorage(
L1ChugSplashProxy(payable(_proxy)).setStorage(
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc,
bytes32(uint256(uint160(implementation)))
bytes32(uint256(uint160(_implementation)))
);
} else if (proxyType == ProxyType.ResolvedDelegate) {
string memory name = implementationName[address(proxy)];
addressManager.setAddress(name, implementation);
string memory name = implementationName[address(_proxy)];
addressManager.setAddress(name, _implementation);
} else {
revert("ProxyAdmin: unknown proxy type");
}
}
/**
* @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation.
* This contract must be the admin of `proxy`.
* @notice Changes a proxy's implementation contract and delegatecalls the new implementation
* with some given data. Useful for atomic upgrade-and-initialize calls.
*
* @param proxy The proxy to call.
* @param implementation The implementation to upgrade the proxy to.
* @param data The calldata to pass to the implementation.
* @param _proxy Address of the proxy to upgrade.
* @param _implementation Address of the new implementation address.
* @param _data Data to trigger the new implementation with.
*/
function upgradeAndCall(
Proxy proxy,
address implementation,
bytes memory data
Proxy _proxy,
address _implementation,
bytes memory _data
) external payable onlyOwner {
ProxyType proxyType = proxyType[address(proxy)];
ProxyType proxyType = proxyType[address(_proxy)];
if (proxyType == ProxyType.ERC1967) {
proxy.upgradeToAndCall{ value: msg.value }(implementation, data);
_proxy.upgradeToAndCall{ value: msg.value }(_implementation, _data);
} else {
// reverts if proxy type is unknown
upgrade(proxy, implementation);
(bool success, ) = address(proxy).call{ value: msg.value }(data);
upgrade(_proxy, _implementation);
(bool success, ) = address(_proxy).call{ value: msg.value }(_data);
require(success);
}
}
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
/* Interface Imports */
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./SupportedInterfaces.sol";
/* Library Imports */
import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IRemoteToken, IL1Token } from "./SupportedInterfaces.sol";
import { CrossDomainMessenger } from "./CrossDomainMessenger.sol";
import { OptimismMintableERC20 } from "./OptimismMintableERC20.sol";
/**
* @title StandardBridge
* This contract can manage a 1:1 bridge between two domains for both
* ETH (native asset) and ERC20s.
* This contract should be deployed behind a proxy.
* TODO: do we want a donateERC20 function as well?
* @notice StandardBridge is a base contract for the L1 and L2 standard ERC20 bridges.
*/
abstract contract StandardBridge {
using SafeERC20 for IERC20;
/**********
* Events *
**********/
/**
* @notice Emitted when an ETH bridge is initiated to the other chain.
*
* @param from Address of the sender.
* @param to Address of the receiver.
* @param amount Amount of ETH sent.
* @param extraData Extra data sent with the transaction.
*/
event ETHBridgeInitiated(
address indexed _from,
address indexed _to,
uint256 _amount,
bytes _extraData
address indexed from,
address indexed to,
uint256 amount,
bytes extraData
);
/**
* @notice Emitted when an ETH bridge is finalized on this chain.
*
* @param from Address of the sender.
* @param to Address of the receiver.
* @param amount Amount of ETH sent.
* @param extraData Extra data sent with the transaction.
*/
event ETHBridgeFinalized(
address indexed _from,
address indexed _to,
uint256 _amount,
bytes _extraData
address indexed from,
address indexed to,
uint256 amount,
bytes extraData
);
/**
* @notice Emitted when an ERC20 bridge is initiated to the other chain.
*
* @param localToken Address of the ERC20 on this chain.
* @param remoteToken Address of the ERC20 on the remote chain.
* @param from Address of the sender.
* @param to Address of the receiver.
* @param amount Amount of ETH sent.
* @param extraData Extra data sent with the transaction.
*/
event ERC20BridgeInitiated(
address indexed _localToken,
address indexed _remoteToken,
address indexed _from,
address _to,
uint256 _amount,
bytes _extraData
address indexed localToken,
address indexed remoteToken,
address indexed from,
address to,
uint256 amount,
bytes extraData
);
/**
* @notice Emitted when an ERC20 bridge is finalized on this chain.
*
* @param localToken Address of the ERC20 on this chain.
* @param remoteToken Address of the ERC20 on the remote chain.
* @param from Address of the sender.
* @param to Address of the receiver.
* @param amount Amount of ETH sent.
* @param extraData Extra data sent with the transaction.
*/
event ERC20BridgeFinalized(
address indexed _localToken,
address indexed _remoteToken,
address indexed _from,
address _to,
uint256 _amount,
bytes _extraData
address indexed localToken,
address indexed remoteToken,
address indexed from,
address to,
uint256 amount,
bytes extraData
);
/**
* @notice Emitted when an ERC20 bridge to this chain fails.
*
* @param localToken Address of the ERC20 on this chain.
* @param remoteToken Address of the ERC20 on the remote chain.
* @param from Address of the sender.
* @param to Address of the receiver.
* @param amount Amount of ETH sent.
* @param extraData Extra data sent with the transaction.
*/
event ERC20BridgeFailed(
address indexed _localToken,
address indexed _remoteToken,
address indexed _from,
address _to,
uint256 _amount,
bytes _extraData
address indexed localToken,
address indexed remoteToken,
address indexed from,
address to,
uint256 amount,
bytes extraData
);
/*************
* Constants *
*************/
/**
* @notice The L2 gas limit set when eth is depoisited using the receive() function.
*/
uint32 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 200_000;
/*************
* Variables *
*************/
/**
* @notice The messenger contract on the same domain
* @notice Messenger contract on this domain.
*/
CrossDomainMessenger public messenger;
/**
* @notice The corresponding bridge on the other domain
* @notice Corresponding bridge on the other domain.
*/
StandardBridge public otherBridge;
/**
* @notice Mapping that stores deposits for a given pair of local and remote tokens.
*/
mapping(address => mapping(address => uint256)) public deposits;
/*************
* Modifiers *
*************/
/**
* @notice Only allow EOAs to call the functions. Note that this
* is not safe against contracts calling code during their constructor
* @notice Only allow EOAs to call the functions. Note that this is not safe against contracts
* calling code within their constructors, but also doesn't really matter since we're
* just trying to prevent users accidentally depositing with smart contract wallets.
*/
modifier onlyEOA() {
require(!Address.isContract(msg.sender), "Account not EOA");
......@@ -107,8 +134,7 @@ abstract contract StandardBridge {
}
/**
* @notice Ensures that the caller is the messenger, and that
* it has the l2Sender value set to the address of the remote Token Bridge.
* @notice Ensures that the caller is a cross-chain message from the other bridge.
*/
modifier onlyOtherBridge() {
require(
......@@ -119,33 +145,45 @@ abstract contract StandardBridge {
_;
}
/**
* @notice Ensures that the caller is this contract.
*/
modifier onlySelf() {
require(msg.sender == address(this), "Function can only be called by self.");
_;
}
/********************
* Public Functions *
********************/
/**
* @notice EOAs can simply send ETH to this contract to have it be deposited
* to L2 through the standard bridge.
* @notice Allows EOAs to deposit ETH by sending directly to the bridge.
*/
receive() external payable onlyEOA {
_initiateBridgeETH(msg.sender, msg.sender, msg.value, RECEIVE_DEFAULT_GAS_LIMIT, bytes(""));
}
/**
* @notice Send ETH to the message sender on the remote domain
* @notice Sends ETH to the sender's address on the other chain.
*
* @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
* @param _extraData Extra data to be sent with the transaction. Note that the recipient will
* not be triggered with this data, but it will be emitted and can be used
* to identify the transaction.
*/
function bridgeETH(uint32 _minGasLimit, bytes calldata _extraData) public payable onlyEOA {
_initiateBridgeETH(msg.sender, msg.sender, msg.value, _minGasLimit, _extraData);
}
/**
* @notice Send ETH to a specified account on the remote domain. Note that if ETH is sent to a
* contract and the call fails, then that ETH will be locked in the other bridge.
* @notice Sends ETH to a receiver's address on the other chain. Note that if ETH is sent to a
* smart contract and the call fails, the ETH will be temporarily locked in the
* StandardBridge on the other chain until the call is replayed. If the call cannot be
* replayed with any amount of gas (call always reverts), then the ETH will be
* permanently locked in the StandardBridge on the other chain.
*
* @param _to Address of the receiver.
* @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
* @param _extraData Extra data to be sent with the transaction. Note that the recipient will
* not be triggered with this data, but it will be emitted and can be used
* to identify the transaction.
*/
function bridgeETHTo(
address _to,
......@@ -156,7 +194,18 @@ abstract contract StandardBridge {
}
/**
* @notice Send an ERC20 to the message sender on the remote domain
* @notice Sends ERC20 tokens to the sender's address on the other chain. Note that if the
* ERC20 token on the other chain does not recognize the local token as the correct
* pair token, the ERC20 bridge will fail and the tokens will be returned to sender on
* this chain.
*
* @param _localToken Address of the ERC20 on this chain.
* @param _remoteToken Address of the corresponding token on the remote chain.
* @param _amount Amount of local tokens to deposit.
* @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
* @param _extraData Extra data to be sent with the transaction. Note that the recipient will
* not be triggered with this data, but it will be emitted and can be used
* to identify the transaction.
*/
function bridgeERC20(
address _localToken,
......@@ -177,7 +226,19 @@ abstract contract StandardBridge {
}
/**
* @notice Send an ERC20 to a specified account on the remote domain
* @notice Sends ERC20 tokens to a receiver's address on the other chain. Note that if the
* ERC20 token on the other chain does not recognize the local token as the correct
* pair token, the ERC20 bridge will fail and the tokens will be returned to sender on
* this chain.
*
* @param _localToken Address of the ERC20 on this chain.
* @param _remoteToken Address of the corresponding token on the remote chain.
* @param _to Address of the receiver.
* @param _amount Amount of local tokens to deposit.
* @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
* @param _extraData Extra data to be sent with the transaction. Note that the recipient will
* not be triggered with this data, but it will be emitted and can be used
* to identify the transaction.
*/
function bridgeERC20To(
address _localToken,
......@@ -199,7 +260,15 @@ abstract contract StandardBridge {
}
/**
* @notice Finalize an ETH sending transaction sent from a remote domain
* @notice Finalizes an ETH bridge on this chain. Can only be triggered by the other
* StandardBridge contract on the remote chain.
*
* @param _from Address of the sender.
* @param _to Address of the receiver.
* @param _amount Amount of ETH being bridged.
* @param _extraData Extra data to be sent with the transaction. Note that the recipient will
* not be triggered with this data, but it will be emitted and can be used
* to identify the transaction.
*/
function finalizeBridgeETH(
address _from,
......@@ -216,7 +285,18 @@ abstract contract StandardBridge {
}
/**
* @notice Finalize an ERC20 sending transaction sent from a remote domain
* @notice Finalizes an ERC20 bridge on this chain. Can only be triggered by the other
* StandardBridge contract on the remote chain.
*
*
* @param _localToken Address of the ERC20 on this chain.
* @param _remoteToken Address of the corresponding token on the remote chain.
* @param _from Address of the sender.
* @param _to Address of the receiver.
* @param _amount Amount of ETH being bridged.
* @param _extraData Extra data to be sent with the transaction. Note that the recipient will
* not be triggered with this data, but it will be emitted and can be used
* to identify the transaction.
*/
function finalizeBridgeERC20(
address _localToken,
......@@ -246,6 +326,18 @@ abstract contract StandardBridge {
}
}
/**
* @notice Completes an outbound token transfer. Public function, but can only be called by
* this contract. It's security critical that there be absolutely no way for anyone to
* trigger this function, except by explicit trigger within this contract. Used as a
* simple way to be able to try/catch any type of revert that could occur during an
* ERC20 mint/transfer.
*
* @param _localToken Address of the ERC20 on this chain.
* @param _remoteToken Address of the corresponding token on the remote chain.
* @param _to Address of the receiver.
* @param _amount Amount of ETH being bridged.
*/
function completeOutboundTransfer(
address _localToken,
address _remoteToken,
......@@ -269,14 +361,11 @@ abstract contract StandardBridge {
}
}
/**********************
* Internal Functions *
**********************/
/**
* @notice Initialize the StandardBridge contract with the address of
* the messenger on the same domain as well as the address of the bridge
* on the remote domain
* @notice Initializer.
*
* @param _messenger Address of CrossDomainMessenger on this network.
* @param _otherBridge Address of the other StandardBridge contract.
*/
function _initialize(address payable _messenger, address payable _otherBridge) internal {
require(address(messenger) == address(0), "Contract has already been initialized.");
......@@ -286,7 +375,15 @@ abstract contract StandardBridge {
}
/**
* @notice Bridge ETH to the remote chain through the messenger
* @notice Initiates a bridge of ETH through the CrossDomainMessenger.
*
* @param _from Address of the sender.
* @param _to Address of the receiver.
* @param _amount Amount of ETH being bridged.
* @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
* @param _extraData Extra data to be sent with the transaction. Note that the recipient will
* not be triggered with this data, but it will be emitted and can be used
* to identify the transaction.
*/
function _initiateBridgeETH(
address _from,
......@@ -311,7 +408,16 @@ abstract contract StandardBridge {
}
/**
* @notice Bridge an ERC20 to the remote chain through the messengers
* @notice Sends ERC20 tokens to a receiver's address on the other chain.
*
* @param _localToken Address of the ERC20 on this chain.
* @param _remoteToken Address of the corresponding token on the remote chain.
* @param _to Address of the receiver.
* @param _amount Amount of local tokens to deposit.
* @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
* @param _extraData Extra data to be sent with the transaction. Note that the recipient will
* not be triggered with this data, but it will be emitted and can be used
* to identify the transaction.
*/
function _initiateBridgeERC20(
address _localToken,
......@@ -351,7 +457,17 @@ abstract contract StandardBridge {
}
/**
* @notice Bridge an ERC20 to the remote chain through the messengers
* @notice Sends ERC20 tokens to a receiver's address on the other chain WITHOUT doing any
* validation. Be EXTREMELY careful when using this function.
*
* @param _localToken Address of the ERC20 on this chain.
* @param _remoteToken Address of the corresponding token on the remote chain.
* @param _to Address of the receiver.
* @param _amount Amount of local tokens to deposit.
* @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
* @param _extraData Extra data to be sent with the transaction. Note that the recipient will
* not be triggered with this data, but it will be emitted and can be used
* to identify the transaction.
*/
function _initiateBridgeERC20Unchecked(
address _localToken,
......@@ -383,10 +499,11 @@ abstract contract StandardBridge {
}
/**
* Checks if a given address is an OptimismMintableERC20. Not perfect, but good enough.
* Just the way we like it.
* @notice Checks if a given address is an OptimismMintableERC20. Not perfect, but good enough.
* Just the way we like it.
*
* @param _token Address of the token to check.
*
* @return True if the token is an OptimismMintableERC20.
*/
function _isOptimismMintableERC20(address _token) internal view returns (bool) {
......@@ -394,10 +511,11 @@ abstract contract StandardBridge {
}
/**
* Checks if the "other token" is the correct pair token for the OptimismMintableERC20.
* @notice Checks if the "other token" is the correct pair token for the OptimismMintableERC20.
*
* @param _mintableToken OptimismMintableERC20 to check against.
* @param _otherToken Pair token to check.
* @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)
......
......@@ -5,17 +5,17 @@ pragma solidity ^0.8.9;
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
interface IRemoteToken {
function mint(address _to, uint256 _amount) external virtual;
function mint(address _to, uint256 _amount) external;
function burn(address _from, uint256 _amount) external virtual;
function burn(address _from, uint256 _amount) external;
function remoteToken() external virtual;
function remoteToken() external;
}
interface IL1Token {
function mint(address _to, uint256 _amount) external virtual;
function mint(address _to, uint256 _amount) external;
function burn(address _from, uint256 _amount) external virtual;
function burn(address _from, uint256 _amount) external;
function l1Token() external virtual;
function l1Token() external;
}
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