Loopring / protocols

A zkRollup DEX & Payment Protocol
https://loopring.org
334 stars 123 forks source link

[Loopring 4.0] Allow minting tokens on layer2 for spot trading with layer1 collaterals. #1196

Closed dong77 closed 4 years ago

dong77 commented 4 years ago

The challenge

A challenge we have learned from operating Loopring.io is that market makers (MMs) will have to possess a lot of real assets to start providing service, because all tokens need to be real, and the exchange reserve ratio has to be 100%.

A potential solution

We want to make sure MMs can utilize their assets more efficiently, by reducing the exchange's reserve ratio but still making sure when users wish to withdraw, they can always get what they deserve.

To make this possible, we will need to change Loopring's spot trading protocol as follows (you'll see why):

On the smart contract level (layer1), we track how many tokens each account owe the exchange, and how much collateral each user has. For simplicity, let us assume all collaterals are Ether, but in theory, the collaterals can be any token with decent layer1 volumes. Allow permissionless offchain withdrawal. This means offchain withdrawals don't need signatures from the account owner -- the funds will always go to the original owner, not any other recipient address. Take LRC as an example, if Alice owes 0 LRC in the contract, when she withdraws LRC, the withdrawal works as the current design, her address will receive the LRC. If Alice owes 100 LRC on layer1, and withdraws 40 LRC from the exchange, her layer1 balance will become -60LRC, and she receives 0 LRC in her address. If Alice withdraws 1ETH, our contract will swap (using Kyber or Uniswap) some ETH to cover up to 60LRC, if there are still Ether left, those Ether will be sent to Alice's address; otherwise, there remains a smaller negative LRC balance on layer1. Note that if Alice owes any amount in any token, any withdrawals will have to pay back the owing token first. The actual delivery of token/ether can happen only when Alice owes nothing to the exchange.

Scenarios

Scenario 1

  1. Alice collateralize 1 ETH (=$200) to borrow-and-deposit 1,000,000 (=$2000USD), Her onchain balance is layer1=[-1,000,000LRC, 1ETH], offchain balance is layer2=[1,000,000 LRC]
  2. Alice does not trade and withdrawal all LRC. Now her balances are: layer1=[1ETH], layer2=[]. He can also withdraw 1 Ether from layer1.

Scenario 2

  1. Alice collateralize 1 ETH (=$200) to borrow-and-deposit 1,000,000 (=$2000USD), Her onchain balance is layer1=[-1,000,000LRC, 1ETH], offchain balance is layer2=[1,000,000 LRC]
  2. The price of LRC increased. If Alice puts more Ether on layer1 as collateral, she will be able to maintain the required margin percentage. Otherwise, the operator will create an offchain withdrawal request to withdraw as many LRC as possible and probably withdraw as many other tokens as possible to bring the margin ration above the requirement. As a last resort, the operator will swap the Ether collateral into LRC to pay the outstanding layer1 balance. If due to layer1 liquidity or oracle, Alice still owes some tokens after all layer2 tokens and her collaterals are liquidated, the exchange will have to cover the loss.

Scenario 2

  1. Alice collateralize 1 ETH (=$200) to borrow-and-deposit 1,000,000 (=$2000USD), Her onchain balance is layer1=[-1,000,000LRC, 1ETH], offchain balance is layer2=[1,000,000 LRC]
  2. Alice sold all LRC to Bob, now Bob request to withdraw all LRC.
  3. Before processing Bob's withdrawal, the operator will have to liquidate Alice's account to bring real LRC into the exchange from layer1 swap contracts.

Collateral Liquidiation

This feature is not designed to short/long a token, but for market makers to mint layer2 tokens out of collateral to trade. Therefore market makers are most likely to maintain the LRC balance on layer2 instead of selling a significant portion of them.

Liquidation of collaterals will base on the difference between the market maker's layer2 and layer1 balance, not the absolute borrowed amount. In scenario#1, if Alice never sold any LRC in layer2, her collaterals will not be liquidated due to change in LRC price.

Minimal Reserve Ratio

The exchange will probably require the overall reserve ratio for any token to be higher than a threshold. The total amount of token owed by all users shall not exceed the capacity of layer1 swapping contracts.

Brechtpd commented 4 years ago

Another idea: Let's say someone wants to make market for LRC/ETH.

All in a single Ethereum transaction (possibly with multiple blocks)

  1. Take out flash loan for LRC and/or ETH (can be done using the tokens in the exchange itself, and even other sources that allow flash loans if needed)
  2. Deposit tokens into loopring
  3. Do many trades
  4. Withdraw LRC and/or ETH tokens back out
  5. Buy/sell whatever LRC/ETH needed to pay back loan with onchain liquidity
  6. Close the flash loan

