MIP 3: Add a cache to reduce gas costs Source

AuthorAlex Scott
Discussions-Tohttps://forum.mstable.org/t/mip-3-add-cache-to-reduce-gas-costs/227
StatusImplemented
Created2020-10-27

Simple Summary

Depositing and withdrawing from lending markets (Compound, Aave) constitutes 60-90% of the gas cost of a given transaction. This MIP proposes to implement a cache to reduce frequency of interactions with the lending pools, thus reducing gas costs in MINT, SWAP and REDEEM by 60-90% for the majority of users.

Abstract

We propose upgrading the Masset.sol contract, allowing it to track and retain a percentage of each collateral in a “cache”. This cache will reside in the pre-existing PlatformIntegration for gas optimisation purposes. Only if necessary will the Masset then deposit or withdraw from the lending markets. When a deposit or withdrawal happens, the cache will reset to the ideal target weight, determined by a governance parameter. Subsequently, the BasketManager.sol will be upgraded to use the cache data during normal interest collection.

Motivation

Since mStable launched in early June, gas costs have increased drastically. This increase combined with interaction with lending markets Compound & Aave makes MINT, SWAP and REDEEM much more expensive than feasible. This massive gas cost incurred during lending market interaction is avoidable and not necessary to do for every transaction. An intuitive concern with caching could be that it produces less yield for SAVE, but in fact it will likely produce a higher yield due to having more liquid bAssets available to swap/redeem for cheap, thus increasing total volume in the system. This proposed cache is analogous to a computers memory cache - “an auxiliary memory from which high-speed retrieval is possible.”. In our case, memory = liquidity pool && high speed = low gas.

Specification

Overview

At the high level, there will now be a % of bAsset liquidity retained in the corresponding PlatformIntegration (a.k.a. “the Cache”). Deposits and withdrawals of a given bAsset will primarily interact with this Cache, unless a withdrawal will push the cache past 0 units, or a deposit will push the cache past X% of total mAsset supply. At this point, an amount will be deposited/withdrawn from the lending market to reset the cache to X/2% of total supply.

The solution will comprise of three parts:

1 - Upgrade to Masset.sol

Configurable property: Max Cache % (hereafter MAX)

A generic rule will be in place for deposits (MINT, SWAP in) and withdrawals (SWAP out, REDEEM).

The rule is as follows: if a deposit pushes a bAsset balance > MAX * totalSupply, then deposit bAsset into the lending market, in order to bring the bAsset balance back to MAX/2 * totalSupply.

The inverse is true for withdrawals: if there is sufficient liquidity to withdraw (goes beyond 0), then withdraw enough bAsset from the lending market to a) pay out to user and b) reset bAsset balance back to MAX/2 * totalSupply.

Pseudocode for a deposit here:


// Before
uint totalSupply = 100000000; // 100%
uint bAssetBalance = 600000; // 6%
var MAX = 0.07; // 7%

// Deposit
uint newDeposit = 500000; // 5%

uint newBalance = bAssetBalance + newDeposit; // 11%
if(newBalance > MAX*totalSupply) {
  uint overWeight = newBalance - (MAX*totalSupply/2);
  depositToLendingMarket(overWeight);
}

Pseudocode for a withdrawal here:


// Before
uint totalSupply = 100000000; // 100%
uint bAssetBalance = 200000; // 2%
var MAX = 0.07; // 7%

// Deposit
uint withdrawal = 500000; // 5%

if(withdrawal > bAssetBalance){
  uint ideal = (MAX*totalSupply/2);
  uint mustWithdraw = ideal + (withdrawal - bAssetBalance);
  withdrawFromLendingMarket(mustWithdraw);
}

2 - Upgrade to each PlatformIntegration

New function: withdrawRaw allows the Masset or BasketManager to withdraw bAssets directly from the contract.

New function: withdraw that accepts an amount and a totalAmount. The totalAmount is withdrawn from the lending protocol before sending the amount back to the recipient, keeping totalAmount-amount in the cache.

Move of responsibility for accounting during a given deposit to the Masset.sol. Previously when calling deposit, the call would fail if there was insufficient liquidity - now that there is a large amount of bAsset held in the cache, it’s unlikely to fail under the same circumstances (i.e. not having just received sufficient funds). The Masset.sol now has checks to ensure it is sending sufficient capital.

3 - Upgrade to BasketManager.sol

Currently the interest collection mechanism reads balances from Aave/Compound for each bAsset, before minting a corresponding amount of mUSD based on the accrued interest. The BasketManager must be upgraded now to read both bAsset.balanceOf(integration) as well as the balance from lending market. This is a trivial change.

Rationale

There were a few technical considerations taken into account before coming up with the final design.

Cache size: total supply % vs per bAsset %

When choosing what to base the cache size on, there were two clear options: a % of total supply of mAsset, or a % of each bAsset. The % of each bAsset posed some problems: consider that a bAsset has only a few thousand units of liquidity - there is no point in depositing this to a lending pool. Additionally, if a bAsset has 50m in liquidity, it’s unlikely that a high % of this should be in the cache, as it does not support the thesis of reducing gas costs for small orders.

If the path of a per bAsset % is one to go down, there would also need to be a minimum cache size in terms of units, where max cache = min(X% of bAsset, Yk units).

Rebalancing: automated vs manual

It’s possible to completely remove the lending market interactions from the forging process, and transfer the responsibility over to a publicly callable, external function. This function could be called regularly to rebalance the pools to their optimal cache size. With that in mind, and to reduce the manual overhead of the system, we opted to keeping as is.

Lending market vs masset upgrade

There were two places to technically implement the cache - either in Masset.sol or in the platform integration contracts. I believe doing so here removes both technical overhead from development and reduces gas costs for the majority of calls.

Technical Specification

N/A

Configurable Values (Via MCCP)

Configurable value: Max Cache %

This value will determine the maximum amount of each bAsset that should be in the cache, and is a percentage of total supply of the mAsset. This can be configured by governance.

Copyright and related rights waived via CC0.