RFP 16: Dynamic COMP reward distribution

Upon browsing Compound Grants I noticed a proposal request related to an idea I had earlier: Weighted Algorithmic COMP Distribution, so I’ve taken this request under my wing.

What

This proposal splits COMP rewards distributions between borrowers and suppliers. Upon passing, governance will be enabled to set reward rates specifically for borrowers vs. suppliers in any market.

Example: ZRX currently has a distribution rate of 0.0014625 per block - 0.0014625 for suppliers and 0.0014625 for borrowers. This can be changed to 0.002925 for suppliers and 0 for borrowers, the inverse, or anything in-between (or higher/lower).

Why

If governance is able to change the ratio, we can more effectively incentivize, develop, and maintain markets. For example, distributing all rewards for a market to its suppliers is a good way to incentivize deposits. Or we could distribute more to borrowers to incentivize borrowing.

How

Call Comptroller#_setCompSpeeds(cToken …, supplySpeed …, borrowSpeed …).

Status

  1. [x] Write core code
  2. [x] Ensure the update didn’t break any tests
  3. [x] Write new test cases
  4. [ ] Write simulation script for the proposal - after the changes are reviewed
  5. [ ] Create the proposal

Links

9 Likes

I love to see it! This is a much-needed improvement that I think governance would be happy to implement. Thank you for taking the lead on this.

2 Likes

Great work getting started here. A few notes though. Firstly, I actually started working on this recently as I started in the last community call. Please publicize what you are working so we don’t waste time having two projects working to solve the same issue. If you’d like, we can coordinate work and do this together.

Second, I do not think that this is the correct method for going about solving this issue. While the UX of it is that we would say 50/50 or 70/30, we definitely should not be storing ratios and then calculating the amount every time. This actually would make it significantly more confusing for governance (more parameters to set) and add on unnecessary gas overhead.

What should be done is just setting the borrowRate and supplyRate for each asset. I started with this, but then got sidetracked on the whitelisting account project. You can see what I did so far here.

6 Likes

Good points about simplifications for governance and also for gas costs.

I was so excited to start working on grants that I forgot to ask if anyone was already working on this one. :joy:

I think I’ll set up some project management system that we can all use to organize the community’s human resources.

But anyway, I’m happy to work on this with you. I’ll send you a message on Discord. :slight_smile:

I’ve updated this proposal after working with @arr00 on this. Please re-read the first post.

It’s ready for a review! :partying_face:

1 Like

Awesome work! Looking forward to seeing this get added.

1 Like

Testing of “split COMP rewards distribution” is live on the Ropsten testnet. The new rates are as follows.

Supply (:comp: per block):

  • ETH: 0.02 (from 0.01)
  • USDC: 0 (from 0.01)
  • USDT: 0 (from 0.01)
  • COMP: 0.02 (from 0.01)
  • DAI: 0 (from 0.01)
  • UNI: 0.0002 (from 0.0001)
  • ZRX: 0.0002 (from 0.0001)
  • BAT: 0.0002 (from 0.0001)

Borrow (:comp: per block):

  • ETH: 0 (from 0.01)
  • USDC: 0.02 (from 0.01)
  • USDT: 0.02 (from 0.01)
  • COMP: 0 (from 0.01)
  • DAI: 0.02 (from 0.01)
  • UNI: 0 (from 0.0001)
  • ZRX: 0 (from 0.0001)
  • BAT: 0 (from 0.0001)

:partying_face:

Edit: I wrote the wrong speeds. They are correct as per the rates in the contract now.

7 Likes

Community Member Testing

As I mentioned in the Compound Developer Community Call, I’d love to see community members help test this improvement. Here’s how you can help.

Testing distribution rates

Setup

  1. Switch over to the Ropsten testnet
    image
  2. Get some ETH from here or here
  3. Use Compound’s faucets to get all other tokens (other than COMP)… go to the app dashboard
  4. Select a token you want to get from the faucet, go to the withdraw tab, click on the “FAUCET” link, then send the transaction. Once confirmed, you’ll receive some tokens.
  5. (Optional) Swap ETH for other tokens using Uniswap or Sushiswap if you want more than what the Compound faucet provides.

Testing

Distribution rates

We want to ensure that COMP is distributed correctly as per the rates in the post above.

To do this, deposit and/or borrow some tokens. The amount of COMP you should receive is as follows.

accrued(COMP) = sum of: for each market, for both supply-side and borrow-side, delta blocks * user portion of market * rate for market

Where delta blocks is the change in block number from the time you borrowed/supplied to the time you withdrew/repayed (or present), and the rate for market as defined in the post above.

Example: If I supply 10% of the TVL for the BAT (supply-side) market for 100 blocks, I should receive 100 * 0.10 * supplyRate(UNI) = 100 * 0.10 * 0.0002 COMP = 0.002 COMP

You can get the block number for each transaction by viewing the transaction on Etherscan.

It’s simple to calculate the rates when you’re only active in one market, but it gets more complex with the more markets you’re active in. It’s important to test that the distribution rates are correct when active in multiple markets on both the supply-side and borrow-side.

Claiming

We want to ensure that users are able to successfully claim their accrued COMP. Please try claiming such and let us know if you’re unable to.

Using the protocol as you normally would

Try using the protocol as you normally would and ensure that everything works as expected.

Conclusion

This may be tricky for users not used to using testnets or debugging applications, so if you do wish to help us test this, just do what you can. Any help is appreciated!

4 Likes

Thanks for your work.

I’d like to check some points before sharing it in community testers.

  • What kinds of test results you need? Does it need to create some reporting format? (ex. block height and tx for each activies, comp rewards tx)
  • For self verification, I’m not sure how to get user portion of market factor.

Claiming COMP works fine for me on Ropsten.

The CompoundLens contract needs an update for the new supply and borrow comp speeds CompoundLens on Ropsten 0xc1a7ab77932cfd41c4164a253faf5a06afc3906e.

  • function cTokenMetadata(CToken cToken) public returns (CTokenMetadata memory)
  • function cTokenMetadataAll(CToken[] calldata cTokens) external returns (CTokenMetadata[] memory)

The struct returned only has the old compSpeed.

    struct CTokenMetadata {
        address cToken;
        uint exchangeRateCurrent;
        uint supplyRatePerBlock;
        uint borrowRatePerBlock;
        uint reserveFactorMantissa;
        uint totalBorrows;
        uint totalReserves;
        uint totalSupply;
        uint totalCash;
        bool isListed;
        uint collateralFactorMantissa;
        address underlyingAssetAddress;
        uint cTokenDecimals;
        uint underlyingDecimals;
        uint compSpeed;
        uint borrowCap;
    }

I think we might want to remove compSpeed and add the two new mappings?

  • mapping(address => uint) public compBorrowSpeeds;
  • mapping(address => uint) public compSupplySpeeds;
2 Likes

@dakeshi for each deposit, borrow, repay, withdraw, or COMP claim transaction, please report a link to the transaction on Etherscan. We can figure out all details from those.

To get the portion of the market,

  1. Go to the network config file
  2. Find the address of the cToken you’re interacting with (ex: cUSDC)
  3. Go to the Etherscan page for the contract (search by address), go to the “Contract” tab, then click on “Read Contract” (ex: https://ropsten.etherscan.io/address/0x2973e69b20563bcc66dC63Bde153072c33eF37fe#readContract)
  4. If borrowing in this cToken market, read totalBorrows. Otherwise, read totalSupply.
  5. Add the amount you are borrowing/supplying to totalBorrows/totalSupply (respectively)
  6. Divide the amount of the underlying you are borrowing/supplying (mantissa) by totalBorrows/totalSupply (respectively) to get the portion of the market.

Example for borrowing 1000 USDC:

  • USDC has 6 decimal places, so the mantissa USDC amount is 1000e6 = 1000000000
  • At this time, totalBorrows = 1115004256069
  • After my borrow transaction, totalBorrows = 1115004256069 + 1000000000 = 1116004256069
  • Portion of market = 1000000000/1116004256069 = 0.000896 = 0.0896%
1 Like

Congrats everyone, we made it!!! :partying_face:

Proposal 62

4 Likes

Gauntlet has reviewed the patch and will will be voting FOR. It is good to see additional scenario tests added on the supply/borrow for set/reset which can test permutations of borrow and supply.

The potential long-term drawback of this approach is if we did end up in a situation where COMP had a lot of markets and most of them don’t offer COMP rewards. That would be inefficient since functions like claimComp would always check each market for rewards. In any case, this is hard to avoid because some users have accrued rewards from before a market was unCOMPed. In the future, there could be a fastClaimComp function that slightly saves gas by only attempting to claim for markets with non-zero borrow/supply speeds. In that case, a user would call claimComp first to “clear” their record for any removed markets and then fastClaim from thereon.

5 Likes