The market maker can just do trades with "virtual balances", but when we're creating the block it's possible to calculate what balance is needed to actually do all the trades and this amount is deposited before the trades are done in the blocks. This amount is loaned and paid back afterwards. It's possible there are some uncertainties for the market maker (e.g. if the market maker depends on a certain price to be available onchain, which is after the market maker actually did all trades). But all in all, the risks will be fairly small (similar to market making itself I guess).

The important thing to note is that there is no need to know beforehand how much is actually loaned and paid back. Orders can be created and matched for an account with no balances in it at all at the time the orders are matched on the exchange. Only once all blocks are put into blocks is it necessary to check how much needs to be loaned and how this loan can be paid back.

Main benefits:

I think this method is possible in 3.1 if block commitments and block verifies are done in a single Ethereum transaction. And the flash loan is opened using some other dapp because our current exchange implementation doesn't allow it.

In 3.5/4.0 this method will be even easier.

I have used "market maker" here, but this method only works when blocks are submitted exactly as needed onchain for the flash loan to work, so basically only we (the operator) can make this work. But I don't think that's necessary bad and perhaps an API could help to allow arbitrary market makers to make use of this as well.

Example

Alice wants to sell 1,000 ETH for 1,000,000 DAI. Alice may want to do that for any reason. For example, Alice knows she can sell the 1,000,000 DAI on uniswap for 1,010 ETH and make a profit. Or she knows she can use the 1,000,000 DAI to buy another token on Loopring and sell those tokens to get 1,020 ETH back. The reason doesn't matter, but Alice has a plan to make a profit, unfortunately she does not have 1,000 ETH in her Loopring wallet.

So let's say Alice has no balances at all in her account. But she creates an order selling 1,000 ETH for 1,000,000 DAI. This order may get filled completely, partially or not at all. At this point this does not matter. The hope is to sell up to 1,000 ETH at a certain price because that would be profitable. Alice's order gets completely filled and she now has a virtual balance of -1,000 ETH and +1,000,000 DAI (trade 1). This is a virtual balance, because that will not be Alice's balance when the trade is actually processed in a block. At this point we still don't know how much needs to be loaned or how it will be paid back.

At this point Alice also creates an order selling 1,000,000 DAI for 1,020 ETH because the price of ETH is decreasing. Alice is lucky and 50% of this order gets filled. The balance of Alice is now -490 ETH and +500.000 DAI (trade 2).

At this point the operator has enough trades to fill a couple of blocks and he wants to submit these blocks onchain. The operator runs over all the trades and sees that to do all the trades of Alice an additional 1,000 ETH is needed to make sure all trades can be done. In coordination with Alice, a flashloan of 1,000 ETH is opened for Alice and this 1,000 ETH is deposited to Alice account. At this point the trades can happen as usual inside the blocks. The balance after the deposit, but before any trade happens is 1,000 ETH and 0 DAI. After trade 1 the balance of Alice is 0 ETH and 1,000,000 DAI. After trade 2 the balance of Alice if 510 ETH and 500,000 DAI.

Great, all trades have happened and not once did we go negative and trade tokens that not exist. But we still need to pay back that flashloan. To do that, Alice withdraws 510 ETH and 500,000 DAI. The 500,000 DAI is sold on uniswap for 505 ETH. Alice now has 1015 ETH onchain and can repay the 1000 ETH flash loan, while she still has 15 ETH left as a profit. She got this profit by trading inside the exchange as well as by tapping into the available onchain liquidity.

dong77 commented 4 years ago

@Brechtpd I understand the general idea but didn't manage to understand the details. So please provide more information.

I have this related idea, which may work together with your solution.

Allow layer2 account to have a temporary negative balance

If Alice has 100LRC, she can sell 300 LRC in one trade, then buy back 250 LRC in another trade. Her ending balance in a block is now 50LRC. We allow her balance to become -200LRC in the middle of a block.

Then a market maker can create orders whose combined quality is greater than his actual layer2 balance.

This looks like a simplified layer-2 flash-loan without layer-2 composability.

Brechtpd commented 4 years ago

Negative balance is of course also a possible way to do this, but only negative balances has some drawbacks:

  1. The protocol needs to support these negative balances.
  2. The protocol needs to make sure that negative balances don't stay negative (either within a block, or over multiple blocks that are submitted onchain at the same time, similar to flashloan but actually worse, see below).
  3. No direct support to tap into onchain liquidity

