-
Notifications
You must be signed in to change notification settings - Fork 124
[protocol 3.7] Loopring Ethport (Bridge and Converter) and AMM Optimizations (#2218) #2344
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
dong77
commented
Apr 17, 2021
dong77
commented
Apr 18, 2021
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Brecht, when you have new changes, would you create a PR on top of this branch?
#2347 is on top of this branch. |
dong77
commented
Apr 18, 2021
packages/loopring_v3/contracts/test/TestMigrationBridgeConnector.sol
Outdated
Show resolved
Hide resolved
dong77
commented
Apr 18, 2021
dong77
commented
Apr 18, 2021
dong77
commented
Apr 20, 2021
# Conflicts: # packages/loopring_v3/contracts/amm/libamm/AmmBlockReceiver.sol # packages/loopring_v3/contracts/aux/access/LoopringIOExchangeOwner.sol
Brechtpd
approved these changes
Apr 23, 2021
|
All tests pass. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
https://github.com/Loopring/protocols/wiki/L2-Vaults
Initial idea for using flash loans here: #1196 (comment)
Bridge
The Bridge works similarly to an AMM pool in that the contract is the owner of an account on layer 2, and on-chain we verify and approve transactions from and to this account. This is the bridge that is used to communicate from and to Loopring. Most times the Bridge account and contract contains no funds, it's simply used as an intermediate. To do communication with Loopring the bridge allows doing just two things.
1. Batch deposits
There is a
batchDepositcall that can be done by any external contract that simply deposits a number of tokens to the Bridge account on L2. The functions also takes in a list of transfers that need to be done on layer 2 from the bridge account to all other accounts in this specified list. So this way only one normal deposit for each distinct token is done, and all other batched deposits are done by simple transfers from the bridge account to the actual destination address on L2. The specified transfers are hashed and stored on-chain so we can later process them in a block, an event is logged with the data to make fetching this list of transfers easy. A uniquebatchIDis assigned to each batched deposit to make sure the hash is unique and can't fail. The operator doesn't have to do all these batch deposits, if they are too old they can be withdrawn from the bridge contract directly as a safety for both the operator (spam) and users (operator stops working). The bridge also allows a small max fee to be used from the batched deposits to pay for the L2 transfers.2. Bridge calls
The bridge also allows calling external contracts efficiently (with funds) using batching with the help of
Connectors. The idea of theConnectoris that an integration with some dApp is wanted, but we need to be able to do these efficiently in a batched way for all kinds of different scenarios like:All these can work differently for batching so we leave that up to the connector to implement, and so the bridge just calls the standard function
processCallsto let the connector do what's necessary. In some cases these connectors just need to do another L1 call and that's it. In other cases, for example a token swap, another token is bought that needs to be returned to the buyer on L2. This is where the batched deposits come in again to allow the connector to do just that in a standard way.These connectors also need to handle the funds in some way. In all cases all funds are first moved to the Bridge contract. This was chosen as a way to not impose any risks on all funds in the deposit contract, I think it's too risky to even call trusted functions directly on the deposit contract. But the current method is pretty efficient, in many cases there is only a need to do as many L1 token transfers as there are unique tokens to and from the Bridge contract, even if multiple connectors are called.
All connectors can do everything, but there is the concept of trusted and untrusted connectors for efficiency reasons, which gas a small implementation difference:
processCallson trusted connectors using a delegate call. This means no additional token transfers are needed to execute the code in the connector, but the connector has access to all funds currently being processed in the bridge.processCallson non-trusted connectors using a real call after transferring the necessary funds to the connector contract. This makes sure that only funds of users that want to interact with that connector are at risk in that connector.What is a trusted and untrusted connector can be set by the Bridge owner. Implementation wise this is only a very small difference at the Bridge side (the connector doesn't need to know), but for gas efficiency it can be worth it to make the connector trusted after we make sure it's safe. On the other hand, untrusted connectors are also great because users can call any connector without us having to worry about the connector misbehaving and putting other people's funds at risk.
No matter if the connector is trusted or not, connectors are given a max gas limit and are always allowed to fail in the
processCallscall. If the call fails the Bridge automatically approves the transfers back from the Bridge to the user's account.There is also the notion of a connector group. This is simply a way that the same connector can be used with all kinds of inputs. For example, for a uniswap connector a group would be the two tokens that need to be swapped, and this data is encoded in
groupDataand grouped together so all these swaps can be handled together.userDataon the other hand is for user specific data a connector could us. Think something like max slippage for a swap, or the destination address for a mass migration to a different rollup.Calls submitted to the connector will be sorted on group and tokens so they can be easily processed.
Similarly to the AMM pools, the user can sign a
BridgeCalltransaction which is verified on-chain using theSignatureVerificationtx so the L2 keys can be used. So every bridge call requires 2 L2 transactions: an L2 transfer and the signature verification tx.Fees
When people sign a transaction they don't know how big a batch it will be part of. The operator itself also doesn't always know how much a bridge call will end up costing.
To solve this problem users sign a transaction and a pay fee for an abosolute amount of gas that the operator needs to provide to the batched bridge calls. The total amount of gas the operator needs to provide to the connector is at least the sum of all these gas amounts.
The other problem is that, when just using this mechanism, the operator could submit a single bridge call that is very likely to have low gas amount just by itself, which will fail because the whole operation will take much more gas than what that single bridge call provides. And so there is an additional
IBridgeConnector.getMinGasLimitwhich also forces the operator to at least provide the minimum amount of gas required for all the calls by the connector itself.Converters
Converters are the implementation of the scheme described here. They are very efficient at doing many conversions from tokenA -> tokenB, but each converter needs its own token and contract deployed.BaseConverterhandles all the general code that can be reused between all converters (failure logic for the conversion, but also the main vault stuff like the token code, burning, minting, depositing, withdrawing,...). Specific implementations just need to implement a simpleconvertfunction to do the specific implementation for the conversion. There is no problem with rounding errors for the vault token transfers if the user doesn't pay a fee on this token (which I think wouldn't make a lot of sense) because the vault token is transferred in and out using a float representation.Everything Converters can do, the Bridge can do as well, but a bit less efficient in some cases (although the Bridge could also be more efficient in others). On the other, the Bridge can do everything converters can do and more (like mass migrations between L2s).
Example integrations
Converter
The converter can do anything that can be represented by a token <-> token trade, under parameters shared by everyone in the batch. Each conversion from tokenA -> tokenB needs a separate contract.
Bridge
The bridge can do anything the Converter can do and more. For example, the bridge can do anything that can be represented by a token <-> token trade, but now with parameters that can be defined by each user separately. The bridge also supports operations that only require tokens to go out (like mass migration). Only a single
BridgeConnectorcontract needs to be deployed to enable a certain kind of L1 operation for all tokens. For example, just a single Uniswap connector is able to convert between any tokens.Even L1 operations that are not batchable could have some small benefits by using the bridge because the token withdrawals/deposits from/to the exchange can be shared by all bridge operations.
Flash minting