Platform: Code4rena
Start Date: 14/07/2022
Pot Size: $25,000 USDC
Total HM: 2
Participants: 63
Period: 3 days
Judge: PierrickGT
Total Solo HM: 1
Id: 147
League: ETH
Rank: 33/63
Findings: 1
Award: $44.80
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: hickuphh3
Also found by: 0x29A, 0x52, 0xNazgul, Chom, Deivitto, ElKu, Funen, IllIllI, Meera, ReyAdmirado, SooYa, TomJ, Trumpero, Waze, __141345__, ak1, asutorufos, c3phas, cRat1st0s, csanuragjain, delfin454000, exd0tpy, fatherOfBlocks, hake, hansfriese, horsefacts, hyh, karanctf, kenzo, kyteg, ladboy233, pashov, peritoflores, rajatbeladiya, rbserver, reassor, rokinot, simon135, wastewa
44.8039 USDC - $44.80
Collateral rounding error
In the following line there is a division before multimplication. As artIn < auction_.art
then this division will be zero.
uint256 inkAtEnd = uint256(artIn).wdiv(auction_.art).wmul(auction_.ink);@audit div before mul
Multiply before divide
[-] uint256 inkAtEnd = uint256(artIn).wdiv(auction_.art).wmul(auction_.ink); [+] uint256 inkAtEnd = uint256(artIn).wmul(auction_.ink).wdiv(auction_.art);
#0 - HickupHH3
2022-07-18T14:04:23Z
See libraries wdiv
and wmul
, it'll be doing multiplication then division.
#1 - ecmendenhall
2022-07-19T04:25:04Z
#2 - HickupHH3
2022-07-19T10:29:40Z
IMO it shouldn't be a duplicate; #28 does a much better job at explaining why the rounding error can still occur despite the code doing multiplication first, then division: (artIn * 1e18) / auction_.art * auction_.ink / 1e18
.
Edit: Nvm it isn't up to me to decide, should only be giving objective factual comments.
#3 - alcueca
2022-07-21T10:54:04Z
It doesn't matter which finding is better described, they are both invalid.
uint256(artIn).wdiv(auction_.art).wmul(auction_.ink);
is equivalent to artIn * 1e18 / auction_.art * auction_.ink / 1e18
#4 - PierrickGT
2022-07-29T10:15:04Z
Agree with sponsor and Hickup, we do multiply first by 1e18
and then divide by 1e18
, so it will cancel each other out.
For this reason, I have labelled both issues as being invalid.
#5 - dmitriia
2022-08-05T20:28:53Z
Agree with sponsor and Hickup, we do multiply first by
1e18
and then divide by1e18
, so it will cancel each other out. For this reason, I have labelled both issues as being invalid.
It's understood, but this doesn't look to be enough, please kindly read the description of #28
#6 - dmitriia
2022-08-08T15:18:31Z
It doesn't matter which finding is better described, they are both invalid.
uint256(artIn).wdiv(auction_.art).wmul(auction_.ink);
is equivalent toartIn * 1e18 / auction_.art * auction_.ink / 1e18
@alcueca The expressions aren't equivalent and there are a range of cases where precision loss leads to value rounding as it is shown in #28. I.e. both findings are valid. The severity is debatable, I believe as 1e18 does cover a substantial chunk of cases (but not all of them as you imply here), so it should be medium. It would be high if the precision was lost in majority of the cases
#7 - HickupHH3
2022-08-08T17:58:26Z
@dmitriia I thought about this, the issue would still persist regardless of order in a different manner.
The fix: artIn * auction_.ink / 1e18 * 1e18 / auction_.art
will render zero in cases where the current implementation wouldn't: (artIn * 1e18) / auction_.art * auction_.ink / 1e18
. Consider the case where
artIn = 1e10
auction_.ink = 1e6
auction_.art = 1e15
The fix would yield 1e10 * 1e6 / 1e18 * 1e18 / 1e15 = 0
while the current implementation would yield 1e10 * 1e18 / 1e15 * 1e6 / 1e18 = 10
.
#28 makes a point of the fyToken amount auction_.art
being generally greater than 1e18
, which I agree with. However, I think the likelihood of it exceeding 1e36 is very low.
#8 - dmitriia
2022-08-08T18:22:24Z
@HickupHH3 Yes, you are right, the proper fix here is an introduction of additional precision enhancing multiplier, switching wdiv with wmul just reduces the probability of encountering the issue, altering, but not removing the surface.
The issue itself can be formulated as 'using wad math isn't enough to fully counter division precision loss'. The real life probability might be low, but these two issues aren't invalid, just describe a corner case (as many others actually are, which is natural, as main case is usually already thought of)
#9 - PierrickGT
2022-08-08T20:16:54Z
@dmitriia @HickupHH3 thanks for looking into this issue.
@GalloDaSballo wrote a script to compare the two order of operations and noticed that the rounding difference does exist but it's in an order of magnitude of 1 billionth of a ETH (1e9), which is negligible.
For this reason, I will remove the invalid
label and downgrade this issue to a QA Report one.
The script is available here: https://gist.github.com/GalloDaSballo/1f86b550b45975445848bec61353f45e
And here is the result: