Philipp Muens | pmuens.eth
Philipp Muens | pmuens.eth

Philipp Muens | pmuens.eth

How to calculate percentages in Solidity

Photo by Aaron Lefler on Unsplash

How to calculate percentages in Solidity

Using Basis Points to implement percentage calculations with integers

Philipp Muens | pmuens.eth's photo
Philipp Muens | pmuens.eth
·Jul 13, 2022·

5 min read

Subscribe to my newsletter and never miss my upcoming articles

Solidity is one of the most used Smart Contract programming languages out there. Given its ubiquity it's no surprise that a lot of popular DeFi Protocols rely on it to encode financial concepts and perform calculations with varying degree of complexity.

If you ever attempted to write your own Solidity Smart Contract to process financial data you might've come across the problem of calculating percentages properly.

Reading through this Blog Post you'll learn what Basis Points are, why should use regular integers to represent numbers in finance and how you can calculate percentages in Solidity with high precision. Let's dive into it!

Calculating Percentages

Before we jump straight into the Solidity bits we should recap the math behind percentage calculations.

To calculate what p% of x is you'll use the following formula:

$$ y = \frac{x * p}{100} $$

Let's walk through some examples to see the formula in action.

First off let's do a simple sanity check and calculate what 25% (p) of 100 (x) is. Doing the math in our head, we know that 25% of 100 is 25. Let's plug in the numbers and see what happens.

$$ y = \frac{100 * 25}{100} = 25 $$

Yes, that seems to be working as expected. Next up, let's calculate what 2,5% (p) of 7000 (x) is:

$$ y = \frac{7000 * 2,5}{100} = 175 $$

Using an Online Calculator to double-check we can confirm that our calculation is correct as well.

With that out of the way it's time to translate the math into Solidity code!

Floating- and Fixed-Point Numbers

If we try to implement the formula in Solidity as is and work through the above examples we'll immediately run into an obvious problem: How do we express percentages that are fractions such as 2,5%?

The issue is that Solidity neither fully supports Floating-Point Numbers nor Fixed-Point Numbers. In fact there's support for the definition of Fixed-Point Numbers in Solidity but you cannot do calculations with them as of yet.

You might be familiar with the float or double data types in other Programming Languages that can be used to represent values such as 3,1415 or fractions like 1/4.

In Solidity we're currently limited to do math calculation solely with integers. The good news is that we can leverage some tricks to perform computations on integers that would normally best be done with float or double data types.

How would this look like in our case?

Meet: Basis Points

As luck would have it, there's already a well-known measurement unit in finance that solves all our problems: Basis Points (commonly abbreviated as bp or bps).

As per the Wikipedia definition:

A basis point is one hundredth of 1 percentage point.

Confused? Let's unpack this sentence.

If you look at the sentence and squint you can derive that 1% is 100bps.

So 2,5% would be 250bps and 7,48% would be 748bps. Looks like this is exactly what we need.

But how can we use Basis Points in percentage calculations? It's actually pretty simple!

Notice that to translate percentages to Basis Points we had to multiply the percentage value by 100. When we want to use Basis Points in percentage calculations like above we just need to "scale down" the percentage calculation by 100. To do this we simply need to update the denominator from 100 to 10.000 (100 * 100). That's it.

Solidity Code

Now we're well equipped to translate our percentage formula into Solidity code.

// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8;

contract Percentages {
    function calculate(uint256 amount, uint256 bps) public pure returns (uint256) {
        return amount * bps / 10_000;
    }
}

Running our examples through it we can see that the function works as expected!

One minor change we can make is to require that the amount multiplied by the Basis Points is greater than or equal to 10.000. This ensures that we're not running into the issue of values being rounded down to 0.

// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8;

contract Percentages {
    function calculate(uint256 amount, uint256 bps) public pure returns (uint256) {
        require((amount * bps) >= 10_000);
        return amount * bps / 10_000;
    }
}

Note: With the percentage calculation above, make sure to multiply first before doing the division. Doing the division first can result in values being rounded down to 0 before the multiplication is applied which will cause a multiplication by 0.

Conclusion

And there you have it. An elegant way to calculate percentages in Solidity without the need for float or double data types by virtue of using Basis Points as measurement units.

While it might feel like some form of "hack" to use integers and Basis Points for financial calculations it's actually recommended to do it this way. Floating- and Fixed Point Numbers are notoriously hard to work with as they'll introduce rounding errors and imprecision. Something we don't really want to see in our DeFi Protocols. Maybe it's a good thing to be restricted to only use integers after all.

I hope that you found this article useful. Feel free to ping me on Twitter if you have any questions or feedback.

Additional Resources

 
Share this