Supplying Assets to the Compound Protocol

Quick Start Guide

Adam Bavosa
Feb 12 · 19 min read

The Compound Protocol is a series of interest rate markets running on the Ethereum blockchain. When users & applications supply an asset to the Compound protocol, they receive a cToken, which is an ERC-20 token that can be exchanged for its underlying asset at any time.

As interest accrues to the suppliers of an asset, the cToken exchange rate (relative to the underlying asset) increases over time.

Non-technical users can interact with the Compound protocol using an interface like Dharma or app.compound.finance; developers can create their own applications that interact with Compound’s smart contracts.

In this guide, we’re going to walk through supplying assets via JSON RPC and via proxy smart contracts that live on the blockchain. These are the 2 ways in which developers can write software to utilize the Compound protocol.

If you are new to Ethereum, we suggest that you start by Setting up your Development Environment for Ethereum.

All of the code referenced in this guide can be found in this GitHub Repository: Quick Start: Supplying Assets to the Compound Protocol.

Compound Markets

The smart contracts that power Compound are deployed to the Ethereum blockchain. This means that at the time of this guide’s writing, the only types of assets that Compound can support are Ethereum and Ethereum tokens.

The supported assets are listed at https://compound.finance/markets. Based on the implementation of Ethereum, we have to utilize 2 similar processes:

  • The ETH supply method
  • The ERC20 token supply method

Like mentioned earlier, when someone supplies an asset to the protocol, they are given cTokens in exchange. The method for getting cETH is different from the method for getting cDAI or cREP. We’ll run through code examples and explanations for the two different asset supply methods.

Connecting to the Ethereum Network

Each of the code examples in this guide enable you to supply your ETH or ERC20 token to Compound on the Ethereum main net, any of the public test nets, or your own local machine’s test net.

You will need to use the contract address and HTTP provider URL for the particular network that you want to use with Compound. The contract addresses for their corresponding networks are listed at https://compound.finance/developers#networks.

If you are trying to test with your local test net, use the Main net contract addresses. We will make a fork of the main net which will run on our localhost.

If you want to use a public test net (like Ropsten, Göerli, Kovan, or Rinkeby), make an Infura account at https://infura.io/ to get your API key. If you are using your own localhost test net, or the production main net, we will use the Cloudflare entrypoint for the main net (https://cloudflare-eth.com).

For each of the Web3 object initializations in the scripts, change the HTTP provider for the network you wish to interact with.

const web3 = new Web3('http://127.0.0.1:8545');

(Ropsten, Göerli, Kovan, or Rinkeby)

const web3 = new Web3('https://ropsten.infura.io/v3/<_YOUR_INFURA_API_KEY_HERE_>');
const web3 = new Web3('https://mainnet.infura.io/v3/<_YOUR_INFURA_API_KEY_HERE_>');// Alternatively, if you don't have an Infura account:
const web3 = new Web3('https://cloudflare-eth.com/');

Supplying to Compound on a Localhost Network

To run an Ethereum local test net on your machine, we will fork the Main net. This means that you can interact with the production smart contracts in a test environment. No real ETH will be used and no modifications to the production blockchain will occur. If you haven’t already, install Node.js. Click here to install the LTS of Node.js and NPM.

Let’s install and initialize Ganache CLI.

npm i -g ganache-cli## or for yarn fans: yarn global add ganache-cliganache-cli \
-f https://cloudflare-eth.com/ \
-m "clutch captain shoe salt awake harvest setup primary inmate ugly among become" \
-i 999 \
-u 0x9759A6Ac90977b93B58547b4A71c78317f391A28

Run this command in a second command line window before you start running the code referenced later in this guide. The command spins up a test Ethereum blockchain on your localhost.

A quick explanation of each of the command line flags:

  • -f Forks the Main Ethereum network to your local machine for development and testing.
  • -m Runs Ganache with an Ethereum key set based on the mnemonic passed. The first 10 addresses have 100 test ETH in their balance on the local test net every time you boot Ganache. Do not use this mnemonic anywhere other than your localhost test net.
  • -i Sets an explicit network ID to avoid confusion and errors.
  • -u Unlocks an address so you can write to your localhost test blockchain without knowing that address’s private key. We are unlocking the above address so we can mint our own test DAI on our localhost test net (more on this later).