Especially 2. is challenging if any account could have negative balances. This could probably be mitigated if we only have single account that can go negative and all accounts "mint" from this account, that way the operator needs to make sure only those balances are back to positive at the end of the block for example. Certainly doable, but still a bit troublesome. But the limited timespan a balance can remain negative is still a big limitation. And this negative balance also needs to be made positive again within the exchange, which is also quite the limitation when there's limited liquidity in the exchange itself.

3 is perhaps the biggest drawback to just supporting temporary negative balances. There's lots of places where liquidity can be sourced onchain, but if it's confined to just within the layer 2 there's still no immediate way to bring in new real liquidity. You can do lots of big trades within a short timespan, but the fact that the balance needs to be positive again within a short timespan (we can't allow those balances to remain negative) seems to be a pretty big limitation. You can do big trades, but you almost need to do the opposite trade shortly afterwards to get your balance positive again. So useful for arbitrage within the exchange, but still quite limited.

By moving real tokens in and out of the exchange with a flashloan this isn't the case. The full system just needs to balance out within a single Ethereum transaction, but the balances inside the exchange itself can be wildly different. If you sell 1,000 ETH for 1,000,000 DAI you actually move 1000 ETH into the exchange and possibly 1,000,000 DAI back out the exchange. This is exactly what would happen in a real trade, and this change within the exchange can be permanent.

Basically with just negative balances the exchange itself needs to remain balanced. With actual flash loans the total system (exchange + other defi) needs to remain balanced, which provides bigger opportunities over larger timeframes.

dong77 commented 4 years ago

Regarding your example.

I'm having a hard time figuring out how these steps can be put into one single Ethereum tx, especially when there are multiple layer1 interactions.


Below is what I imageed, but I'm not sure it's your design.

Alice signs 3 requests and sends them to the operator:

  1. A flash loan of 1000 ETH,
  2. An offchain withdrawal request of up to 1000 ETH, with the flash loan contract as the recipient
  3. An offchain withdrawal request of up to 1000000 LRC, with uniswap LRC/ETH as the recipient.

The Operator submit one Ethereum tx with the following inner-txs:

dong77 commented 4 years ago

@Brecht, I think our proposals have one thing in common at least. At some point, the MM's account will have a negative balance. The difference is how long can the negative persist. We have the following options:

  1. A negative balance can exist only within a block, after all the requests in a block has been processed, all balances must be >=0.
  2. A negative balance can exist across blocks, but only within an Ethreum tx.
  3. A negative balance can exist across Ethereum tx.

My original idea is related to #3, my second idea is related to #1, your idea is related to #2.

The real difference between #2 and #3 is that in #2, the operator has to find a way to liquidate the user's account to payback the flash loan for each Ethereum TX, because there is no collateral. This processing can be considered as a special case of #3 where the account is under-collateralized and need liquidation.

Brechtpd commented 4 years ago

Alice signs 3 requests and sends them to the operator:

  1. A flash loan of 1000 ETH,
  2. An offchain withdrawal request of up to 1000 ETH, with the flash loan contract as the recipient
    1. An offchain withdrawal request of up to 1000000 LRC, with uniswap LRC/ETH as the recipient.

Something like this. Point 1 Isn't really needed because the operator can just deposit to Alice's account, no authorization needed for that. Point 2 and 3 could be needed, but if Alice doesn't store any personal funds in it (as this isn't really needed) then Alice may as well share the trading key pair with the operator or allow the operator to create onchain withdrawal requests for Alice using an Agent.

Basically, in any scenario the operator has full control and also needs to have full control because the operator needs to make sure the loan can be paid back in all cases (otherwise trades that happened on the exchange would need to be reverted which would be bad). The operator decides what's included in a block and the market maker needs to convince the operator the loan can be paid back somehow.

The easiest case is of course if the market maker is the operator itself. But I think it's also possible for 3rd parties to make use of this by providing the operator with some guarantees that show what it's doing will not result in the loan not being able to be paid back, but this may be a little more limited because the operator needs to be able to make the decision to do the trade or not.

  1. Perhaps a unified market maker account contract that provides some functionality the operator can understand could be helpful for this. In the simple case the market maket just wants to swap the tokens on uniswap the market maker just shows what the price is on uniswap and the operator sees that this could indeed be a good trade.
  2. The market maker account contract can also need some additional funds to make sure the loan can be paid back even if things go wrong and a loss is made. This could be seen as the collateral or maybe even an insurance fund. The big difference here is that these funds are not critical to the security of the system and if things go very wrong the worst thing that can happen is that the trades that only happened on the exchange are reverted (similar to how trades that currently happened in a block that is only committed can still be reverted if it is not verified).

