Adding New Markets to Compound

In this post, we’ll describe the steps we took to deploy cUNI, a cToken contract for the Uniswap UNI token. The steps should be similar for supporting new tokens, and we hope that this can become a resource for other developers when adding new markets to the protocol.

Core Code Changes

All code changes for the Compound Protocol should be created as a pull request on https://github.com/compound-finance/compound-protocol. If you’re proposing a change to the protocol that will not immediately go live (e.g. a feature improvement) then make your pull requests against the develop branch. If your pull request is intended to be merged only after the change is made (e.g. supporting a new token) then make your change against master. That is, master always refers to what is deployed to Ethereum, but develop may be advanced without a corresponding change to master.

For cUNI, we made the following changes:

First, we reverted changes to our delegator contract that had a minor bug affecting the output size of certain function calls. This change should have been merged into develop, but was not yet merged in. You can view the pull request here.

Next, we added a new CToken Delegate contract. Specifically, the cUNI contract is what we call a CTokenDelegator (it has upgradable functionality controlled by a CTokenDelegate contract). This is to mirror behavioral changes in UNI. A CCompLikeDelegate contract was added in this pull request, which allows the admin of the CToken to call a _delegateTo function, which will allow the cToken to vote its UNI tokens.

We added proper unit tests for this function. As the function does not significantly differ or introduce any likely regressions, we chose to peer-review and internally audit the change; for larger changes, you should consider contracting an auditor (that can be tipped, or reimbursed in the proposal, using protocol resources).

Once these pull requests were created and reviewed, we could move on to deploying the contracts to test-net and integrating them into the Compound Protocol on Ropsten.

Deploying to Testnet

There are multiple ways to deploy to test-net. You can follow the same procedure you would use for main-net (see below), but there is also Eureka, which is a tool similar to Terraform for Ethereum contracts. With a change in configuration to Eureka, you will be able to push your new token to all of our test-nets in a simple, declarative manner. You can read more about Eureka in its repository.

Deploying with Eureka

First clone compound-eureka and follow installation instructions.

Let’s start by adding new definitions. Definitions tell Eureka what types of contracts exist, what properties they have, and how to deploy them. For example, the Uni contracts new CompLike delegate as such:

define('Erc20', {
  match: {
    type: 'uni',
    has_properties: ['address']
  },
  contract: 'Uni',
  properties: {
    address: 'address',
    block: {
      type: 'number',
      setter: async ({trx}, contract, block) => {}
    },
    name: 'string',
    decimals: 'number',
    symbol: 'string'
  },
  build: async ({existing}, contract, { address }) => {
    return existing(contract, address);
  }
});

define('CCompLikeDelegate', {
  contract: 'CCompLikeDelegate',
  build: async ({deploy}, contract, props) => deploy(contract)
});

Here we add two new definitions, one for Uni which simply uses and existing Uni build and a CCompLikeDelegate with a deploy function that takes no args.

Next, we’ll add new declarations to our Eureka files. These declarations describe what contracts we expect to have available after we apply the Eureka changes. We will add UNI, CCompLikeDelegate and cUNI declarations, such as:

// Main-net https://etherscan.io/token/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984
Erc20#uni [uni] ! {
  type: "uni"
  address: '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984'
  name: "Uniswap"
  symbol: "UNI"
  decimals: 18
}

// CToken Delegate
CCompLikeDelegate#cCompLikeDelegate [429c9cd6] {}

CToken#cUNI [b73e68c] {
  type: "delegator"
  symbol: "cUNI"
  name: "Compound UNI"
  underlying: #uni
  initial_exchange_rate: 0.02e28
  comptroller: #comptroller
  delegate: #cCompLikeDelegate
  interest_rate_model: #Base200bps_Slope3000bps
  admin: $admin
}

Note: you may need to add oracle information (e.g. in oracle-simple.eureka).

Additionally, you could add another definition for Uni to deploy it from scratch (instead of using an existing version). Finally, you may want to saddle import the build for Uni, which will pull the source code, ABIs, etc from Etherscan and create a build file (like what would be produced by solc). You can run:

npx saddle import -n ropsten --outdir=.build --outname=uni.json 0x1f9840a85d5af5bf1d1762f925bdaddc4201f984

Next, you can apply the migration, which will deploy the contracts and create a Comp governance proposal on the given network to have your new cToken supported. If you have 100,000 test-net Comp (or you are developing locally any are not using the governance system), you can add the token to the supported tokens mapping to have a proposal or change created automatically:

Unitroller#comptroller [b73e68c] {
  // ...

  supported_markets: [
    //...
    #cUNI
  ]
}

Next, run the Eurkea migration, such as:

