Chainlink price feed decimals

Remy NTSHAYKOLO
4 min readFeb 6, 2022

--

Introduction

Price feeds addresses can be found here : https://docs.chain.link/docs/ethereum-addresses/

Price feeds provide conversion rate between two assets.

Price feed columns names
eth / usd price feed

The Dec column reefers to the number of decimals the value will have. What does it mean?

At the given time this price feed (eth/dollar) return the following value: 299267876787. The 8 decimals literally means that the number that was returned has 8 decimal. Thus, 299267876787 should be read as 2992,67876787. (67876787 are the 8 decimals)

Problem to solve

Solidity does not handle decimals ok I get it, so I could just divide by 10**8 to work with the “real” rate in my smart contract => 299267876787 / 10**8 = 2992. Hum, that is actually not a great idea, let me explain you why.

Given the following smart contract from the wonderful solidity course (https://www.youtube.com/watch?v=M576WGiDBdQ&t=22934s&ab_channel=freeCodeCamp.org).

Basically, it is a lottery. You have to pay a certain dollar amount to get in. here it is 50$.

The function enter check wether the value associated with the transaction (which is in wei) is greater than the entrance fee. If it does, the sender address of the transaction is added to the players array.

Let zoom on the getEntranceFee() function.

```(, int256 price, , , ) = ethUsdPriceFeed.latestRoundData();``` This line retrieve the price feed (ETH/USD), it sets the price variable to 299267876787.

``` uint256 adjustedPrice = uint256(price).div(1e8);``` This line converts the previous value to “real”. adjustedPrice is set to 2992.

``` uint256 entranceFee = usdEntryFee / adjustedPrice;``` This line convert the 50$ entrance fee into ether. entranceFee is set to 0. 🚨🚨🚨 ALERT BAD BEHAVIOUR 🚨🚨🚨 Solidity does not handle float. In solidity 3/2 =1 (to get 1 basically 3/2=1.5 and it just truncates the decimal values). So 50/2992 =0 (Because 50/2992=0.016, truncating the decimals it is equal to 0) . I told you it was a bad idea! Ok so what should I do?

In order to make the division 50/2992 different than 0, we should multiply 50 by a great value. Which value should we use? 1e3? 1e18? 1e12? (The answer is 1e18, 1e3 and 1e12 are just random values I choosed)

Solution

A small analogy:

Bob walk home to work in 2 hour. There is 1 kilometer between his home and his work. What speed Bob walks at?

In order to compute the speed, we use the formula v =d (km) /t(h) so v = 1/2 . Problem in solidity this will lead to a speed of 0 km/h. To solve this, we multiply 1 kilomoter in meter.

v = d *1000 (m) / t (h) = 1000 /2 = 500 m/h. That is ok in solidity cause the ratio is greater than 0.

If then I wanted to compare the bob walk speed with Alice walk speed (which is given in km/h) I should convert Alice speed in m/h then proceed to the comparison.

The idea here is given a division : Numerator / Denominator, we should always ensure that the Numerator is greater than the Denominator and consequently adjust the unit of the result.

Back to our initial problem.

The function enter, compare the msg.value which is in wei (1 eth = 1e18 wei) with the output of getEntranceFee. So the output of this function should be in wei, in order to compare values with same units.

We should adjust this line to make it return wei ``` uint256 entranceFee = usdEntryFee / adjustedPrice;```

The ratio usdEntryFee / adjustedPrice give an ether value. To convert it into wei we should multiply it by 1e18 (1eth = 1e18 wei).

🚨🚨🚨 THE ORDER OF OPERATION MATTER HERE 🚨🚨🚨.

Wrong order : 50/2992 * 1e18 => In solidity this will lead to 0 because 50/2992 =0 and 0 *1e18 =0

Instead we should do (50*1e18)/2992 = 16622340425531914 wei

Now in the enter function we can easily compare the sender input value with the entrance fee because both are in wei.

I hope it helped!

In the end don’t forget to test your code whit unit tests, this way, you will not have bad surprise!

--

--