Published in


How we built on-chain APR for Ethereum DeFi

For on-chain investment decisions we need data. There exists aggregation sites that provide off-chain aggregation of defi rates; is a great example. We could simply go look where we can get the highest rates;

Easy, let’s invest our DAI into Fulcrum. But how can we make this decision on-chain? How can a smart contract have this information to make this decision?

Let’s start with compound;

Easy enough, cTokens have a supplyRatePerBlock function that returns a uint. How many blocks does compound consider to be in a year? No quick lookup in cTokens that tell us this, and no documentation on cTokens to explain this. So we go over to their github. cToken.sol does not include any data on the blocks per year, however we can see the amount of blocks is in InterestRateModel.sol;

cToken.sol inherits from cTokenInterfaces.sol, which has;

So we have a public interestRateModel variable.

Let’s confirm with the contract;

Which leads us to;

Perfect, so we can read from cToken to InterestRateModel and get blocksPerYear.

So putting it together;

APR is;


Next we tackle Fulcrum

fulcrum (bZx)

Started at, no docs, but it shows “Powered by bZx”

Documentation doesn’t give any real information. Selecting “Smart Contracts” just goes to the github repo. Guess we are digging;

Searching the ABI for “rate” gave us;

We can find the token addresses in their mainnet registry (click from integrations in documentation)

Read as proxy gives us;

So easy enough, putting it together

iToken(address).supplyInterestRate() //this is returned as 1e20, so /100

Difficult to find, but easy to use;

Next dYdX


Not a lot of information;

Also doesn’t really help.

Getters.sol had some functions that looked viable;

Borrower rate though, we want supplier rate;

Need to do some figuring out here;

So we have getMarketInterestRate() which gives us the interest rate for that market (the borrower). This is a per second value, so we need to multiple adjusted for a year. We know that borrow rate is divided amongst all suppliers. We can get the borrows and supply from getMarketTotalPar() to get utilization.


uint256 borrow = getMarketInterestRate(marketID)*secondsPerYear;
uint256 usage = getMarketTotalPar(marketID).borrow / getMarketTotalPar(marketID).supply;
uint256 apr = borrow * usage * getEearningsRate()

I did not find any direct documentation explaining that process.

Next Aave


Easy enough getReserveData(_reserve). Reserves are just the underlying token address. So if we wanted to do DAI, then it is;


Upon implementation though, turns out getReserveData returns 13 variables. Maximum stack depth is 16 variables. So including an address argument local variable and liquidityRate return local variable, we can’t call this function from an agnostic function. I want to build something that generates the liquidityPool everytime and does not save this is global space.

So trying to implement getReserveData did not work. Instead, let’s see what getReserveData does;

LendingPool.getReserveData calls dataProvider.getReserveData, so we look it up;

We see liquidityRate is provided by core.getReserveCurrentLiquidityRate(_reserve);

We can get LendingPoolCore directly from the lendingPoolAddressesProvider, and then call Core directly, so putting it together;

LendingPoolCore core = LendingPoolCore(LendingPoolAddressesProvider(AAVE).getLendingPoolCore());

Aave stores liquidityRate as a RAY from WadRayMath (1e27), so we adjust by (1e9).

Putting it all together;

Now that we have all the helper functions, we simply map the address providers and addresses, build rollup functions that take generic into individual, into group. And we end up with;

APR Oracle

And now finally, we can build a recommendation system;

To interact with the contract you can read the specification at;



-- defi made simple

Recommended from Medium

JediSwap — The first fully permissionless composable AMM on Starknet

Does decentralization & blockchain bring us closer to nature?

BSClaunch Research: An In-depth overview About Pet TapMe

R3 Corda: The Enterprise Blockchain

Blockchain’s Role in Today’s Internet of Things (IoT) Landscape

The VASIL Staking Pool

How to build Web3.0 better for the hard-growing DAPP?

Merlinlab- Auto-compounding yield aggregator with an ecosystem optimized to focus on safe and…

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Andre Cronje

Andre Cronje

More from Medium

Yearn Finance is Sponsoring the ETHAmsterdam 2022 Hackathon

📐 Curve + Yearn + Babylon

Decentralized governance and the Howey Test

DXdao is Excited to Attend ETHDenver 2022