MIP 2: Add a liquidator to sell reward tokens Source

AuthorAlex Scott, George Ornbo

Simple Summary

This MIP proposes a liquidation mechanism for reward tokens ($COMP, $LEND) accrued in the protocol. Through the liquidator mechanism tokens may be liquidated and the value of these tokens realised by SAVE.


Create a Liquidator contract that sells the reward tokens approved in both Compound and Aave integrations. This will allow accrued rewards tokens to be sold on DEXs and the value returned to SAVE. This will bolster the SAVE APY and realised the value of accrued rewards.

The protocol earns rewards from different platforms for lending stablecoins. To dat, the protocol has earned ~470 $COMP tokens for lending DAI and USDC and it is expected that the protocol will earn tokens from other platforms. This MIP offers an automated generic way to liquidate accrued tokens and to return this value to the protocol. This will bolster the SAVE APY.


The mStable protocol lends stablecoins to lending platforms and derives a return for depositors based on the Supply APY of the Platform. In addition to Supply APY rates many lending platforms offer liquidity providers rewards in the form of tokens. These tokens are tradable on DEXs and have value and provide an additional yield for liquidity provision.

The mStable protocol deposits DAI and USDC to Compound and has been accruing $COMP tokens as a result. These tokens are valuable but the current protocol has no way to realise the value from accrued rewards. As such these tokens remain on a contract and the value cannot be liquidated.

For lending DAI and USDC to Compound the protocol has accrued $COMP tokens as rewards. The $COMP that has been accrued on the CompoundIntegration with a balance of ~470 $COMP equivalent to $72k at the time of writing. This value should be liquidated and distrubted to SAVE. The protocol should automate this selling to support the push towards decentralisation.



This MIP creates a generic liquidiator that allows accrued rewards tokens to be retrieved from a given integration (e.g. CompoundIntegration), and sold for mUSD which is then sent to the SavingsManager.

Liquidations are triggered once by week by any community member through an external function.


  1. Liquidator sells $COMP for USDC (or other) on Uniswap once per week (up to trancheAmount).
  2. Sell USDC for mUSD on Curve and send to SavingsManager
  3. SavingsManager streams mUSD to SAVE, second by second over the course of a week


There final architecture choice increases very little gas in the system - only causing 1600 gas (2x SLOAD) to a SAVE deposit. No gas costs are seen for general mint/swap/redemption. Any gas cost is incurred once a week when the liquidation is triggered.

The streaming of the mUSD to SAVE also achieves a gradual release of the realised mUSD over the course of a full week to SAVE, avoiding the scenario in which malicious actors could trigger the liquidation and jump into the SAVE contract to immediately benefit.

Capitalising on the new Curve 3Pool, it is now optimal to sell COMP for a given bAsset on Uniswap (e.g. DAI or USDC), then immediately sell that bAsset on Curve for mUSD.

There were a number of previous architecture ideas that turned out to be sub optimal, including intermittent collection from the Liquidator, and intermittent airdropping.

Technical Specification

A Liquidator.sol contract will be created that will sell Token A for Token B on a DEX. Initially Uniswap and Curve will be supported but Balancer, 1inch, UniSwap and other integrations may be added. In the short term the contract will be upgradable with a view to it becoming immutable.

Protocol contracts that hold reward tokens will give the Liquidator infinite approval to spend the rewards tokens. The Liquidator will then begin selling Token A once per week, for Token B, and then turning this Token B into mUSD.

Contract changes

Liquidator.sol (NEW)

Key functions:

    * @dev Create a liquidation
    * @param _integration The integration contract address from which to receive sellToken
    * @param _sellToken Token harvested from the integration contract
    * @param _bAsset The asset to buy on Uniswap
    * @param _curvePosition Position of the bAsset in Curves MetaPool
    * @param _uniswapPath The Uniswap path as an array of addresses e.g. [COMP, WETH, DAI]
    * @param _trancheAmount The amount of bAsset units to buy in each weekly tranche
    function createLiquidation(
        address _integration,
        address _sellToken,
        address _bAsset,
        int128 _curvePosition,
        address[] calldata _uniswapPath,
        uint256 _trancheAmount

    * @dev Triggers a liquidation, flow (once per week):
    *    - Sells $COMP for $USDC (or other) on Uniswap (up to trancheAmount)
    *    - Sell USDC for mUSD on Curve
    *    - Send to SavingsManager
    * @param _integration Integration for which to trigger liquidation
    function triggerLiquidation(address _integration)

CompoundIntegration (PROXY UPGRADE)

     * @dev Approves Liquidator to spend COMP (0xc00e94Cb662C3520282E6f5717214004A7f26888)
    function approveRewardToken()

SavingsManager (MODULE UPGRADE)

Deposited mUSD from the Liquidator is streamed to the SAVE contract over the course of a full week. This means that as deposits to SAVE happen, they realise some of the value.

     * @dev Allows the liquidator to deposit proceeds from iquidated gov tokens.
     * Transfers proceeds on a second by second basis to the Savings Contract over 1 week.
     * @param _mAsset The mAsset to transfer and distribute
     * @param _liquidated Units of mAsset to distribute
    function depositLiquidation(address _mAsset, uint256 _liquidated)

Configurable Values (Via MCCP)

  • TrancheAmount (max units of mUSD to purchase per week - useful for starting period)
  • BuyAsset (must be some bAsset of USDC, USDT or DAI since that is the Curve 3 pool)

Copyright and related rights waived via CC0.