From 7427eb94210b487bacca6939a6552f173b3019a3 Mon Sep 17 00:00:00 2001 From: 0xParticle Date: Wed, 24 Jul 2024 15:57:42 -0300 Subject: [PATCH 01/12] interop: liquidity migration specs --- specs/interop/liquidity_migration.md | 358 +++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 specs/interop/liquidity_migration.md diff --git a/specs/interop/liquidity_migration.md b/specs/interop/liquidity_migration.md new file mode 100644 index 000000000..726cc4e3b --- /dev/null +++ b/specs/interop/liquidity_migration.md @@ -0,0 +1,358 @@ +# Liquidity Migration + + + + +- [Overview](#overview) +- [Implementation changes](#implementation-changes) + - [External functions](#external-functions) + - [`convertToSuper`](#converttosuper) + - [`convertFromSuper`](#convertfromsuper) + - [Events](#events) + - [`ConvertedToSuper`](#convertedtosuper) + - [`ConvertedFromSuper`](#convertedfromsuper) + - [Internal Functions](#internal-functions) + - [`checkPair`](#checkpair) +- [Diagram](#diagram) +- [Invariants](#invariants) +- [Considerations](#considerations) +- [Appendix](#appendix) + - [Access Control](#access-control) + - [Decimal management](#decimal-management) + + + +## Overview + +The `OptimismMintableERC20` tokens and `L2StandardToken`s (_legacy tokens_), +which correspond to locked liquidity in L1, are incompatible with interop. +Legacy token owners must convert into a `SuperchainERC20` representation, as defined in the [standard](token-bridging.md), +to move across the Superchain. + +The conversion method will use the `L2StandardBridge` mint/burn rights +over the legacy tokens to allow easy migration to and from the +corresponding `SuperchainERC20`. + +## Implementation changes + +The `L2StandardBridge` will be extended to include the following functions and events: + +### External functions + +#### `convertToSuper` + +Converts `_amount` of legacy tokens with address `_legacyAddr` +to the same `_amount` of `SuperchainERC20` tokens in address `_superAddr`. + +```solidity +convertToSuper(address _legacyAddr, address _superAddr, uint256 _amount) +``` + +The function will + +1. Check that `_legacyAddr` and `_superAddr` are valid and paired. +2. Check both tokens have the same amount of decimals. +3. Burn `_amount` of `_legacyAddr` from `msg.sender`. +4. Mint `_amount` of `_superAddr` to `msg.sender`. + +An example implementation that depends on the `checkPair()` +access control function looks like this: + +```solidity +function convertToSuper(address _legacyAddr, address _superAddr, uint256 _amount) public { + require(checkPair(_legacyAddr, _superAddr), "Invalid address pair"); + require(IERC20(_legacyAddr).decimals() == IERC20(_superAddr).decimals(), "Decimals do not match") + + IERC20(_legacyAddr).burn(msg.sender, _amount); + IERC20(_superAddr).mint(msg.sender, _amount); + + emit ConvertedToSuper(_legacyAddr, _superAddr, msg.sender, _amount); +} +``` + +#### `convertFromSuper` + +Converts `_amount` of `SuperchainERC20` tokens in address `_superAddr` +to the same `_amount` of legacy tokens with address `_legacyAddr`. + +```solidity +convertToSuper(address _legacyAddr, address _superAddr, uint256 _amount) +``` + +The function will + +1. Check that `_legacyAddr` and `_superAddr` are valid and paired. +2. Check both tokens have the same amount of decimals. +3. Burn `_amount` of `_superAddr` from `msg.sender`. +4. Mint `_amount` of `_legacyAddr` to `msg.sender`. + +An example implementation that depends on the `checkPair()` +access control function looks like this: + +```solidity +function convertFromSuper(address _legacyAddr, address _superAddr, uint256 _amount) public { + require(checkPair(_legacyAddr, _superAddr), "Invalid address pair"); + require(IERC20(_legacyAddr).decimals() == IERC20(_superAddr).decimals(), "Decimals do not match") + + IERC20(_superAddr).burn(msg.sender, _amount); + IERC20(_legacyAddr).mint(msg.sender, _amount); + + emit ConvertedFromSuper(_legacyAddr, _superAddr, msg.sender, _amount); +} +``` + +### Events + +#### `ConvertedToSuper` + +It MUST trigger when anyone converts legacy tokens +to `SuperchainERC20` with `convertToSuper`. + +```solidity +event ConvertedToSuper(address indexed legacyAddr, address indexed superAddr, address indexed caller, uint256 amount, uint256 adjustedAmount); +``` + +#### `ConvertedFromSuper` + +It MUST trigger when anyone converts `SuperchainERC20` +into legacy tokens with `convertFromSuper`. + +```solidity +event ConvertedFromSuper(address indexed legacyAddr, address indexed superAddr, address indexed caller, uint256 amount); +``` + +### Internal Functions + +#### `checkPair` + +Checks the validity of the `_legacyAddr` and `_superAddr` pair. +To do so, it will verify: + +1. Valid `OptimismMintableERC20` address: + + 1. For representations minted before + the `OptimismMintableERC20Factory` upgrade: + Check an allowed token list that connects L2 representations with the + corresponding token address in the native chain (_remote token_ address). + The allowed list will live in the `OptimismMintableERC20Factory` + and will be a mapping from the legacy address to the _remote token_ address + (L1 token address for tokens native to L1). + 2. For representations minted after + the `OptimismMintableERC20Factory` upgrade: + Verify the `OptimismMintableERC20Factories` deployment lists. + This can be the same mapping used for 1a. + + This approach requires updating + the `OptimismMintableERC20Factory` to store + deployments as a mapping from `OptimismMintableERC20` + to its `REMOTE_TOKEN` + (both allowed list and new deployments). + +2. Valid `SuperchainERC20` address: + checks the `SuperchainERC20Factory` mapping + that stores the corresponding remote token address + for each `SuperchainERC20` address. +3. Both tokens correspond to the same remote token address: + it will compare the remote token addresses stored in the deployment + mappings and revert with `InvalidPair` if they do not match. + +You can read more details about access control in the [Appendix](#decimal-management). + +A reference implementation would look like follows + +```solidity +IOptimismMintableERC20Factory public factory = IOptimismMintableERC20Factory(Predeploys.OptimismMintableERC20Factory); +ISuperchainERC20Factory public superFactory = ISuperchainERC20Factory(Predeploys.SuperchainERC20Factory); + +function isValidPair(address _legacyAddr, address _superAddr) internal view returns (bool) { + address _legacyRemoteToken = factory.deployments(_legacyAddr); + require(_legacyRemoteToken != address(0), "Invalid Legacy address"); + + address _superRemoteToken = superFactory.deployments(_superAddr); + require(_superRemoteToken != address(0), "Invalid SuperchainERC20 address"); + + require(_legacyRemoteToken == _superRemoteToken, "Invalid token pair"); + + return true; +} +``` + +Notes: + +- It is possible to split the `deployments` mapping in the + `OptimismMintableERC20Factory` into two + and make more explicit the distinction between pre-upgrade and + post-upgrade deployments. We suggest sticking to the single + mapping as it improves readability. +- Code can be simplified by using || in + the requires or conditionals. + The tradeoff would be losing error precision. + +## Diagram + +`convertToSuper` + +```mermaid +sequenceDiagram + participant Alice + participant L2StandardBridge + participant factory as OptimismMintableERC20Factory + participant superfactory as SuperchainERC20Factory + participant SuperERC20 as SuperchainERC20 + participant legacy as Legacy Token + + Alice->>L2StandardBridge: convertToSuper(legacyAddr, superAddr, amount) + L2StandardBridge-->factory: check legacy token is allowed + L2StandardBridge-->superfactory: check super token is allowed + L2StandardBridge-->L2StandardBridge: checks matching remote and decimals + L2StandardBridge->>legacy: if checks passed-> burn(Alice, amount) + L2StandardBridge->>superERC20: mint(Alice, AdjustedAmount) + +``` + +`convertFromSuper` + +```mermaid +sequenceDiagram + participant Alice + participant L2StandardBridge + participant factory as OptimismMintableERC20Factory + participant superfactory as SuperchainERC20Factory + participant legacy as Legacy Token + participant SuperERC20 as SuperchainERC20 + + Alice->>L2StandardBridge: convertFromSuper(legacyAddr, superAddr, amount) + L2StandardBridge-->factory: check legacy token is allowed + L2StandardBridge-->superfactory: check super token is allowed + L2StandardBridge-->L2StandardBridge: checks matching remote and decimals + L2StandardBridge->>superERC20: if checks passed-> burn(Alice, amount) + L2StandardBridge->>legacy: mint(Alice, AdjustedAmount) + +``` + +## Invariants + +The conversion flow will need to conserve the following invariants: + +- Conservation of amount: + The burnt amount should match the minted amount. - It is possible to relax this invariant and adjust the amount + based on decimal difference. + An example implementation can be found in the [Appendix](#decimal-management). +- Revert for non valid or non paired: + Both `convertFromSuper` and `convertToSuper` + SHOULD revert when called with: - Legacy tokens that are not in the allowed list or were not + deployed by the `OptimismMintableERC20Factory` . - `SuperchainERC20`s not deployed + by the `SuperchainERC20Factory`. - Legacy tokens and `SuperchainERC20`s + corresponding to different + remote token addresses. +- Freedom of conversion for valid and paired tokens: + anyone can convert between allowed legacy representations and + valid `SuperchainERC20s` corresponding to the same remote token. + +## Considerations + +- The `OptimismMintableERC20Factory` should be upgraded to + the latest version and include the allowed token list. +- Tokens in the allowed list and newly deployed tokens in both + factories should implement `decimals()`. +- The token list should only allow legacy tokens that: + - Are immutable: connecting upgradable tokens + would pose a risk for locked liquidity of the other representations. + - Implement a valid interface, such as `IOptimismMintableERC20` + or `IL2StandardToken`. + - Deployed with 0 supply: tokens are native to L1, + so legacy supply can only grow with deposits from L1. + - Can only be minted by the `L2StandardBridge`. + - Doesn’t implement fancy functions: + e.g. additional mint rights, ability to change l1token, l2bridge, etc. + This is hard to measure and will require social consensus. +- Any `SuperchainERC20` used for conversion will need to allow + the `L2StandardBridge` to mint and burn tokens. + This can be achieved with an extension to the standard or by overriding + the mint and burn functions. +- For custom implementations: Can we have clusters of allowed L2 representations for the same L1 token? + This means that each representation can convert within the same cluster but not with the others, + even if the L1 token matches. - Example: CRV in L1 has CRVa and CRVb as two representations in an L2. + Can I have a SuperCRVa and a SuperCRVb so that CRVa can only convert to SuperCRVa + and CRVb converts to SuperCRVb? - If we do, then we need to know for each Legacy Factory + which is the allowed Superchain Factory - If not, it’s enough to compare + the L1 token (REMOTE_TOKEN) + +## Appendix + +### Access Control + +Each `SuperchainERC20` will correspond to a a remote token and can be connected +to multiple existing legacy representations. +Furthermore, each legacy representation can be connected to various +`SuperchainERC20` (one per metadata). +This also implies that users can freely move between allowed legacy +representations and `SuperchainERC20s` using the convert functions +(call `convertToSuper(legacy1Addr,...)` and then +`convertFromSuper(legacy2Addr,...)` for example). +As legacy tokens can be used to unlock liquidity in the native chain +(L1 for tokens in the Standard Bridge), it is fundamental only +to allow safe representations to connect. + +Both `convertFromSuper` and `convertToSuper` will first +check the validity of the input addresses by calling the `checkPair` +internal function. +The function will check that the `SuperchainERC20` address can +be converted with the `OptimismMintableERC20` address. + +The `checkPair` function will call a Token List which will act as an allow-list +of tokens that unify representations that used outdated but legit factories. +This Token List will consist of mapping legacy addresses to remote addresses so +it can work as an allow-list and remove the need to check for interface +differences between `ILegacyMintableERC20` +and `IOptimismMintableERC20`. +The Token List will be a one-time update conducted by Optimism and will +live in the `OptimismMintableERC20Factory` contract. +If the legacy token is not included in the token list +(for new tokens, for instance), +the `checkPair` will verify validity against the factory. + +### Decimal management + +It is possible to remove the revert on decimal difference +and adjust the amounts and events accordingly: + +```solidity +function convertFromSuper(address _legacyAddr, address _superAddr, uint256 _amount) public { + require(checkPair(_legacyAddr, _superAddr), "Invalid address pair"); + uint256 _adjustedAmount = computeAmountOut(_legacyAddr, _superAddr, _amount); + + IERC20(_superAddr).burn(msg.sender, _amount); + IERC20(_legacyAddr).mint(msg.sender, _adjustedAmount); + + emit ConvertedFromSuper(_legacyAddr, _superAddr, msg.sender, _amount, _adjustedAmount); +} +``` + +Where the `computeAmountOut` function queries the decimals +for the ERC20 in `_legacyAddr` and `_superAddr` and +adjusts input `_amount` to mint the corresponding `_adjustedAmount`. + +A reference implementation of +`computeAmountOut` would look like follows: + +```solidity +function computeAmountOut(address _fromToken, address _toToken, uint256 _amountIn) internal view returns (uint256) { + uint8 _fromDecimals = IERC20(_fromToken).decimals(); + uint8 _toDecimals = IERC20(_toToken).decimals(); + + uint256 factor; + if (fromDecimals > toDecimals) { + factor = 10 ** (fromDecimals - toDecimals); + return _amountIn / factor; + } else { + factor = 10 ** (toDecimals - fromDecimals); + return _amountIn * factor; + } +} +``` + +Notice the amount adjustment will lead to precision loss. +A way to go around this issue is to adjust the burn amount accordingly +so that precision is conserved, +but this approach would lead to dust balances. From a9b993b5e1fae90044ab1759d88748b7f1547e14 Mon Sep 17 00:00:00 2001 From: 0xParticle Date: Wed, 24 Jul 2024 16:09:06 -0300 Subject: [PATCH 02/12] feat: fixed code indentation --- specs/interop/liquidity_migration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/interop/liquidity_migration.md b/specs/interop/liquidity_migration.md index 726cc4e3b..1f85c6b75 100644 --- a/specs/interop/liquidity_migration.md +++ b/specs/interop/liquidity_migration.md @@ -92,7 +92,7 @@ access control function looks like this: ```solidity function convertFromSuper(address _legacyAddr, address _superAddr, uint256 _amount) public { require(checkPair(_legacyAddr, _superAddr), "Invalid address pair"); - require(IERC20(_legacyAddr).decimals() == IERC20(_superAddr).decimals(), "Decimals do not match") + require(IERC20(_legacyAddr).decimals() == IERC20(_superAddr).decimals(), "Decimals do not match") IERC20(_superAddr).burn(msg.sender, _amount); IERC20(_legacyAddr).mint(msg.sender, _amount); From aacb961a9472a751d0fd7aada028faf4d34d52ed Mon Sep 17 00:00:00 2001 From: 0xParticle <102677731+0xParticle@users.noreply.github.com> Date: Wed, 24 Jul 2024 16:11:26 -0300 Subject: [PATCH 03/12] feat: changed code indentation --- specs/interop/liquidity_migration.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/specs/interop/liquidity_migration.md b/specs/interop/liquidity_migration.md index 1f85c6b75..c6cedc494 100644 --- a/specs/interop/liquidity_migration.md +++ b/specs/interop/liquidity_migration.md @@ -2,7 +2,6 @@ - - [Overview](#overview) - [Implementation changes](#implementation-changes) - [External functions](#external-functions) @@ -62,10 +61,10 @@ access control function looks like this: function convertToSuper(address _legacyAddr, address _superAddr, uint256 _amount) public { require(checkPair(_legacyAddr, _superAddr), "Invalid address pair"); require(IERC20(_legacyAddr).decimals() == IERC20(_superAddr).decimals(), "Decimals do not match") - + IERC20(_legacyAddr).burn(msg.sender, _amount); IERC20(_superAddr).mint(msg.sender, _amount); - + emit ConvertedToSuper(_legacyAddr, _superAddr, msg.sender, _amount); } ``` From f438439dd8c23ed33fa82f29b08ac8356adb5e72 Mon Sep 17 00:00:00 2001 From: 0xParticle Date: Wed, 24 Jul 2024 16:14:53 -0300 Subject: [PATCH 04/12] feat: fix code indentation --- specs/interop/liquidity_migration.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/specs/interop/liquidity_migration.md b/specs/interop/liquidity_migration.md index c6cedc494..f04e6e6b9 100644 --- a/specs/interop/liquidity_migration.md +++ b/specs/interop/liquidity_migration.md @@ -2,6 +2,7 @@ + - [Overview](#overview) - [Implementation changes](#implementation-changes) - [External functions](#external-functions) @@ -59,13 +60,13 @@ access control function looks like this: ```solidity function convertToSuper(address _legacyAddr, address _superAddr, uint256 _amount) public { - require(checkPair(_legacyAddr, _superAddr), "Invalid address pair"); - require(IERC20(_legacyAddr).decimals() == IERC20(_superAddr).decimals(), "Decimals do not match") - - IERC20(_legacyAddr).burn(msg.sender, _amount); - IERC20(_superAddr).mint(msg.sender, _amount); - - emit ConvertedToSuper(_legacyAddr, _superAddr, msg.sender, _amount); + require(checkPair(_legacyAddr, _superAddr), "Invalid address pair"); + require(IERC20(_legacyAddr).decimals() == IERC20(_superAddr).decimals(), "Decimals do not match") + + IERC20(_legacyAddr).burn(msg.sender, _amount); + IERC20(_superAddr).mint(msg.sender, _amount); + + emit ConvertedToSuper(_legacyAddr, _superAddr, msg.sender, _amount); } ``` @@ -319,7 +320,7 @@ and adjust the amounts and events accordingly: ```solidity function convertFromSuper(address _legacyAddr, address _superAddr, uint256 _amount) public { require(checkPair(_legacyAddr, _superAddr), "Invalid address pair"); - uint256 _adjustedAmount = computeAmountOut(_legacyAddr, _superAddr, _amount); + uint256 _adjustedAmount = computeAmountOut(_legacyAddr, _superAddr, _amount); IERC20(_superAddr).burn(msg.sender, _amount); IERC20(_legacyAddr).mint(msg.sender, _adjustedAmount); From 875ecd22745bb274ffea50cb0e1dea37f5add67a Mon Sep 17 00:00:00 2001 From: 0xParticle Date: Thu, 25 Jul 2024 12:07:26 -0300 Subject: [PATCH 05/12] feat: moved allowed list discussions to appendix and decimals checks inside checkPair --- specs/interop/liquidity_migration.md | 79 +++++++++++++--------------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/specs/interop/liquidity_migration.md b/specs/interop/liquidity_migration.md index f04e6e6b9..9ed4d8f68 100644 --- a/specs/interop/liquidity_migration.md +++ b/specs/interop/liquidity_migration.md @@ -50,10 +50,9 @@ convertToSuper(address _legacyAddr, address _superAddr, uint256 _amount) The function will -1. Check that `_legacyAddr` and `_superAddr` are valid and paired. -2. Check both tokens have the same amount of decimals. -3. Burn `_amount` of `_legacyAddr` from `msg.sender`. -4. Mint `_amount` of `_superAddr` to `msg.sender`. +1. Check that `_legacyAddr` and `_superAddr` are valid, paired and have the same amount of decimals. +2. Burn `_amount` of `_legacyAddr` from `msg.sender`. +3. Mint `_amount` of `_superAddr` to `msg.sender`. An example implementation that depends on the `checkPair()` access control function looks like this: @@ -81,10 +80,9 @@ convertToSuper(address _legacyAddr, address _superAddr, uint256 _amount) The function will -1. Check that `_legacyAddr` and `_superAddr` are valid and paired. -2. Check both tokens have the same amount of decimals. -3. Burn `_amount` of `_superAddr` from `msg.sender`. -4. Mint `_amount` of `_legacyAddr` to `msg.sender`. +1. Check that `_legacyAddr` and `_superAddr` are valid, paired and have the same amount of decimals. +2. Burn `_amount` of `_superAddr` from `msg.sender`. +3. Mint `_amount` of `_legacyAddr` to `msg.sender`. An example implementation that depends on the `checkPair()` access control function looks like this: @@ -92,7 +90,6 @@ access control function looks like this: ```solidity function convertFromSuper(address _legacyAddr, address _superAddr, uint256 _amount) public { require(checkPair(_legacyAddr, _superAddr), "Invalid address pair"); - require(IERC20(_legacyAddr).decimals() == IERC20(_superAddr).decimals(), "Decimals do not match") IERC20(_superAddr).burn(msg.sender, _amount); IERC20(_legacyAddr).mint(msg.sender, _amount); @@ -109,7 +106,7 @@ It MUST trigger when anyone converts legacy tokens to `SuperchainERC20` with `convertToSuper`. ```solidity -event ConvertedToSuper(address indexed legacyAddr, address indexed superAddr, address indexed caller, uint256 amount, uint256 adjustedAmount); +event ConvertedToSuper(address indexed legacyAddr, address indexed superAddr, address indexed caller, uint256 amount); ``` #### `ConvertedFromSuper` @@ -128,26 +125,12 @@ event ConvertedFromSuper(address indexed legacyAddr, address indexed superAddr, Checks the validity of the `_legacyAddr` and `_superAddr` pair. To do so, it will verify: -1. Valid `OptimismMintableERC20` address: - - 1. For representations minted before - the `OptimismMintableERC20Factory` upgrade: - Check an allowed token list that connects L2 representations with the - corresponding token address in the native chain (_remote token_ address). - The allowed list will live in the `OptimismMintableERC20Factory` - and will be a mapping from the legacy address to the _remote token_ address - (L1 token address for tokens native to L1). - 2. For representations minted after - the `OptimismMintableERC20Factory` upgrade: - Verify the `OptimismMintableERC20Factories` deployment lists. - This can be the same mapping used for 1a. - - This approach requires updating +1. Valid legacy address: + Verify the `OptimismMintableERC20Factories` deployment lists. This approach requires updating the `OptimismMintableERC20Factory` to store deployments as a mapping from `OptimismMintableERC20` - to its `REMOTE_TOKEN` - (both allowed list and new deployments). - + to its `REMOTE_TOKEN`. + The mapping will be backwards compatible, as detailed in the [Appendix](#backwards-compatibility). 2. Valid `SuperchainERC20` address: checks the `SuperchainERC20Factory` mapping that stores the corresponding remote token address @@ -155,6 +138,7 @@ To do so, it will verify: 3. Both tokens correspond to the same remote token address: it will compare the remote token addresses stored in the deployment mappings and revert with `InvalidPair` if they do not match. +4. Both tokens have the same amount of decimals. You can read more details about access control in the [Appendix](#decimal-management). @@ -164,29 +148,28 @@ A reference implementation would look like follows IOptimismMintableERC20Factory public factory = IOptimismMintableERC20Factory(Predeploys.OptimismMintableERC20Factory); ISuperchainERC20Factory public superFactory = ISuperchainERC20Factory(Predeploys.SuperchainERC20Factory); -function isValidPair(address _legacyAddr, address _superAddr) internal view returns (bool) { +function checkPair(address _legacyAddr, address _superAddr) internal view returns (bool) { + // Check validity of the legacy token address _legacyRemoteToken = factory.deployments(_legacyAddr); require(_legacyRemoteToken != address(0), "Invalid Legacy address"); + // Check validity of the superchainERC20 token address _superRemoteToken = superFactory.deployments(_superAddr); require(_superRemoteToken != address(0), "Invalid SuperchainERC20 address"); + // Check both tokens correspond to the same remote token require(_legacyRemoteToken == _superRemoteToken, "Invalid token pair"); + // Check both tokens share decimals + require(IERC20Metadata(_legacyAddr).decimals() == IERC20Metadata(_superAddr).decimals(), "Decimals do not match") + return true; } ``` -Notes: - -- It is possible to split the `deployments` mapping in the - `OptimismMintableERC20Factory` into two - and make more explicit the distinction between pre-upgrade and - post-upgrade deployments. We suggest sticking to the single - mapping as it improves readability. -- Code can be simplified by using || in - the requires or conditionals. - The tradeoff would be losing error precision. +Note: Code can be simplified by using || in +the requires or conditionals. +The tradeoff would be losing error precision. ## Diagram @@ -235,9 +218,10 @@ sequenceDiagram The conversion flow will need to conserve the following invariants: - Conservation of amount: - The burnt amount should match the minted amount. - It is possible to relax this invariant and adjust the amount - based on decimal difference. - An example implementation can be found in the [Appendix](#decimal-management). + The burnt amount should match the minted amount. + - It is possible to relax this invariant and adjust the amount + based on decimal difference. + An example implementation can be found in the [Appendix](#decimal-management). - Revert for non valid or non paired: Both `convertFromSuper` and `convertToSuper` SHOULD revert when called with: - Legacy tokens that are not in the allowed list or were not @@ -312,6 +296,17 @@ If the legacy token is not included in the token list (for new tokens, for instance), the `checkPair` will verify validity against the factory. +### Backwards compatibility + +The check on legacy address validity will query the upgraded `OptimismMintableERC20Factory`, +which stores new deployments in a mapping from +the legacy address to the _remote token_ address. +This method should also allow tokens that were +deployed before the factory upgrade. +To address this situation, a token list will be written to the same mapping +via storage manipulation. +How the allowed list is created is still to be defined. + ### Decimal management It is possible to remove the revert on decimal difference From 4e9ce648a6c0cc91a67eb78f82be74b5c42ff4d9 Mon Sep 17 00:00:00 2001 From: 0xParticle Date: Thu, 25 Jul 2024 17:00:28 -0300 Subject: [PATCH 06/12] feat: added an implementation sub section with modifications to the Factory --- specs/interop/liquidity_migration.md | 64 ++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/specs/interop/liquidity_migration.md b/specs/interop/liquidity_migration.md index 9ed4d8f68..c5b30aeb1 100644 --- a/specs/interop/liquidity_migration.md +++ b/specs/interop/liquidity_migration.md @@ -5,19 +5,21 @@ - [Overview](#overview) - [Implementation changes](#implementation-changes) - - [External functions](#external-functions) + - [L2StandardBridge](#l2standardbridge) - [`convertToSuper`](#converttosuper) - [`convertFromSuper`](#convertfromsuper) - - [Events](#events) - [`ConvertedToSuper`](#convertedtosuper) - [`ConvertedFromSuper`](#convertedfromsuper) - - [Internal Functions](#internal-functions) - [`checkPair`](#checkpair) + - [OptimismMintableERC20Factory](#optimismmintableerc20factory) + - [`deployments`](#deployments) + - [`createOptimismMintableERC20`](#createoptimismmintableerc20) - [Diagram](#diagram) - [Invariants](#invariants) - [Considerations](#considerations) - [Appendix](#appendix) - [Access Control](#access-control) + - [Backwards compatibility](#backwards-compatibility) - [Decimal management](#decimal-management) @@ -35,13 +37,15 @@ corresponding `SuperchainERC20`. ## Implementation changes -The `L2StandardBridge` will be extended to include the following functions and events: +Bothe the `L2StandardBridge` and the `OptimismMintableERC20TokenFactory` will be extended +to allow for liquidity migration: -### External functions +### L2StandardBridge #### `convertToSuper` -Converts `_amount` of legacy tokens with address `_legacyAddr` +The `L2StandardBridge` SHOULD add a `convertToSuper` external function that +converts `_amount` of legacy tokens with address `_legacyAddr` to the same `_amount` of `SuperchainERC20` tokens in address `_superAddr`. ```solidity @@ -71,7 +75,8 @@ function convertToSuper(address _legacyAddr, address _superAddr, uint256 _amount #### `convertFromSuper` -Converts `_amount` of `SuperchainERC20` tokens in address `_superAddr` +The `L2StandardBridge` SHOULD add a `convertFromSuper` external function that +converts `_amount` of `SuperchainERC20` tokens in address `_superAddr` to the same `_amount` of legacy tokens with address `_legacyAddr`. ```solidity @@ -98,11 +103,10 @@ function convertFromSuper(address _legacyAddr, address _superAddr, uint256 _amou } ``` -### Events - #### `ConvertedToSuper` -It MUST trigger when anyone converts legacy tokens +The `L2StandardBridge` SHOULD add a `ConvertedToSuper` event +that MUST trigger when anyone converts legacy tokens to `SuperchainERC20` with `convertToSuper`. ```solidity @@ -111,18 +115,18 @@ event ConvertedToSuper(address indexed legacyAddr, address indexed superAddr, ad #### `ConvertedFromSuper` -It MUST trigger when anyone converts `SuperchainERC20` +`ConvertedFromSuper` event +that MUST trigger when anyone converts `SuperchainERC20` into legacy tokens with `convertFromSuper`. ```solidity event ConvertedFromSuper(address indexed legacyAddr, address indexed superAddr, address indexed caller, uint256 amount); ``` -### Internal Functions - #### `checkPair` -Checks the validity of the `_legacyAddr` and `_superAddr` pair. +The `L2StandardBridge` SHOULD add a `checkPair` internal function that +checks the validity of the `_legacyAddr` and `_superAddr` pair. To do so, it will verify: 1. Valid legacy address: @@ -171,6 +175,38 @@ Note: Code can be simplified by using || in the requires or conditionals. The tradeoff would be losing error precision. +### OptimismMintableERC20Factory + +#### `deployments` + +The `OptimismMintableERC20Factory` SHOULD include a new mapping that stores the remote +token address for each deployed `OptimismMintableERC20`. + +```solidity +mapping(address => address) public deployments; +``` + +The mapping will be updated on new updates and only once +manually via storage manipulation to include the list of allowed tokens +(see the [Appendix](#backwards-compatibility)). + +#### `createOptimismMintableERC20` + +The function should be modified to update the `deployments` mapping. + +```solidity +function createOptimismMintableERC20(address _remoteToken, string memory _name, string memory _symbol) public returns (address) { + require(_remoteToken != address(0), "OptimismMintableERC20Factory: must provide remote token address"); + bytes32 salt = keccak256(abi.encode(_remoteToken, _name, _symbol)); + address localToken = address(new OptimismMintableERC20{salt: salt}(BRIDGE, _remoteToken, _name, _symbol)); + ... + // Add the following line + deployments[localToken] = _remoteToken; + // + return localToken; +} +``` + ## Diagram `convertToSuper` From 3b5134d3aeca59ddfad281819f2104688d9d0488 Mon Sep 17 00:00:00 2001 From: 0xParticle Date: Fri, 26 Jul 2024 13:27:16 -0300 Subject: [PATCH 07/12] feat: changed implementation to a single convert function and fixed multiple minor comments --- specs/interop/liquidity_migration.md | 320 ++++++++------------------- 1 file changed, 98 insertions(+), 222 deletions(-) diff --git a/specs/interop/liquidity_migration.md b/specs/interop/liquidity_migration.md index c5b30aeb1..417cb0b11 100644 --- a/specs/interop/liquidity_migration.md +++ b/specs/interop/liquidity_migration.md @@ -6,12 +6,11 @@ - [Overview](#overview) - [Implementation changes](#implementation-changes) - [L2StandardBridge](#l2standardbridge) - - [`convertToSuper`](#converttosuper) - - [`convertFromSuper`](#convertfromsuper) - - [`ConvertedToSuper`](#convertedtosuper) - - [`ConvertedFromSuper`](#convertedfromsuper) - - [`checkPair`](#checkpair) + - [`convert`](#convert) + - [`Converted`](#converted) + - [`_validatePair`](#_validatepair) - [OptimismMintableERC20Factory](#optimismmintableerc20factory) + - [Version](#version) - [`deployments`](#deployments) - [`createOptimismMintableERC20`](#createoptimismmintableerc20) - [Diagram](#diagram) @@ -20,7 +19,6 @@ - [Appendix](#appendix) - [Access Control](#access-control) - [Backwards compatibility](#backwards-compatibility) - - [Decimal management](#decimal-management) @@ -37,114 +35,68 @@ corresponding `SuperchainERC20`. ## Implementation changes -Bothe the `L2StandardBridge` and the `OptimismMintableERC20TokenFactory` will be extended +Both the `L2StandardBridge` and the `OptimismMintableERC20TokenFactory` will be extended to allow for liquidity migration: ### L2StandardBridge -#### `convertToSuper` +#### `convert` -The `L2StandardBridge` SHOULD add a `convertToSuper` external function that -converts `_amount` of legacy tokens with address `_legacyAddr` -to the same `_amount` of `SuperchainERC20` tokens in address `_superAddr`. +The `L2StandardBridge` SHOULD add a `convert` public function that +converts `_amount` of `_from` token to `_amount` of `_to` token, +if and only if the token addresses are valid (as defined below). ```solidity -convertToSuper(address _legacyAddr, address _superAddr, uint256 _amount) +convert(address _from, address _to, uint256 _amount) ``` The function will -1. Check that `_legacyAddr` and `_superAddr` are valid, paired and have the same amount of decimals. -2. Burn `_amount` of `_legacyAddr` from `msg.sender`. -3. Mint `_amount` of `_superAddr` to `msg.sender`. +1. Check that `_from` and `_to` addresses are valid, paired and have the same amount of decimals. +2. Burn `_amount` of `_from` from `msg.sender`. +3. Mint `_amount` of `_to` to `msg.sender`. -An example implementation that depends on the `checkPair()` +An example implementation that depends on the `_validatePair()` access control function looks like this: ```solidity -function convertToSuper(address _legacyAddr, address _superAddr, uint256 _amount) public { - require(checkPair(_legacyAddr, _superAddr), "Invalid address pair"); - require(IERC20(_legacyAddr).decimals() == IERC20(_superAddr).decimals(), "Decimals do not match") - - IERC20(_legacyAddr).burn(msg.sender, _amount); - IERC20(_superAddr).mint(msg.sender, _amount); - - emit ConvertedToSuper(_legacyAddr, _superAddr, msg.sender, _amount); -} -``` - -#### `convertFromSuper` - -The `L2StandardBridge` SHOULD add a `convertFromSuper` external function that -converts `_amount` of `SuperchainERC20` tokens in address `_superAddr` -to the same `_amount` of legacy tokens with address `_legacyAddr`. - -```solidity -convertToSuper(address _legacyAddr, address _superAddr, uint256 _amount) -``` - -The function will - -1. Check that `_legacyAddr` and `_superAddr` are valid, paired and have the same amount of decimals. -2. Burn `_amount` of `_superAddr` from `msg.sender`. -3. Mint `_amount` of `_legacyAddr` to `msg.sender`. - -An example implementation that depends on the `checkPair()` -access control function looks like this: - -```solidity -function convertFromSuper(address _legacyAddr, address _superAddr, uint256 _amount) public { - require(checkPair(_legacyAddr, _superAddr), "Invalid address pair"); - - IERC20(_superAddr).burn(msg.sender, _amount); - IERC20(_legacyAddr).mint(msg.sender, _amount); - - emit ConvertedFromSuper(_legacyAddr, _superAddr, msg.sender, _amount); +function convert(address _from, address _to, uint256 _amount) public { + _validatePair(_from, _to); + IERC20(_from).burn(msg.sender, _amount); + IERC20(_to).mint(msg.sender, _amount); + emit Converted(_from, _to, msg.sender, _amount); } ``` -#### `ConvertedToSuper` +#### `Converted` -The `L2StandardBridge` SHOULD add a `ConvertedToSuper` event -that MUST trigger when anyone converts legacy tokens -to `SuperchainERC20` with `convertToSuper`. +The `L2StandardBridge` SHOULD add a `Converted` event +that MUST trigger when anyone converts tokens +with `convert`. ```solidity -event ConvertedToSuper(address indexed legacyAddr, address indexed superAddr, address indexed caller, uint256 amount); +event Converted(address indexed from, address indexed to, address indexed caller, uint256 amount); ``` -#### `ConvertedFromSuper` +where `from` is the address of the input token, `to` is the address of the output token, +`caller` is the `msg.sender` of the function call and `amount` is the converted amount. -`ConvertedFromSuper` event -that MUST trigger when anyone converts `SuperchainERC20` -into legacy tokens with `convertFromSuper`. +#### `_validatePair` -```solidity -event ConvertedFromSuper(address indexed legacyAddr, address indexed superAddr, address indexed caller, uint256 amount); -``` +The `L2StandardBridge` SHOULD add a `_validatePair` internal function that +checks the validity of the `_from` and `_to` pair. +To do so, it will verify that: -#### `checkPair` - -The `L2StandardBridge` SHOULD add a `checkPair` internal function that -checks the validity of the `_legacyAddr` and `_superAddr` pair. -To do so, it will verify: - -1. Valid legacy address: - Verify the `OptimismMintableERC20Factories` deployment lists. This approach requires updating - the `OptimismMintableERC20Factory` to store - deployments as a mapping from `OptimismMintableERC20` - to its `REMOTE_TOKEN`. - The mapping will be backwards compatible, as detailed in the [Appendix](#backwards-compatibility). -2. Valid `SuperchainERC20` address: - checks the `SuperchainERC20Factory` mapping - that stores the corresponding remote token address - for each `SuperchainERC20` address. -3. Both tokens correspond to the same remote token address: +1. Both tokens have the same amount of decimals. +2. There is one valid legacy address: + Verify the `OptimismMintableERC20Factories` deployment lists. +3. There is one valid `SuperchainERC20` address: + Verify the `SuperchainERC20Factory` deployment list. +4. Both tokens correspond to the same remote token address: it will compare the remote token addresses stored in the deployment mappings and revert with `InvalidPair` if they do not match. -4. Both tokens have the same amount of decimals. -You can read more details about access control in the [Appendix](#decimal-management). +You can read more details about access control in the [Appendix](#access-control). A reference implementation would look like follows @@ -152,22 +104,28 @@ A reference implementation would look like follows IOptimismMintableERC20Factory public factory = IOptimismMintableERC20Factory(Predeploys.OptimismMintableERC20Factory); ISuperchainERC20Factory public superFactory = ISuperchainERC20Factory(Predeploys.SuperchainERC20Factory); -function checkPair(address _legacyAddr, address _superAddr) internal view returns (bool) { - // Check validity of the legacy token +function _validatePair(address _from, address _to) internal { + // 1. Decimals check + require(IERC20Metadata(_from).decimals() == IERC20Metadata(_to).decimals(), "Decimals do not match") + + if (_isOptimismMintableERC20(_from)) { + _validateFactories(_from, _to) + } else { + _validateFactories(_to, _from) + } +} + +function _validateFactories(address _legacyAddr, address _superAddr) internal { + // 2. Valid legacy check address _legacyRemoteToken = factory.deployments(_legacyAddr); require(_legacyRemoteToken != address(0), "Invalid Legacy address"); - // Check validity of the superchainERC20 token + // 3. Valid SuperchainERC20 check address _superRemoteToken = superFactory.deployments(_superAddr); require(_superRemoteToken != address(0), "Invalid SuperchainERC20 address"); - // Check both tokens correspond to the same remote token + // 4. Same remote address check require(_legacyRemoteToken == _superRemoteToken, "Invalid token pair"); - - // Check both tokens share decimals - require(IERC20Metadata(_legacyAddr).decimals() == IERC20Metadata(_superAddr).decimals(), "Decimals do not match") - - return true; } ``` @@ -177,6 +135,13 @@ The tradeoff would be losing error precision. ### OptimismMintableERC20Factory +#### Version + +Ensure the `OptimismMintableERC20Factory` implementation uses CREATE2 instead of CREATE and decimals as part of the salt. +This is in line with the latest stable implementation in the +[optimism repo](OptimismMintableERC20Factory-repo) and +[mainnet deployment](https://etherscan.io/address/0xe01efbeb1089d1d1db9c6c8b135c934c0734c846#code). + #### `deployments` The `OptimismMintableERC20Factory` SHOULD include a new mapping that stores the remote @@ -186,50 +151,21 @@ token address for each deployed `OptimismMintableERC20`. mapping(address => address) public deployments; ``` -The mapping will be updated on new updates and only once -manually via storage manipulation to include the list of allowed tokens -(see the [Appendix](#backwards-compatibility)). +The mapping will be populated as a one-time update +via storage manipulation to include the list of allowed tokens +(see the [Appendix](#backwards-compatibility)), and on new deployments afterwards. #### `createOptimismMintableERC20` -The function should be modified to update the `deployments` mapping. +The [function](createOptimismMintable) should be modified to update the `deployments` mapping. ```solidity -function createOptimismMintableERC20(address _remoteToken, string memory _name, string memory _symbol) public returns (address) { - require(_remoteToken != address(0), "OptimismMintableERC20Factory: must provide remote token address"); - bytes32 salt = keccak256(abi.encode(_remoteToken, _name, _symbol)); - address localToken = address(new OptimismMintableERC20{salt: salt}(BRIDGE, _remoteToken, _name, _symbol)); - ... - // Add the following line - deployments[localToken] = _remoteToken; - // - return localToken; -} +deployments[localToken] = _remoteToken; ``` ## Diagram -`convertToSuper` - -```mermaid -sequenceDiagram - participant Alice - participant L2StandardBridge - participant factory as OptimismMintableERC20Factory - participant superfactory as SuperchainERC20Factory - participant SuperERC20 as SuperchainERC20 - participant legacy as Legacy Token - - Alice->>L2StandardBridge: convertToSuper(legacyAddr, superAddr, amount) - L2StandardBridge-->factory: check legacy token is allowed - L2StandardBridge-->superfactory: check super token is allowed - L2StandardBridge-->L2StandardBridge: checks matching remote and decimals - L2StandardBridge->>legacy: if checks passed-> burn(Alice, amount) - L2StandardBridge->>superERC20: mint(Alice, AdjustedAmount) - -``` - -`convertFromSuper` +`convert` ```mermaid sequenceDiagram @@ -237,16 +173,16 @@ sequenceDiagram participant L2StandardBridge participant factory as OptimismMintableERC20Factory participant superfactory as SuperchainERC20Factory - participant legacy as Legacy Token - participant SuperERC20 as SuperchainERC20 + participant legacy as from Token + participant SuperERC20 as to Token - Alice->>L2StandardBridge: convertFromSuper(legacyAddr, superAddr, amount) + Alice->>L2StandardBridge: convert(from, to, amount) L2StandardBridge-->factory: check legacy token is allowed L2StandardBridge-->superfactory: check super token is allowed L2StandardBridge-->L2StandardBridge: checks matching remote and decimals - L2StandardBridge->>superERC20: if checks passed-> burn(Alice, amount) - L2StandardBridge->>legacy: mint(Alice, AdjustedAmount) - + L2StandardBridge->>legacy: IERC20(from).burn(Alice, amount) + L2StandardBridge->>SuperERC20: IERC20(to).mint(Alice, amount) + L2StandardBridge-->L2StandardBridge: emit Converted(from, to, Alice, amount) ``` ## Invariants @@ -255,27 +191,27 @@ The conversion flow will need to conserve the following invariants: - Conservation of amount: The burnt amount should match the minted amount. - - It is possible to relax this invariant and adjust the amount - based on decimal difference. - An example implementation can be found in the [Appendix](#decimal-management). - Revert for non valid or non paired: - Both `convertFromSuper` and `convertToSuper` - SHOULD revert when called with: - Legacy tokens that are not in the allowed list or were not - deployed by the `OptimismMintableERC20Factory` . - `SuperchainERC20`s not deployed - by the `SuperchainERC20Factory`. - Legacy tokens and `SuperchainERC20`s - corresponding to different - remote token addresses. + - Tokens with different decimals. + `convert` SHOULD revert when called with: + - Legacy tokens that are not in the allowed list or were not + deployed by the `OptimismMintableERC20Factory`. + - `SuperchainERC20`s not deployed + by the `SuperchainERC20Factory`. + - Legacy tokens and `SuperchainERC20`s + corresponding to different + remote token addresses. - Freedom of conversion for valid and paired tokens: anyone can convert between allowed legacy representations and valid `SuperchainERC20s` corresponding to the same remote token. ## Considerations -- The `OptimismMintableERC20Factory` should be upgraded to +- The `OptimismMintableERC20Factory` SHOULD be upgraded to the latest version and include the allowed token list. - Tokens in the allowed list and newly deployed tokens in both - factories should implement `decimals()`. -- The token list should only allow legacy tokens that: + factories SHOULD implement `decimals()`. +- The token list SHOULD only allow legacy tokens that: - Are immutable: connecting upgradable tokens would pose a risk for locked liquidity of the other representations. - Implement a valid interface, such as `IOptimismMintableERC20` @@ -286,17 +222,12 @@ The conversion flow will need to conserve the following invariants: - Doesn’t implement fancy functions: e.g. additional mint rights, ability to change l1token, l2bridge, etc. This is hard to measure and will require social consensus. -- Any `SuperchainERC20` used for conversion will need to allow +- Any `SuperchainERC20` used for conversion MUST allow the `L2StandardBridge` to mint and burn tokens. This can be achieved with an extension to the standard or by overriding the mint and burn functions. -- For custom implementations: Can we have clusters of allowed L2 representations for the same L1 token? - This means that each representation can convert within the same cluster but not with the others, - even if the L1 token matches. - Example: CRV in L1 has CRVa and CRVb as two representations in an L2. - Can I have a SuperCRVa and a SuperCRVb so that CRVa can only convert to SuperCRVa - and CRVb converts to SuperCRVb? - If we do, then we need to know for each Legacy Factory - which is the allowed Superchain Factory - If not, it’s enough to compare - the L1 token (REMOTE_TOKEN) +- The `SuperchainERC20Factory` MUST include a `deployments` mapping + that stores the remote token address for each deployed `SuperchainERC20` address. ## Appendix @@ -307,83 +238,28 @@ to multiple existing legacy representations. Furthermore, each legacy representation can be connected to various `SuperchainERC20` (one per metadata). This also implies that users can freely move between allowed legacy -representations and `SuperchainERC20s` using the convert functions -(call `convertToSuper(legacy1Addr,...)` and then -`convertFromSuper(legacy2Addr,...)` for example). +representations and `SuperchainERC20s` using the convert function +(call `convert(legacy1Addr,superAddr,...)` and then +`convert(superAddr,legacy2Addr,...)` for example). As legacy tokens can be used to unlock liquidity in the native chain (L1 for tokens in the Standard Bridge), it is fundamental only to allow safe representations to connect. -Both `convertFromSuper` and `convertToSuper` will first -check the validity of the input addresses by calling the `checkPair` -internal function. -The function will check that the `SuperchainERC20` address can -be converted with the `OptimismMintableERC20` address. +### Backwards compatibility -The `checkPair` function will call a Token List which will act as an allow-list -of tokens that unify representations that used outdated but legit factories. -This Token List will consist of mapping legacy addresses to remote addresses so +As previously mentioned, the check on legacy address validity will query the `OptimismMintableERC20Factory`, +which stores a [`deployments`](#deployments) mapping from +the legacy address to the _remote token_ address, so it can work as an allow-list and remove the need to check for interface differences between `ILegacyMintableERC20` and `IOptimismMintableERC20`. -The Token List will be a one-time update conducted by Optimism and will -live in the `OptimismMintableERC20Factory` contract. -If the legacy token is not included in the token list -(for new tokens, for instance), -the `checkPair` will verify validity against the factory. - -### Backwards compatibility +The `deployments` mapping will be updated [whenever a new deployment occurs](#createoptimismmintableerc20). -The check on legacy address validity will query the upgraded `OptimismMintableERC20Factory`, -which stores new deployments in a mapping from -the legacy address to the _remote token_ address. -This method should also allow tokens that were +But convert should also be able to allow tokens that were deployed before the factory upgrade. To address this situation, a token list will be written to the same mapping -via storage manipulation. +via storage manipulation as a one-time update. How the allowed list is created is still to be defined. -### Decimal management - -It is possible to remove the revert on decimal difference -and adjust the amounts and events accordingly: - -```solidity -function convertFromSuper(address _legacyAddr, address _superAddr, uint256 _amount) public { - require(checkPair(_legacyAddr, _superAddr), "Invalid address pair"); - uint256 _adjustedAmount = computeAmountOut(_legacyAddr, _superAddr, _amount); - - IERC20(_superAddr).burn(msg.sender, _amount); - IERC20(_legacyAddr).mint(msg.sender, _adjustedAmount); - - emit ConvertedFromSuper(_legacyAddr, _superAddr, msg.sender, _amount, _adjustedAmount); -} -``` - -Where the `computeAmountOut` function queries the decimals -for the ERC20 in `_legacyAddr` and `_superAddr` and -adjusts input `_amount` to mint the corresponding `_adjustedAmount`. - -A reference implementation of -`computeAmountOut` would look like follows: - -```solidity -function computeAmountOut(address _fromToken, address _toToken, uint256 _amountIn) internal view returns (uint256) { - uint8 _fromDecimals = IERC20(_fromToken).decimals(); - uint8 _toDecimals = IERC20(_toToken).decimals(); - - uint256 factor; - if (fromDecimals > toDecimals) { - factor = 10 ** (fromDecimals - toDecimals); - return _amountIn / factor; - } else { - factor = 10 ** (toDecimals - fromDecimals); - return _amountIn * factor; - } -} -``` - -Notice the amount adjustment will lead to precision loss. -A way to go around this issue is to adjust the burn amount accordingly -so that precision is conserved, -but this approach would lead to dust balances. +[OptimismMintableERC20Factory-repo]: https://github.com/ethereum-optimism/optimism/blob/v1.8.0/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol +[createOptimismMintable]: https://github.com/ethereum-optimism/optimism/blob/ddc37daa49558c2fb5c1a92e694eeb7de5942e00/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol#L108 From 16b26d4dfa3990cad5d99bd7b14562c2e788bbfd Mon Sep 17 00:00:00 2001 From: 0xParticle <102677731+0xParticle@users.noreply.github.com> Date: Fri, 26 Jul 2024 14:09:07 -0300 Subject: [PATCH 08/12] feat: fix lint --- specs/interop/liquidity_migration.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/interop/liquidity_migration.md b/specs/interop/liquidity_migration.md index 417cb0b11..dda640c41 100644 --- a/specs/interop/liquidity_migration.md +++ b/specs/interop/liquidity_migration.md @@ -2,7 +2,6 @@ - - [Overview](#overview) - [Implementation changes](#implementation-changes) - [L2StandardBridge](#l2standardbridge) From 8d6660c4a60423c414b74552fa88988a28a8093d Mon Sep 17 00:00:00 2001 From: 0xParticle Date: Fri, 26 Jul 2024 14:22:22 -0300 Subject: [PATCH 09/12] feat: lint --- specs/interop/liquidity_migration.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specs/interop/liquidity_migration.md b/specs/interop/liquidity_migration.md index dda640c41..6faf0b9e0 100644 --- a/specs/interop/liquidity_migration.md +++ b/specs/interop/liquidity_migration.md @@ -2,6 +2,8 @@ +**Table of Contents** + - [Overview](#overview) - [Implementation changes](#implementation-changes) - [L2StandardBridge](#l2standardbridge) From d03ba29a29119ed5fcf6aa650245429775f27ae1 Mon Sep 17 00:00:00 2001 From: 0xParticle Date: Mon, 29 Jul 2024 10:27:19 -0300 Subject: [PATCH 10/12] feat: discussions on version --- specs/interop/liquidity_migration.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/specs/interop/liquidity_migration.md b/specs/interop/liquidity_migration.md index 6faf0b9e0..d30e104f3 100644 --- a/specs/interop/liquidity_migration.md +++ b/specs/interop/liquidity_migration.md @@ -134,14 +134,15 @@ Note: Code can be simplified by using || in the requires or conditionals. The tradeoff would be losing error precision. -### OptimismMintableERC20Factory - +### OptimismMintableERC20Factory #### Version +The `OptimismMintableERC20Factory` should not use CREATE as the creation method and should make the salt depend on +the token metadata. + +The CREATE2 version depends on the compiler and is therefore not suggested for the long term. For these reasons, it is recommended that the Factory version + uses CREATE3 for deployments. + -Ensure the `OptimismMintableERC20Factory` implementation uses CREATE2 instead of CREATE and decimals as part of the salt. -This is in line with the latest stable implementation in the -[optimism repo](OptimismMintableERC20Factory-repo) and -[mainnet deployment](https://etherscan.io/address/0xe01efbeb1089d1d1db9c6c8b135c934c0734c846#code). #### `deployments` From 7b3fed075cd7c58df024b1c50418af58499e5873 Mon Sep 17 00:00:00 2001 From: 0xParticle Date: Mon, 29 Jul 2024 10:30:23 -0300 Subject: [PATCH 11/12] feat: lint --- specs/interop/liquidity_migration.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/specs/interop/liquidity_migration.md b/specs/interop/liquidity_migration.md index d30e104f3..e8a676741 100644 --- a/specs/interop/liquidity_migration.md +++ b/specs/interop/liquidity_migration.md @@ -134,15 +134,16 @@ Note: Code can be simplified by using || in the requires or conditionals. The tradeoff would be losing error precision. -### OptimismMintableERC20Factory -#### Version -The `OptimismMintableERC20Factory` should not use CREATE as the creation method and should make the salt depend on -the token metadata. +### OptimismMintableERC20Factory -The CREATE2 version depends on the compiler and is therefore not suggested for the long term. For these reasons, it is recommended that the Factory version - uses CREATE3 for deployments. +#### Version +The `OptimismMintableERC20Factory` should not use CREATE as the creation method and should make the salt depend on +the token metadata. +The CREATE2 version depends on the compiler and is therefore not suggested for the long term. +For these reasons, it is recommended that the Factory version +uses CREATE3 for deployments. #### `deployments` From 64d24cbc3f181edd98b1e4a427feea16d44a8759 Mon Sep 17 00:00:00 2001 From: 0xParticle Date: Wed, 31 Jul 2024 09:59:22 -0300 Subject: [PATCH 12/12] feat: typo --- specs/interop/liquidity_migration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/interop/liquidity_migration.md b/specs/interop/liquidity_migration.md index e8a676741..6c00884a9 100644 --- a/specs/interop/liquidity_migration.md +++ b/specs/interop/liquidity_migration.md @@ -236,7 +236,7 @@ The conversion flow will need to conserve the following invariants: ### Access Control -Each `SuperchainERC20` will correspond to a a remote token and can be connected +Each `SuperchainERC20` will correspond to a remote token and can be connected to multiple existing legacy representations. Furthermore, each legacy representation can be connected to various `SuperchainERC20` (one per metadata).