TrueFi-Protocol / truefi-spec

Under-collateralized lending and defi smart contracts
41 stars 17 forks source link

Optimize TrueFi pool join and exit #28

Open dmaretskyi opened 3 years ago

dmaretskyi commented 3 years ago

Today joining or exiting a pool spends around 700-800 thousand gas which cost $100-$200 in fees. This makes it unfeasible to use the pool with smaller sums.

Both join and exit transactions spend almost 90% gas calculating the pool value. Optimisation ideas are centered around reusing the same calculation for multiple transactions.

Gas profiling of the current smart-contracts

liquidExit

https://dashboard.tenderly.co/tx/mainnet/0xc448a21d588c350fccfb09ed599c6d36850503ea85ac51c280f01e4555b9b760/gas-usage

getPoolValue = 615274 gas (90%)
total = 682976 gas

join

https://dashboard.tenderly.co/tx/mainnet/0x689a04afc74d6a8a703b0864a108ccc5c1e9221f2aa98314039ce28a99744786/gas-usage

getPoolValue = 675414 gas (88%)
total = 764492 gas

Option 1: Users queue their deposits and bot processes them in-batch

In this proposal users would call a contract method that would lock their tokens but would not mint any LP tokens right away. Their deposit would be enqueued along with others.

After enough users have enqueued their tokens a bot (probably owned by TrustToken) would call the pool smart-contract to process a batch of enqueued deposits. In this case the pool value would be calculated once and then used to mint LP tokens for each user in the batch.

In this case the most gas would be spent by the bot. The total gas usage would be reduced, but the protocol fees would need to be adjusted accordingly.

Advantages

Disadvantages

TODO

Option 2: Lock the pool value for deposits and withdrawals for a time period

In this case a bot would periodically calculate the pool value and lock the LP token price for a certain period. In that period all of the deposits and withdrawals would be calculated using the locked price.

Analogous to option 1, the bot would be spending the majority of gas.

Advantages

Disadvantages

vanruch commented 3 years ago

Hi @Marik-D, thanks for the suggestions.

I don't think #1 is suitable for us because there's not enough traction to batch transactions. Last USDC pool join was performed 4 days ago for example. This would really mean that we will have a bot which will be executing metatransactions (which might be not that bad of idea)

I like #2 though, after second thought it looks like it's not that important for pool to have latest pool value. This might cause some problems when pool is almost empty but this can probably be neglected. But we still need to carefully check if this will not provide any threads other than arbitrage opportunity (we can introduce a cooldown to prevent this).

yuchenlintt commented 3 years ago

Thanks @Marik-D for these proposals! I think these are both feasible technical optimizations, and I also have a preference for #2.

However, before we add epicycles on epicycles, I'd first like to investigate why calculating pool value costs 700-800k gas, and determine whether it really needs to spend that much. Where is our critical loop?

  1. calculating the Curve strategy value is expensive
  2. (FTL) iteration through all loans, where every loan accrues value linearly per-block
  3. (LoC) ditto with FTL but for buckets
  4. we're calling several outside contracts (Curve, TrueLender2 FTLs, FTLA FTLs, LoCs, SAFU deficits), which would inherently be expensive even if each call were a simple lookup

If we can find a solution that cuts off non-essential code, I would prefer that to building more on top of what we have. For instance for LoCs, perhaps we can accept that the LoC value doesn't accrue per-block. It would only increase when someone manually calls poke().

yuchenlintt commented 3 years ago

Another perspective on the pool value update bot in option 2: sandwich profits could be our incentive for lenders to call the public update function. That is, sandwich lenders are welcome to join + update + liquidExit and collect a proportional share of instant profits. As long as liquidExit has a high enough utilization-dependent penalty, a sandwich lender can't drain too much profit from the pool.

We could adjust the penalty calculation to vary the optimal frequency for a sandwich lender to call update:

Our own bot would be funded from sandwich lending to the protocol when it's gas-profitable.