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: 7/63
Findings: 2
Award: $138.16
🌟 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
39.0709 USDC - $39.07
Finding | Instances | |
---|---|---|
[L-01] | Missing address(0) check | 3 |
Finding | Instances | |
---|---|---|
[N-01] | The use of magic numbers is not recommended | 5 |
[N-02] | Typo | 2 |
[N-03] | Remove TODO’s | 1 |
Contract | Total Instances | Total Findings | Low Findings | Low Instances | NC Findings | NC Instances |
---|---|---|---|---|---|---|
Witch.sol | 10 | 4 | 1 | 3 | 3 | 7 |
Witch.sol | 1 | 1 | 0 | 0 | 1 | 1 |
address(0)
checkFunds might be lost by accident if check is not implemented. 3 instances of this issue have been found:
[L-01] Witch.sol#L286-L301
function payBase( bytes12 vaultId, address to, uint128 minInkOut, uint128 maxBaseIn ) external returns ( uint256 liquidatorCut, uint256 auctioneerCut, uint256 baseIn ) { DataTypes.Auction memory auction_ = auctions[vaultId]; require(auction_.start > 0, "Vault not under auction");
[L-01b] Witch.sol#L176-L177
function auction(bytes12 vaultId, address to)
[L-01c] Witch.sol#L344-L359
function payFYToken( bytes12 vaultId, address to, uint128 minInkOut, uint128 maxArtIn ) external returns ( uint256 liquidatorCut, uint256 auctioneerCut, uint256 artIn ) { DataTypes.Auction memory auction_ = auctions[vaultId]; require(auction_.start > 0, "Vault not under auction");
Consider setting constant numbers as a constant
variable for better readability and clarity.
5 instances of this issue have been found:
[N-01] Witch.sol#L587-L588
proportionNow = 1e18;
[N-01b] Witch.sol#L105-L106
initialOffer == 0 || initialOffer >= 0.01e18,
[N-01c] Witch.sol#L108-L109
require(proportion >= 0.01e18, "Proportion below 1%");
[N-01d] Witch.sol#L162-L163
if (auctioneerReward_ > 1e18) {
[N-01e] Witch.sol#L591-L592
uint256(1e18 - initialProportion).wmul(elapsed.wdiv(duration));
Please fix typos. 2 instances of this issue have been found:
[N-02] Witch.sol#L81
/// @param param Name of parameter to set (must be "ladle")
[N-02b] Witch.sol#L14
uncollateralized -> undercollateralized
They add unnecessary cluttler and harm readbility for auditors. 1 instance of this issue has been found:
[N-03] Witch.sol#L577-L578
// TODO: Replace this contract before then 😰 -> This is funny though
#0 - alcueca
2022-07-22T13:57:51Z
Thanks for finding the typo, the other three are not useful. Please read the README in the future.
🌟 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
99.0875 USDC - $99.09
Finding | Instances | |
---|---|---|
[G-01] | Multiple address mappings can be combined into a single mapping of an address to a struct, where appropriate | 1 |
[G-02] | require() /revert() checks used multiple times should be turned into a function or modifier | 7 |
[G-03] | uint s smaller than uint256 cost more gas | 4 |
[G-04] | Use custom errors rather than revert() /require() strings to save gas | 11 |
Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot. Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operatio 1 instance of this issue has been found:
[G-01] Witch.sol#L65-L69
mapping(bytes12 => DataTypes.Auction) public auctions; mapping(bytes6 => mapping(bytes6 => DataTypes.Line)) public lines; mapping(bytes6 => mapping(bytes6 => DataTypes.Limits)) public limits; mapping(address => bool) public otherWitches; mapping(bytes6 => mapping(bytes6 => bool)) public ignoredPairs;
require()
/revert()
checks used multiple times should be turned into a function or modifierDoing so increases code readability decreases number of instructions for the compiler. 7 instances of this issue have been found:
[G-02] Witch.sol#L328-L329
require(baseJoin != IJoin(address(0)), "Join not found");
[G-02b] Witch.sol#L365-L366
require(liquidatorCut >= minInkOut, "Not enough bought");
[G-02c] Witch.sol#L313-L314
require(liquidatorCut >= minInkOut, "Not enough bought");
[G-02d] Witch.sol#L416-L417
require(auction_.start > 0, "Vault not under auction");
[G-02e] Witch.sol#L358-L359
require(auction_.start > 0, "Vault not under auction");
[G-02f] Witch.sol#L300-L301
require(auction_.start > 0, "Vault not under auction");
[G-02g] Witch.sol#L255-L256
require(auction_.start > 0, "Vault not under auction");
uint
s smaller than uint256
cost more gasWhen using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size. Solidity docs 4 instances of this issue have been found:
[G-03] Witch.sol#L63
uint128 public auctioneerReward = 0.01e18;
[G-03b] Witch.sol#L232-L233
uint128 art = uint256(balances.art).wmul(line.proportion).u128();
[G-03c] Witch.sol#L234-L235
uint128 ink = (art == balances.art)
[G-03d] Witch.sol#L303-L304
uint128 artIn = uint128(
revert()
/require()
strings to save gasCustom errors save ~50 gas each time they're hit by avoiding having to allocate and store the revert string. 11 instances of this issue have been found:
[G-04] Witch.sol#L437-L440
require( auction_.art - artIn >= debt.min * (10**debt.dec), "Leaves dust" );
[G-04b] Witch.sol#L416-L417
require(auction_.start > 0, "Vault not under auction");
[G-04c] Witch.sol#L395-L396
require(ilkJoin != IJoin(address(0)), "Join not found");
[G-04d] Witch.sol#L365-L366
require(liquidatorCut >= minInkOut, "Not enough bought");
[G-04e] Witch.sol#L358-L359
require(auction_.start > 0, "Vault not under auction");
[G-04f] Witch.sol#L328-L329
require(baseJoin != IJoin(address(0)), "Join not found");
[G-04g] Witch.sol#L313-L314
require(liquidatorCut >= minInkOut, "Not enough bought");
[G-04h] Witch.sol#L300-L301
require(auction_.start > 0, "Vault not under auction");
[G-04i] Witch.sol#L102-L108
require(initialOffer <= 1e18, "InitialOffer above 100%"); require(proportion <= 1e18, "Proportion above 100%"); require( initialOffer == 0 || initialOffer >= 0.01e18, "InitialOffer below 1%" ); require(proportion >= 0.01e18, "Proportion below 1%");
[G-04j] Witch.sol#L189-L190
require(cauldron.level(vaultId) < 0, "Not undercollateralized");
[G-04k] Witch.sol#L200-L201
require(limits_.sum <= limits_.max, "Collateral limit reached");