Once you have run Ganache CLI on your command line, it will log 10 wallet addresses, and 10 private keys. Each of the wallets will have 100 test ETH in them which can be used for executing smart contracts locally. Copy and save the first private key.

Supplying to Compound on a Public Network

If you are supplying to Compound on the Main net, Ropsten, Göerli, Kovan, or Rinkeby, you should have already found and copied the Compound contract address for that network (see how above).

You also should have collected some ETH for that network by purchasing/mining (Main), or a test net’s faucet (all the others).

Next, copy and safely store your wallet’s private key. The private key is used to sign transactions that are sent on the Ethereum network. The purpose of this is to certify that the transaction was created and submitted by a unique wallet.

If you are using MetaMask for your Ethereum wallet, open the menu, click the 3 dots on the right, Account Details, Export Private Key, and input your MetaMask password. This will reveal your private key. Keep it safe! Copy this value and save it for later.

It is a best practice to store a key like this as an environment variable on your local machine. When a key is stored as an environment variable, it can be referenced in code files by a variable name, instead of explicitly with a string. This promotes code cleanliness, and reduces the risk of exposing your secret.

Again, if you are only testing smart contracts on your localhost Ganache today, don’t get your MetaMask private key. We’ll rely on the private key from the Ganache command line log.

How to Supply ETH to Compound via JSON RPC

Supplying Ether (ETH) to the Compound protocol is as easy as calling the “mint” function in the Compound cEther smart contract. The “mint” function transfers ETH to the Compound contact address, and mints cETH tokens. The cETH tokes are transferred to the wallet of the supplier.

Remember that the amount of ETH that can be exchanged for cETH increases every Ethereum block, which is about every 15 seconds. There is no minimum or maximum amount of time that suppliers need to keep their asset in the protocol. See the varying exchange rate (Supply APY) for each cToken at https://compound.finance/markets.

In order to call the mint function, you need to first:

  • Have ETH in your Ethereum wallet.
  • Find your Ethereum wallet’s private key.
  • Connect to the network via Infura API key or Cloudflare (see above section Connecting to the Ethereum Network)

There are several programming languages that have Ethereum Web3 libraries, but the most popular at the time of this guide’s writing is JavaScript.

We’ll be using Node.js JavaScript to call the mint function. The following code snippets are from this Node.js file in the supplying assets guide GitHub Repository. Web browser JavaScript is nearly identical to these code examples.

Let’s import Web3.js, and initialize the Web3 object. It’s pointing to our localhost’s Ganache, which has 100 test ETH in each of the test wallets. We get the same 10 test wallet addresses every time we run Ganache CLI with the mnemonic in the above command (from the Connecting to the Ethereum Network section).

const Web3 = require('web3');
const web3 = new Web3('http://127.0.0.1:8545');

If you are using a public network (Ropsten, Kovan, etc.), make sure your wallet has ETH, and that you have your wallet private key stored as an environment variable. Also, have your Infura API key ready if you are deploying to a public test net.

Replace the HTTP provider URL in the Web3 declaration line with the appropriate network’s provider if you are not using the Ganache test environment.

Next, we’ll add our wallet’s private key as a variable. It’s a best practice to access this as an environment variable.

// Your Ethereum wallet private key
const privateKey = process.env.myWalletPrivateKey;
// Add your Ethereum wallet to the Web3 object
web3.eth.accounts.wallet.add('0x' + privateKey);
const myWalletAddress = web3.eth.accounts.wallet[0].address;

If you are writing web browser JavaScript instead of Node.js, you can add the user’s private key to the Web3 object by using the ethereum.enable() command. Here is the alternative code snippet.

// Add your Ethereum wallet to the Web3 object
ethereum.enable();
const myWalletAddress = web3.eth.accounts.wallet[0].address;

Next we’ll make some variables for the contract address and the contract ABI. The contract addresses are posted on this page: https://compound.finance/developers#networks. Remember to use the main net address if you are testing with Ganache CLI. The ABI is the same regardless of the Ethereum network that we are using.

