Closed sherlock-admin closed 1 year ago
The analysis here is incorrect.
For GUSD (2 decimals)
Math: duration = 31,557,600 (1 year) stream amount = 1,000,000 = 1e6 token amount = 100,000,000 = 1e8
ratePerSecond() = 1e6 * 1e8 / 31557600 = 3168808.781402895 which rounds down to 3168808, representing 3.168808 GUSD tokens per seconds, which is $0.03168808 per second The non rounded ratePerSecond is: $1000000/31557600 = $0.03168808781402895 per second
the error per second is 0.03168808781402895 - 0.03168808 = $0.00000000781402895 over a year that is: 0.00000000781402895 * 31557600 = $0.24659199999252
to be convinced with code you can add the following lines at the end of the GUSD test:
vm.warp(stopTime - 1);
console.log("GUSD Recipient Balance: %d", s.balanceOf(recipient));
vm.warp(stopTime);
console.log("GUSD Recipient Balance: %d", s.balanceOf(recipient));
the logs will be:
GUSD Recipient Balance: 99999972
GUSD Recipient Balance: 100000000
so at 1 second before the stream ends, the recipient balance is $999,999.72
The precision loss appears to be negligible as shown by the sponsor comment.
Koolex
high
Non-negligible precision loss for tokens that have small decimals
Summary
Non-negligible precision loss in
ratePerSecond()
function for tokens that have less than 6 decimals such as GUSD (Gemini dollar).Vulnerability Detail
Let's assume:
We would have
Let's apply the math on 3 tokens USDC, GUSD and WETH
USDC (6 decimals):
ratePerSecond()
= 0.031688 lossPerSecond = ratePerSecondWithoutLoss - ratePerSecond lossPerSecond = 0.000000087814029 lossPer(Year-1) = 31_557_599 * lossPerSecond = 2.771199912185947 USDC which is negligibleGUSD (2 decimals):
ratePerSecond()
= 0.03 since lossPerSecond = ratePerSecondWithoutLoss - ratePerSecond lossPerSecond = 0.001688087814029 lossPer(Year-1) = 31_557_599 * lossPerSecond = 53271.998311913756371 GUSD which is not negligibleWETH (18 decimals):
ratePerSecond()
= 0.031688087814028950 lossPerSecond = ratePerSecondWithoutLoss - ratePerSecond lossPerSecond = 0,000000000000000050 lossPer(Year-1) = 31_557_599 * lossPerSecond = 0.00000000157788 WETH which is negligibleAs you can see, the ratePerSecond calculation results in non-negligible loss if the token for example has 2 decimals unlike tokens with 6 or 18 decimals. As per the sponsor, the contract should support popular ERC20 tokens.
Impact
Code Snippet
ratePerSecond()
https://github.com/sherlock-audit/2022-11-nounsdao/blob/main/src/Stream.sol#L125-L138PoC:
Then run:
You should get the following logs on the console:
Tool used
Manual Review
Recommendation
Consider normalizing the token to 18 decimals upon initializing the stream.