The Operator submit one Ethereum tx with the following inner-txs:

  • Take the loan of 1000 ETH and deposit it to Alice's account [ in a deposit block]
  • Trade1 & Trade2 [ in a trade block]
  • Withdraw the 510 ETH to pay the flash loan [ in a withdrawal block]
  • Withdrawl LRC token to swap to ETH then pay the loan. [ in a withdrawal block]

There's actually a problem here that I didn't think of. The deposit needs to happen in the same tx, so the deposit block needs to contain all the deposits up to the flash loan deposit + the flash loan deposit. Unfortunately it is possible that someone made a deposit between the time the deposit block was created and the time the flash loan deposit is made onchain. This means the deposits handled in the deposit blocks are invalid because those extra deposits should have been handled in that deposit block (we force deposits to be handled in the order they are done onchain). We need a more flexible way to deposit if we want to be able to do this.

I think our proposals have one thing in common at least. At some point, the MM's account will have a negative balance. The difference is how long can the negative persist. We have the following options:

  1. A negative balance can exist only within a block, after all the requests in a block has been processed, all balances must be >=0.
  2. A negative balance can exist across blocks, but only within an Ethreum tx.
  3. A negative balance can exist across Ethereum tx.

Yeah you could see it like this. The flash loan is still needed to make the bridge with onchain liquidity, only having negative balances on the exchange does not allow that. Otherwise I think you only allow for arbitrage within the exchange itself. Which could be fine really depending on volume.

The real difference between #2 and #3 is that in #2, the operator has to find a way to liquidate the user's account to payback the flash loan for each Ethereum TX, because there is no collateral. This processing can be considered as a special case of #3 where the account is under-collateralized and need liquidation.

Depending on how this is setup, the market maker would probably also have some extra funds onchain to pay back potential losses. I think you could also see this as some under-collateralized system. With the very important difference that this is not needed for security, only for the level of quality of the exchange. Because of this, the collateral can be very small.

For 3rd party market makers method 3 is probably more flexible in some ways for doing things inside the exchange itself. But if the market maker provides a large amount of "collateral" with method 2, the operator can be sure whatever the market maker is doing will result in the loan being able to be paid back. If that's the case, method 2 is probably very similar to method 3, but without the security risk.

dong77 commented 4 years ago

Relying on flash loan really implies we have to use one single Ethereum TX, this will be a huge challenge for our relayer, especially when the throughput is high enough -- meaning the relayer cannot handle frequent reorganization of virtual blocks.

Brecht, I talked about my collateral-based layer2 lending with the team, it seems the idea is doable and will not impose complexity to the relayer. Tomorrow I'll discuss with Hoss and Steve further. I have more details to share with you later this week. I'll write it down here with those details.

Again, allowing MM to use funds more effeciently will be very useful. This is something even CEXs doesn't offer.

dong77 commented 4 years ago

An update: I talked to Hoss, he believes the complexity introduced by our ideas will cancel out the potential benefit. I would agree.

Brechtpd commented 4 years ago

Okay, that's understandable.

Again, allowing MM to use funds more efficiently will be very useful. This is something even CEXs doesn't offer.

Maybe for a good reason. :) Minting tokens out of nowhere kind of creates a whole lot of problems (you can mint DXD, but nobody says that those tokens are even available somewhere. So if those tokens are minted and sold to users, good luck selling the collateral to buy tokens that don't exist). With the flash loans at least you ensure everything that is being done actually possible, without any risk, but yeah, it does impose heavy limitations on the relayer. I still think it would be cool to use it to mirror onchain liquidity in the rollup (and this would be run by us to have full control), but perhaps only really usable when all requests can be handled in a single block so no reorganization of requests is necessary.

dong77 commented 4 years ago

I’d give up the collateral based solution as it will introduce risk that a simple DEX protocol will avoid. I prefer Loopring to be as simple as possible. Simplicity is a nice property for a protocol.

I’m still open to flash loan integration, but we need to make sure the relayer Implementation is as simple as possible to achieve 1000TPS.

在 2020年5月27日,22:00,Brecht Devos notifications@github.com 写道:

 Okay, that's understandable.

Again, allowing MM to use funds more efficiently will be very useful. This is something even CEXs doesn't offer.

Maybe for a good reason. :) Minting tokens out of nowhere kind of creates a whole lot of problems (you can mint DXD, but nobody says that those tokens are even available somewhere. So if those tokens are minted and sold to users, good luck selling the collateral to buy tokens that don't exist). With the flash loans at least you ensure everything that is being done actually possible, without any risk, but yeah, it does impose heavy limitations on the relayer. I still think it would be cool to use it to mirror onchain liquidity in the rollup (and this would be run by us to have full control), but perhaps only really usable when all requests can be handled in a single block so no reorganization of requests is necessary.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.