Solidity: Floating points and precision
Photo by Nenad Novaković on Unsplash
Here is an article I wrote on June 23, 2022. I suggest you read it.
Looking at Solidity today since the days of v0.4.24, it still has no floating or fixed-point numbers. There is no data type for floating point numbers. This means that integer types in Solidity must be used for the representation of floating-point numbers.
With the way it is, developers are now required to have their floating-point numbers implemented by using the standard integer data type. This can lead to vulnerabilities if not implemented correctly.
Take a look at the code below
// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;
contract FunWithNumbers {
uint public tknPerEther = 10;
uint public weiPerEther = 1e18;
mapping(address => uint) public balance;
function buyToken() external payable {
// converting wei to eth, then multiplying by the token rate
uint token = msg.value/weiPerEther*tknPerEther;
balance[msg.sender] += token;
}
function sellTokens(uint tokens) public {
require(balance[msg.sender] >= tokens);
uint eth = tokens/tknPerEther;
balance[msg.sender] -= tokens;
payable (msg.sender).transfer(eth*weiPerEther);
}
}
The above is a simple contract for buying and selling token.
Looking at it closely, you will find out that the absence of floating point numbers will result to errors, although the mathematical calculations for buying and selling is correct.
Looking at the buyToken function, if the msg.value is less than 1 ether, no matter how close to 1 ether it is, the division will result in 0. It is going to have a pretty major effect on the code. An effect like leaving the final calculation (multiplication) to be 0. This is going to be a major problem for users if this is deployed on the blockchain network.
Also, in the sellToken function, any token less than 10 is going to result in 0.
If you have an int and you try to deal with it as a float, it will round your number, but it might not be in the way you expected.
The problem with the contract is the precision being only to the nearest ether (1e18 wei).
When dealing with higher precision in any smart contract, developers have to be really careful.
What are the prevention measures that can be taken to avoid this vulnerability?
It is important to maintain the right precision in smart contracts.
Make sure that any ratios or rates you use allow for big fractional numerators. In our case, we utilized the rate tknPerEther. WeiPerToken, which would be a sizable amount, would have been preferable. We might use the operation msg.value/weiPerToken to determine the appropriate number of tokens. This would result in a more accurate outcome.
Being aware of the order of operations is another strategy to keep in mind. In our illustration, the formula to calculate the cost of a token was msg.value/weiPerEther\tknPerEther. Keep in mind that the division comes first, then the multiplication. (Solidity, unlike some other languages, ensures that operations are carried out in the order they are written.) If the calculation had first performed the multiplication and then the division, as in msg.value*tknPerEth/weiPerEther*, this example would have been more precise.
Ultimately, it can be a good idea to convert values to higher precision before performing any necessary mathematical operations, and then finally convert back to the precision needed for output when specifying arbitrary precision for integers. Uint256s are frequently employed (because they are best for gas usage), and they provide about 60 orders of magnitude of range, part of which can be devoted to the accuracy of mathematical operations. It could be preferable to keep all variables at high precision in Solidity and convert them back to lesser precisions in third-party applications. In ERC20 token contracts, this is essentially how the decimals variable operates.
This is just one of the vulnerabilities you have to look out for when writing your smart contracts. In other articles to come, we will talk about others to look out for so you can have a safe smart contract that cannot be easily attacked.
Don’t forget to put your thoughts in the comment section and turn on your notification.
SEE YOU NEXT TIME.