// Main Net Contract for cETH (the supply process is different for cERC20 tokens)
const contractAddress = '0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5'; // main net
const abiJson = [......
const compoundCEthContract = new web3.eth.Contract(abiJson, contractAddress);

The final snippet of code is where the magic happens. The first function call sends ETH to the mint function, which mints cETH, and sends it to our wallet address.

The 2 subsequent function calls are not necessary, but they are there for illustration. The first method calls a getter function on the Compound contract that shows how much underlying ETH our cToken balance entitles us to. The second function shows our wallet’s cToken balance.

Our code sends 1 ETH to the contract, and gives our wallet cETH. The ratio of cETH to ETH should be in the ballpark of 50 to 1. Remember that the exchange rate of underlying to cToken increases over time.

Here is the command for running the script from the root directory of the project:

node json-rpc-examples/supply-eth-via-json-rpc.js

Script example output:

Sending ETH to the Compound Protocol...
cETH "Mint" operation successful.
ETH supplied to the Compound Protocol: 0.999999999884479961
My wallet's cETH Token Balance: 49.97183529

How to Supply a Supported ERC20 Token to Compound via JSON RPC

Next we’ll walk through supplying an ERC20 Ethereum token to the Compound protocol via JSON RPC. The script is very similar to the previous section’s script. Our example token will show how to do this with DAI.

You can supply any of the supported ERC20 tokens (listed at https://compound.finance/markets) using the same method implemented in the code below.

This time we’ll need:

  • A small amount of ETH in our wallet (for gas costs).
  • The supported token’s Compound contract address.
  • Your Ethereum wallet’s private key.
  • A balance of an ERC20 Token (for example, DAI) in our wallet.

Let’s first mint some test DAI for our localhost Ganache wallet. If you are using the main net or a public test net, you will have to find another way to acquire some DAI for your Ethereum wallet.

Minting Test Net DAI

The following DAI mint only works for localhost testing. Execute the mint-testnet-dai.js script form the root directory of the project GitHub repository. Before you execute it, make sure you have the latest of the following:

  • DAI Main net contract address (MCD_DAI)
  • DAI Main net Join DAI address (MCD_JOIN_DAI).
  • DAI Main net ABI

The contract address can be found at https://changelog.makerdao.com/.

  • Click the latest production release.
  • Click contract addresses.
  • The main net DAI contract address is in the JSON value of the MCD_DAI key.
  • The Join DAI address is in the MCD_JOIN_DAI key.

Once you have the MCD_DAI value, you can get the contract’s ABI. Go to https://etherscan.io/address/ in a web browser and add the DAI contract address’s hex value at the end of that URL before you request the web page. This will take you to the Etherscan page.

  • Click the Contract tab
  • Find the Contract ABI text box
  • Copy the ABI using the copy button on the right, and save this for later.

Make sure you have Ganache CLI running using the command from earlier, with the latest MCD_JOIN_DAI address unlocked (using the -u flag).

Here’s the scripts code:

// First run Ganache locally with `daiMcdJoin` address unlocked
const Web3 = require('web3');
const web3 = new Web3('http://127.0.0.1:8545');
const daiAbi = [....// Address of DAI contract (latest at https://changelog.makerdao.com/)
const daiMainNetAddress = "...";
// Address of Join (has auth) https://changelog.makerdao.com/ -> releases -> contract addresses -> MCD_JOIN_DAI
const daiMcdJoin = '...';

Copy and paste the daiMainNetAddress, daiMcdJoin, and daiAbi variables we found into the code. The last snippet of code will mint 500 DAI for our test wallet.

Be sure to run the mint-testnet-dai.js script after you’ve initialized Ganache CLI, and before you attempt to supply DAI to Compound.

node mint-testnet-dai.js

Now that we have some DAI, let’s supply it to Compound. The following code is from the supply-erc20-via-json-rpc.js script in the project GitHub repository.

Let’s create a Web3 object and point it to the appropriate Ethereum instance (just like we did in the How to Supply ETH to Compound via JSON RPC section).

const Web3 = require('web3');
const web3 = new Web3('http://127.0.0.1:8545');

Paste in your wallet private key reference.

// Your Ethereum wallet private key
const privateKey = process.env.myWalletPrivateKey;
// Add your Ethereum wallet to the Web3 object
web3.eth.accounts.wallet.add('0x' + privateKey);
const myWalletAddress = web3.eth.accounts.wallet[0].address;

Remember there is an alternative to this if you are writing JavaScript for a web page, ethereum.enable()(described earlier).

Next, we’ll create references to our production contracts using their contract addresses and their ABIs.

// Main Net Contract for cDAI (https://compound.finance/developers#networks)
const compoundCDaiContractAddress = '...';
const compoundCDaiAbiJson = [...];
const compoundCDaiContract = new web3.eth.Contract(compoundCDaiAbiJson, compoundCDaiContractAddress);
// Main Net Contract for DAI token
// Make sure you give your local test net wallet some DAI using mint-testnet-dai.js
const daiContractAddress = '...';
const daiAbiJson = [...];
const daiContract = new web3.eth.Contract(daiAbiJson, daiContractAddress);

See the instructions in the Minting Test Net DAI sections for finding DAI’s ABI and contract address. Paste them into the above code. Depending on the ERC20 token you are attempting to supply, you will need to do some searching online for this information.

The next code snippet has the fun part. The operations must be done in this order:

  • Allow Compound’s contract to transfer the ERC20 token from your wallet by calling the approve function from the original ERC20 token contract.
  • Call the mint function in the Compound cToken contract.

Of course the subsequent calls to balanceOfUnderlying and balanceOf are not necessary, but are present for illustration.

Execute the script from the project root directory using this command line command.

node json-rpc-examples/supply-erc20-via-json-rpc.js

If successful, the output will look something like this.

DAI contract "Approve" operation successful.
Sending DAI to the Compound Protocol...
cDAI "Mint" operation successful.
DAI supplied to the Compound Protocol: 9.999999999992957452
My wallet’s cDAI Token Balance: 495.96058738

How to Supply ETH to Compound via Solidity

Developers can supply Ethereum assets to the Compound protocol using other smart contracts. The most popular programming language for Ethereum development is Solidity. I will run through some code examples for building your own smart contracts that interface with Compound. Let’s get started.

We’ll begin with an ETH supply example. The prerequisites and order of operations for supplying ETH to Compound via Solidity are as follows.

  • Get some ETH into your own Ethereum wallet by purchasing/mining (or faucets on test nets).
  • Deploy your own custom contract to the blockchain. Your contract must have a function that accepts ETH payments. That function must call the mint function in the Compound contract.
  • Send an ETH payment from your wallet to your custom contract.
  • Your custom contract’s function calls the mint function in the Compound contract.
  • Compound accepts the ETH from your contract.
  • Compound sends cETH tokens to your contract’s address (not the invoking wallet).

Let’s run through the example code in the GitHub Repository: Supplying Assets to the Compound Protocol.

First, run Ganache CLI using the command from the Supplying to Compound on a Localhost Network section. No need to do this if you’re deploying to a public test or the production Ethereum network.

Writing a Smart Contract in Solidity

Next we’ll write our Solidity smart contract file.

The first contract is an abstract contract. The purpose of this is to cast an address as the production Compound cETH token contract. This is the elegant alternative to copying, pasting, and co-deploying the full Compound smart contract files, which can be found on the Compound GitHub. Avoid doing that.

Next is MyContract. This is meant to represent your own smart contract. We make a function called supplyEthToCompound which is marked with the payable modifier. This means that the function can accept ETH that is sent to it.

In addition to sending ETH to this function, we need to pass the Compound cToken contract address as a function parameter. The reason we need to pass this is so our function knows where to direct its call to the Compound mint function.

Our function calls the cToken mint function, and forwards all of the ETH to that call (msg.value). Lastly, the function returns true.

Compiling our Solidity Smart Contracts

Before we deploy our function to the blockchain, we’ll need to compile it. In order to compile with a Node.js script we’ll need to install some dependencies. If you cloned the project’s GitHub repository, navigate to the project root directory on the command line and run:

npm install
## alternatively for yarn fans: yarn install

If you did not clone the repository, install each of the dependencies from the project’s package.json file.

Here is our Node.js code for compiling smart contracts.

The script imports the Node.js Solidity compiler dependency and creates a reference to our Solidity file in the solidity-examples/ folder. Next, it reads the code file and defines an input for the compiler package.

The script compiles the Solidity code and writes 2 files to a .build/ folder. The files contain the Ethereum bytecode and also the contract’s ABI. These will be referenced in our deployment script.

From the root directory of the project, run the compile script using the following command:

node compile-smart-contracts.js

Deploying our Solidity Smart Contracts

Let’s deploy our smart contracts to the Ethereum blockchain. We’ll be going over the Node.js deploy script in the project GitHub repository.

The first step is initializing the Web3 object. Be sure to point it to the proper HTTP provider depending on the blockchain you wish to deploy your contract. See the above section Connecting to the Ethereum Network to learn how to point your Web3 instance to different public Ethereum instances.

Stick with the following code if you intend to supply assets to your localhost test environment.

const Web3 = require('web3');
const web3 = new Web3('http://127.0.0.1:8545');

Next, we’ll import our previously compiled contract bytecode and ABI.

const { bytecode } = require('./.build/bytecode.json');
const abi = require('./.build/abi.json');

If you’re deploying to a public network, uncomment lines 18 to 21 in the deploy script, and delete lines 22 to 25. Be sure to add your wallet private key reference on line 19, preferably with an environment variable.

Next, we deploy the contract from our wallet.

To run the deploy script, navigate to the project root directory on the command line and run:

node deploy-smart-contracts.js

If successful, the deploy script will log the contract’s address on the blockchain. Copy the contract’s new address and save it for later.

Calling Our Custom Made Contract with JSON RPC

We now have a custom contract ready to supply assets to Compound. In order to invoke our contract, we must utilize JSON RPC. This can be done using Node.js or web browser JavaScript. In this example, we’ll use a Node.js script in the solidity-examples folder.

The following code snippets are from the supply-eth-via-solidity.js script.

First, we initialize a Web3 object and point it to the Ethereum network of our choosing. Next, we create a reference to our deployed smart contract by its address.

// `myContractAddress` is logged when running the deploy script in the root
// directory of the project. Run the deploy script prior to running this one.
const myContractAddress = '0x9C5Dd70D98e9B321217e8232235e25E64E78C595';
const myAbi = require('../.build/abi.json');
const myContract = new web3.eth.Contract(myAbi, myContractAddress);

Next we create a reference to the cToken contract. Paste in the address and ABI which are posted at: https://compound.finance/developers#networks

// Main net contract address and ABI for cETH, which can be found in the mainnet
// tab on this page: https://compound.finance/developers
const compoundCEthContractAddress = '0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5';
const compoundCEthContractAbi = [...];
const compoundCEthContract = new web3.eth.Contract(compoundCEthContractAbi, compoundCEthContractAddress);

Next we import our wallet’s private key. Like mentioned earlier, use`ethereum.enable()` if you are using web browser JavaScript instead of Node.js.

// Set up a wallet using one of Ganache's key pairs.
// Don't use this key outside of your local test environment.
const privateKey = process.env.myWalletPrivateKey;
// Add your Ethereum wallet to the Web3 object
web3.eth.accounts.wallet.add('0x' + privateKey);

Finally, we supply ETH to Compound via our proxy contract. After that completes, we log our contract’s cToken and underlying ETH balance.

Run this script from the project root directory using the following command:

node solidity-examples/supply-eth-via-solidity.js

If successful, the output for running this script will be something like the following:

Supplied ETH to Compound via MyContract
ETH supplied to the Compound Protocol: 0.999999999998500226
MyContract’s cETH Token Balance: 49.97183528

Our contract address will now have a cETH token balance!

How to Supply a Supported ERC20 Token to Compound via Solidity

The following will run through an example of adding an ERC20 token to the Compound protocol using Solidity smart contracts.

To avoid repetition, we’ll add new code to the Solidity file created in the previous section, How to Supply ETH to Compound via Solidity. We’ll add more abstract smart contracts, and 1 function to our custom contract. The full Solidity file can be found in the project repository.

Here’s an overview of supplying a token to Compound with Solidity:

  • Get some ETH into your own Ethereum wallet by purchasing/mining (or faucets on test nets). This will be used for gas costs.
  • Get some ERC20 token, in this case DAI. If you are working in the production environment, purchase some DAI for your Ethereum wallet. If you are working with a Ganache CLI test blockchain, follow the instructions in the section Minting Test Net DAI to get your test wallet some DAI.
  • Get the address of the ERC20 contract. For DAI, instructions are in the Minting Test Net DAI section.
  • Get the address of the Compound cToken contract. See DAI on this page: https://compound.finance/developers#networks.
  • You transfer DAI from your wallet to your custom contract. This is not done in Solidity, but instead with JSON RPC.
  • You call your custom contract’s function.
  • Your custom contract’s function calls the approve function from the original ERC20 token contract. This allows an amount of the token to be withdrawn by Compound from your custom contract’s token balance.
  • Your custom contract’s function calls the mint function in the Compound cToken contract.

Let’s get started. First we’ll walk through the new code in our Solidity file.

We added 2 new abstract contracts. The first is for our ERC20 token contract, and the second is for Compound’s corresponding cToken contract.

We’ll be able to call the production versions of those contracts using these definitions. We need to initialize them with the production address of the deployed contracts, which we pass to the constructor.

Next, we added a new function to MyContract called supplyErc20ToCompound.

Notice that unlike our first function, this function is not payable. This means that it cannot accept ETH.

The function first creates references to the production instances of DAI and cDAI contracts using our abstract contract definitions.

Next, our function approves the transfer of ERC20 token from our contract’s address to Compound’s cToken contract using the approve method. Finally, our contract calls the cToken contract mint function. This sends some DAI to Compound, and gives our custom contract a balance of cDAI.

Compiling

To avoid repetition, follow the section Compiling our Solidity Smart Contracts above. Once you have re-compiled your contract, the new bytecode and ABI will be waiting in the .build/ folder.

node compile-smart-contracts.js

Deploying

To avoid repetition, follow the section Deploying our Solidity Smart Contracts above. Once you have re-deployed your contract, the script will log the new MyContract address. Copy this and save it for later. We’ll need it to call the smart contract function and supply DAI to Compound.

node deploy-smart-contracts.js

The Web3.js code that will invoke our custom smart contract can be found in the solidity-examples/ folder. Let’s run through the supply-erc20-via-solidity.js script.

First, the script makes a Web3 object and points it to the blockchain network that we want to use to supply to Compound. Next, we make some references to MyContract, the DAI contract, and also the Compound cDAI contract.

Remember, the cToken contract addresses and ABIs can be found at https://compound.finance/developers#networks, and MyContract’s address was logged when we deployed the contract. See the Minting Test Net DAI section to find the newest DAI contract address.

Next, we make a reference to our Ethereum wallet private key. This should be a wallet that has some ETH (for gas) and also DAI (to supply to Compound). Our script’s main function first transfers DAI from our wallet to MyContract.

Finally, we call our new Solidity function, which sends 10 DAI to Compound in exchange for cDAI.

The subsequent functions are not necessary, but they show the underlying balance and cDAI balance in which MyContract now holds. Remember to mint test DAI (described above) before you run the following command!

To execute the script, navigate to the project root directory and run:

node solidity-examples/supply-erc20-via-solidity.js

If successful, the output of the script will show something like this:

Now transferring DAI from my wallet to MyContract...
MyContract now has DAI to supply to the Compound Protocol.
MyContract is now minting cDAI...
Supplied DAI to Compound via MyContract
DAI supplied to the Compound Protocol: 9.999999999805051616
MyContract's cDAI Token Balance: 495.86440618

Remember that this code will work with any of the ERC20 tokens that Compound supports. You will need to swap in the corresponding ERC20 token contract address and ABI into the Node.js scripts.

Thanks for reading and be sure to subscribe to the Compound Newsletter. Feel free to comment on this post, or get in touch in the #development room on our very active Discord server.

Compound

The protocol for algorithmic, efficient Money Markets on the Ethereum blockchain. Seamlessly earn interest or borrow ERC-20 tokens without managing an order book.

Adam Bavosa

Written by

DevRel @ Compound.finance

Compound

Compound

The protocol for algorithmic, efficient Money Markets on the Ethereum blockchain. Seamlessly earn interest or borrow ERC-20 tokens without managing an order book.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade