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: 14/63
Findings: 2
Award: $65.42
🌟 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
48.4804 USDC - $48.48
The name value
could be related to _address
. value
may confuse others with fund or numeric type.
/// @dev Governance function to set other liquidation contracts that may have taken vaults already. /// @param value The address that may be set/unset as another witch /// @param isWitch Is this address a witch or not function setAnotherWitch(address value, bool isWitch) external auth { otherWitches[value] = isWitch; emit AnotherWitchSet(value, isWitch); }
/// @notice Return how much collateral should be given out. function _calcPayout( DataTypes.Auction memory auction_, address to, uint256 artIn ) internal view returns (uint256 liquidatorCut, uint256 auctioneerCut) {
suggestion: Follow NATSPEC.
1e18
is used several times through the code. This may both obscure the purpose of the function and unnecessarily lead to potential error if the constants are changed during development.
102-108: require(initialOffer <= 1e18, "InitialOffer above 100%"); require(proportion <= 1e18, "Proportion above 100%"); require( initialOffer == 0 || initialOffer >= 0.01e18, "InitialOffer below 1%" ); 162-164: if (auctioneerReward_ > 1e18) { revert AuctioneerRewardTooHigh(1e18, auctioneerReward_); } 587: proportionNow = 1e18; 591: uint256(1e18 - initialProportion).wmul(elapsed.wdiv(duration));
Suggestion: Use constants as this would make the code more maintainable and readable while costing nothing gas-wise.
The division is performed first, and multiplication later.
594: uint256 inkAtEnd = uint256(artIn).wdiv(auction_.art).wmul(auction_.ink);
It might be arithmically safer to do the multiplication first instead.
#0 - alcueca
2022-07-22T14:14:19Z
Two useful, thank you.
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x29A, 0xKitsune, 0xNazgul, Aymen0909, Chom, Deivitto, ElKu, JC, JohnSmith, Kaiziron, Limbooo, MadWookie, Meera, ReyAdmirado, Rohan16, Sm4rty, SooYa, TomJ, Trumpero, Waze, __141345__, ajtra, ak1, antonttc, bulej93, c3phas, cRat1st0s, csanuragjain, defsec, durianSausage, fatherOfBlocks, gogo, hake, hickuphh3, ignacio, joestakey, karanctf, kyteg, m_Rassska, pashov, rajatbeladiya, rbserver, robee, rokinot, samruna, sashik_eth, simon135, tofunmi
16.9381 USDC - $16.94
When arguments are read-only on external functions, the data location can be calldata.
function _calcAuction( DataTypes.Vault memory vault, DataTypes.Series memory series, address to, DataTypes.Balances memory balances, DataTypes.Debt memory debt ) internal view returns (DataTypes.Auction memory) {
These parameters passed in are not modified in the function.
Another place is _calcPayout()
:
function _calcPayout( DataTypes.Auction memory auction_, address to, uint256 artIn ) internal view returns (uint256 liquidatorCut, uint256 auctioneerCut) {
Here
DataTypes.Auction memory auction_
can be delcated as
DataTypes.Auction calldata auction_
The demo of the gas comparsion can be seen here.
204: limits_.sum += auction_.ink; 259: limits[auction_.ilkId][auction_.baseId].sum -= auction_.ink; 430: limits_.sum -= auction_.ink; 443: auction_.ink -= inkOut.u128(); 444: auction_.art -= artIn.u128(); 450: limits_.sum -= inkOut.u128(); 598: liquidatorCut -= auctioneerCut;
Suggestion: Consider use X = X - Y to save gas
> 0
can be replaced with != 0
when dealing with uint.!= 0
costs less gas compared to > 0
for unsigned integers with the optimizer enabled (6 gas).
Here is the code as a demo.
Opcode discussion.
255: require(auction_.start > 0, "Vault not under auction"); 300: require(auction_.start > 0, "Vault not under auction"); 358: require(auction_.start > 0, "Vault not under auction"); 393: if (liquidatorCut > 0) { 398: if (auctioneerCut > 0) { 416: require(auction_.start > 0, "Vault not under auction");
Suggestion:
> 0
with != 0
.438: auction_.art - artIn >= debt.min * (10**debt.dec)
Can be writen as
438: unchecked { auction_.art - artIn >= debt.min * (10**debt.dec) }
Memory
can be used instead of Storage
inside a functionfunction _calcAuction() { // ... DataTypes.Line storage line = lines[vault.ilkId][series.baseId]; uint128 art = uint256(balances.art).wmul(line.proportion).u128(); if (art < debt.min * (10**debt.dec)) art = balances.art; uint128 ink = (art == balances.art) ? balances.ink : uint256(balances.ink).wmul(line.proportion).u128(); }
Since line
is not used later outside, it can be declared as memory
to save gas.
Or just cache line.proportion
, which was referred to twice.
uint64 memory proportion = line.proportion;
10**debt.dec
can be saved to an immutable variable in the cauldron
233: function _calcAuction() { // ... if (art < debt.min * (10**debt.dec)) art = balances.art; } 438: function _updateAccounting() { // ... require( auction_.art - artIn >= debt.min * (10**debt.dec), "Leaves dust" ); }
10**debt.dec
is used in _calcAuction()
and _updateAccounting()
, these 2 functions seem frequently used.
Suggestion:
Save 10**debt.dec
to an immutable variable in the cauldron
.