yarn eureka apply -n ropsten -b ./.build -c config/*.js -e eureka/{compound,testnet-gov,ropsten,4_ropsten,oracle-simple,admin-timelock}.eureka

This command will apply all of the declarations found in the given Eureka files. You can find common commands to run in README of the compound-eureka repository. If all goes well, you should have a new token, cToken, delegate and governance proposal (which you can vote for on https://app.compound.finance/vote if you switch to the correct network).

Deploying Manually

To deploy cUNI to mainnet, you can use saddle, a simple framework for building, testing and deploying Ethereum contracts. Saddle is already integrated into the compound-protocol repository. You may need to read the documents on adding keys (we suggest simply adding your private key in ~/.ethereum/<network> and using this key only as gas for deployment. Do not use this method for keys which have large sums of assets or special privileges.

Let’s first deploy our new CCompLikeDelegate contract:

npx saddle deploy -n mainnet CCompLikeDelegate

Next, we will deploy the CToken itself, referencing UNI and the CCompLikeDelegate. Note: many of these values can be found in the compound-config repo.

npx saddle deploy -n mainnet \
  CErc20Delegator \
  0x1f9840a85d5af5bf1d1762f925bdaddc4201f984 \
  0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B \
  0xfb564da37b41b2f6b6edcc3e56fbf523bd9f2012 \
  0.02e28 \
  "Compound Uniswap" \
  "cUNI" \
  8 \
  0x6d903f6003cca6255D85CcA4D3B5E5146dC33925 \
  0xbcfe70265699b7e9fda96a91dc9488b6ee6984a6 \
  ""

Just to explain, those variables are, in order:

  • CErc20Delegator: The name of the contract to deploy
  • 0x1f9840a85d5af5bf1d1762f925bdaddc4201f984: The underlying UNI contract
  • 0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B: The Compound Comptroller
  • `0xfb564da37b41b2f6b6edcc3e56fbf523bd9f2012: The Interest Rate Model
  • 0.02e28: The initial exchange rate (based on 18 decimals for the underlying, 8 decimals for the cToken and a target of 0.02 to 1 scaled by 1e18).
  • Compound Uniswap: The name of the token
  • cUNI: The symbol of the token
  • 8: The number of decimals for the cToken
  • 0x6d903f6003cca6255D85CcA4D3B5E5146dC33925: The Compound Timelock as the admin
  • 0xbcfe70265699b7e9fda96a91dc9488b6ee6984a6: The CCompLikeDelegate, deployed above.
  • "": No special upgrade data

A Note on the Price Feed

For an asset to be supported by the protocol, they require a valid price feed. The Open Price Feed utilizes view contracts, which define the namespace of the asset, and map them to a price contributed by a reporter. The Coinbase Oracle has already added support for Uniswap (UNI); a view contract was deployed mapping the cUNI contract (deployed above) with the price of UNI in the Open Price Feed. You can view currently supported Open Price Feed prices at https://prices.compound.finance, a price feed aggregator.

Creating Governance Proposal

Once you have a token deployed, you’ll need to create a Governance Proposal to integrate your token into the system (e.g. to support a new asset market). You will need either 100,000 COMP delegated to you, a sponsor with 100,000 COMP delegates, or to create an Autonomous Proposal.

Spread the Word

The final step in the governance process is to let people know about your change. The Forums are a great resource for discussion, but you could also discuss on Twitter and in Discord.

8 Likes

I’ve deployed a set of mainnet cUNI contracts using this notebook. I also simulated adding it to the protocol, minting cUNI, borrowing cUNI and being liquidated, and setting the UNI delegate. Everything checked out afaict, I encourage others to sanity check it.

The list of addresses (also in the notebook):

{
  UNI: "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984",
  cUniDelegate: "0x338f7e5d19d9953b76dd81446b142c2d9fe03482",
  cUNI: "0x35a18000230da775cac24873d00ff85bccded550",
  NewPriceFeed: "0x922018674c12a7f0d394ebeef9b58f186cde13c1"
}
1 Like

Can you explain how delegation supposed to work. For example person A have 100 UNI, he supplied it to Compound and got 4000 cUni. Person B borrowed 50 Uni from Compound. Both person A and person B plan to vote. Person B, obviously will have 50 votes, as he have Uni in his wallet. What about person A, who hold 4000 cUni, equivivalent to 100 Uni, where pool currently have 50 Uni? Lets take there are only 2 this persons and nobody else in pool.

@Sirokko, good question. First off, it is the unborrowed UNI tokens that are “locked” in the protocol that will be able to be voted (e.g. if User A supplies 100 UNI and User B borrows 50, then there are 50 UNI left in the cToken that can be voted. If User C borrows 50 more, then there would be 0 in the cToken for it to vote (but high interest rates would likely cause more UNI to re-enter the protocol)). As for voting, we hope / expect a community member to build a voting contract and delegate the UNI in this cToken to that new contract. That contract could be governed by Comp Governance, or any other system the community agrees on. I’m excited to see what people build.