Platform: Code4rena
Start Date: 08/03/2023
Pot Size: $60,500 USDC
Total HM: 2
Participants: 123
Period: 7 days
Judge: hansfriese
Id: 220
League: ETH
Rank: 57/123
Findings: 2
Award: $48.97
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xSmartContract
Also found by: 0x1f8b, 0x6980, 0xAgro, 0xSolus, 0xhacksmithh, 0xkazim, ABA, BPZ, BowTiedOriole, ChainReview, DadeKuma, DeFiHackLabs, Deathstore, DevABDee, Diana, Dravee, Dug, Englave, Go-Langer, Haipls, IceBear, Inspex, Jeiwan, Kek, Kresh, Madalad, MatricksDeCoder, MyFDsYours, RaymondFam, Rolezn, SAAJ, Sathish9098, Taloner, Udsen, Viktor_Cortess, atharvasama, ayden, brgltd, btk, carlitox477, catellatech, chaduke, codeislight, deadrxsezzz, descharre, erictee, fatherOfBlocks, favelanky, glcanvas, handsomegiraffe, jasonxiale, jekapi, joestakey, lemonr, luxartvinsec, martin, matrix_0wl, minhquanym, mrpathfindr, nadin, oyc_109, parsely, peanuts, pfedprog, rbserver, rokso, saian, santipu_, scokaf, slvDev, tsvetanovv, ubl4nk, ulqiorra, yamapyblack, zaskoh
29.6697 USDC - $29.67
address(0)
The following methods have a lack of checks if the received argument is an address, it's good practice in order to reduce human error to check that the address specified in the constructor or initialize is different than address(0)
.
Affected source code:
The changeStakingContractAddress
and changeTreasuryContractAddress
methods do not emit an event when the state changes, something that it's very important for dApps and users.
Affected source code:
It does not check the return returned in the case of ERC-20, so a token that returns false could incur economic losses for the user or the project.
It is necessary to check that if data
is a boolean, it is positive, just like Open Zeppelin's SafeTransfer does.
Affected source code:
Tab was used instead of a space:
S2) and BYTES for time-locked emission rewards. The staker operates on a
Affected source code:
It is not good practice to hardcode values, but if you are dealing with addresses much less, these can change between implementations, networks or projects, so it is convenient to remove these values from the source code.
Affected source code:
Use the selector instead of the raw value:
#0 - c4-judge
2023-03-17T02:36:58Z
hansfriese marked the issue as grade-b
#1 - TimTinkers
2023-03-21T01:11:07Z
Acknowledging the typo, thanks.
#2 - c4-sponsor
2023-03-21T01:11:11Z
TimTinkers marked the issue as sponsor acknowledged
🌟 Selected for report: JCN
Also found by: 0x1f8b, 0xSmartContract, 0xSolus, 0xhacksmithh, 0xnev, Angry_Mustache_Man, Aymen0909, Diana, Flora, Inspex, Madalad, MatricksDeCoder, MiniGlome, R-Nemes, RaymondFam, ReyAdmirado, Rolezn, SAAJ, Sathish9098, Shubham, Udsen, Viktor_Cortess, arialblack14, atharvasama, ayden, c3phas, carlitox477, descharre, dharma09, durianSausage, fatherOfBlocks, ginlee, glcanvas, hunter_w3b, leopoldjoy, matrix_0wl, mrpathfindr, nadin, oyc_109, pipoca, schrodinger, slvDev, ulqiorra, volodya
19.3029 USDC - $19.30
stringEquals
It is possible to optimize the comparison of 2 string using a hash function.
Sample:
pragma solidity ^0.8.19; contract testA { function stringEquals ( string memory _a, string memory _b ) public pure returns (bool) { bytes memory a = bytes(_a); bytes memory b = bytes(_b); // Check equivalence of the two strings by comparing their contents. bool equal = true; assembly { let length := mload(a) switch eq(length, mload(b)) // Proceed to compare string contents if lengths are equal. case 1 { let cb := 1 // Iterate through the strings and compare contents. let mc := add(a, 0x20) let end := add(mc, length) for { let cc := add(b, 0x20) } eq(add(lt(mc, end), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { // If any of these checks fails then arrays are not equal. if iszero(eq(mload(mc), mload(cc))) { equal := 0 cb := 0 } } } // By default the array length is not equal so the strings are not equal. default { equal := 0 } } return equal; } } contract testB { function stringEquals ( string memory _a, string memory _b ) public pure returns (bool) { bytes memory a = bytes(_a); bytes memory b = bytes(_b); if (a.length != b.length) return false; return keccak256(a) == keccak256(b); } }
With my result it's cheaper using keccak256
:
+ With one character ("a") or 32 characters ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"): 1251 before, 1173 after + With 64 characters ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"): 1356 before, 1197 after
Because is only used for if (_stringEquals(class, "Hand of Citadel")) {
that is less than 32 bytes, it will be cheaper using keccak256
Affected source code:
stake
The following logic is redundant an can be removed because it's not possible to send a different type and also it's checked that the rewardCount
has a value with _pools[_assetType].rewardCount == 0
.
function stake ( AssetType _assetType, uint256 _timelockId, uint256, uint256, uint256 ) external nonReentrant { - // Validate that the asset being staked is of a valid type. - if (uint8(_assetType) > 4) { - revert InvalidAssetType(uint256(_assetType)); - } // Validate that the asset being staked matches a configured pool. if (_pools[_assetType].rewardCount == 0) { revert UnconfiguredPool(uint256(_assetType)); }
Affected source code:
#0 - c4-judge
2023-03-17T03:42:57Z
hansfriese marked the issue as grade-b
#1 - c4-sponsor
2023-03-21T01:12:23Z
TimTinkers marked the issue as sponsor confirmed
#2 - TimTinkers
2023-03-21T01:12:37Z
Thanks, good catch.