Platform: Code4rena
Start Date: 21/12/2023
Pot Size: $90,500 USDC
Total HM: 10
Participants: 39
Period: 18 days
Judge: LSDan
Total Solo HM: 5
Id: 315
League: ETH
Rank: 22/39
Findings: 1
Award: $271.14
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0x11singh99, 0xA5DF, 0xMilenov, 0xTheC0der, 7ashraf, Bauchibred, EV_om, Kaysoft, Sathish9098, SpicyMeatball, cheatc0d3, erebus, hash, imare, immeas, joaovwfreire, lil_eth, lsaudit, oakcobalt, para8956, peanuts, rvierdiiev, slvDev, trachev
271.1429 USDC - $271.14
##Summary
The code review reveals key issues in the provided smart contract repository. These include the necessity to verify the existence of unitId
before updating its hash and placing emits within if-statements to prevent false emissions. Other recommendations involve reverting on non-existent bonds, avoiding duplicate token names and symbols, and checking for zero addresses in constructors. The review suggests using modifiers for cleaner code, fixing comment typos, and streamlining redundant mathematical operations for efficiency. These insights offer a concise guide for enhancing the security and clarity of the smart contract implementation.
Issue | Title | Number of Instances |
---|---|---|
L-01 | Check existence of unitId in mapUnitIdHashes before updating hash | 5 |
L-02 | Move Depost emit inside if-statement to avoid false emits | 1 |
L-03 | Revert on non-existent bonds | 1 |
L-04 | Avoid duplicate token names and symbols in constructor | 2 |
L-05 | Funds owned by the contract cannot be sweeped or transferred | 1 |
L-06 | Inaccurate error message | 1 |
L-07 | Non-owner can burn tokens by transferring to address 0 | 1 |
L-08 | Assert to and amount greater than zero when minting | 1 |
L-09 | Add return values for mint and burn | 2 |
L-10 | Check if already un/paused before un/pausing | 2 |
N-01 | Utilize the use of modifiers | - |
N-02 | Fix typo in comments | 1 |
N-03 | Consider adding remaining time in revert message | 1 |
N-04 | Reduce redundant code | 1 |
N-05 | Inaccurate variable naming | 2 |
N-06 | Function create should emit an event | 1 |
N-07 | Boolean return value has no use | 1 |
N-08 | Utilize the use of the already defined error ZeroAddress | 2 |
N-09 | Utilize the use of Non-reentrant modifier for cleaner code | 1 |
N-10 | Check if new implementation address does not equal current address | 1 |
N-11 | Emit an event for increasing unlock time | 2 |
These findings provide actionable insights for enhancing the overall quality and robustness of the smart contract codebase.
unitId
exists inside the mapUnitIdHashes[unitId]
before updating its hashmapUnitIdHashes[unitId].push(unitHash);
Unit memory unit = mapUnits[unitId];
unitHashes = mapUnitIdHashes[unitId];
subComponentIds = mapSubComponents[uint256(unitId)];
function updateHash(IRegistry.UnitType unitType, uint256 unitId, bytes32 unitHash) external returns (bool success) { if (unitType == IRegistry.UnitType.Component) { success = IRegistry(componentRegistry).updateHash(msg.sender, unitId, unitHash); } else { success = IRegistry(agentRegistry).updateHash(msg.sender, unitId, unitHash); } }
if (amount > 0) { // OLAS is a solmate-based ERC20 token with optimized transferFrom() that either returns true or reverts IERC20(token).transferFrom(msg.sender, address(this), amount); + emit Deposit(account, amount, lockedBalance.endTime, depositType, block.timestamp); + emit Supply(supplyBefore, supplyAfter); } - emit Deposit(account, amount, lockedBalance.endTime, depositType, block.timestamp); - emit Supply(supplyBefore, supplyAfter);
480 function getBondStatus(uint256 bondId) external view returns (uint256 payout, bool matured) { 481 payout = mapUserBonds[bondId].payout; 482 // If payout is zero, the bond has been redeemed or never existed 483 if (payout > 0) { 484 matured = block.timestamp >= mapUserBonds[bondId].maturity; 485 } 486 }
The scenario happens if a user decides to create lock funds using the contract address, at the end no one will be able to withdraw the funds, and they will be locked. Consider adding a sweep tokens function to the contract
if (supply == 0) { revert ProductClosed(productId); }
to
and amount
greater than zero when mintingIERC20(rootToken).mint(to, amount);
mint
and burn
/// @dev Mints tokens. /// @param account Account address. /// @param amount Token amount. function mint(address account, uint256 amount) external; /// @dev Burns tokens. /// @param amount Token amount to burn. function burn(uint256 amount) external;
Check the state of contract if already pasued or not before pausing or not pausing
/// @dev Pauses the contract. function pause() external virtual { // Check for the ownership if (msg.sender != owner) { revert OwnerOnly(msg.sender, owner); } paused = true; emit Pause(msg.sender); } /// @dev Unpauses the contract. function unpause() external virtual { // Check for the ownership if (msg.sender != owner) { revert OwnerOnly(msg.sender, owner); } paused = false; emit Unpause(msg.sender); }
Protocol can achieve cleaner code by following separation of concerns and encapsulation priniciple instead of applying the same checks on msg.sneder each methdod, we can use a modifier like onlyOwner
or onlyManager
and appply the needed checks inside these modifiers.
- reminder + remainder
the following mathematical operations code be done in one operation
uint256 supplyBefore = supply; uint256 supplyAfter; // The amount cannot be less than the total supply unchecked { supplyAfter = supplyBefore - amount; supply = supplyAfter; }
to
unchecked{ supply = supply - amount; } ... emit Supply(supply + amount, supply - amount);
token
instead of account
/// @dev Token is non-transferable. /// @param account Token address. error NonTransferable(address account);
token
instead of account
/// @dev Token is non-delegatable. /// @param account Token address. error NonDelegatable(address account);
create
should emit an eventThe boolean return value success
indicating the function successful behaviour has no use and can be neglected as the function reverts on failure, and there is no cas where the function does not revert and success
is set to false.
function updateHash(address unitOwner, uint256 unitId, bytes32 unitHash) external virtual returns (bool success) { ... success = true; emit UpdateUnitHash(unitId, unitType, unitHash); }
ZeroAddress()
The contract defines an error which is called ZeroAddress()
in the following lines
IErrorsTokenomics.sol #17 error ZeroAddress();
but when comes to checking for zero address, it uses exipicit checking for zero addresses
- if (_bondCalculator != address(0)) { - bondCalculator = _bondCalculator; - emit BondCalculatorUpdated(_bondCalculator); - } + if (_bondCalculator == address(0)) { + revert ZeroAddress(); + } + emit BondCalculatorUpdated(_bondCalculator);
On increasing unlock time, function should emit an event with the parameters including account, previous lock time, and new lock time
#0 - c4-pre-sort
2024-01-10T14:41:28Z
alex-ppg marked the issue as insufficient quality report
#1 - c4-pre-sort
2024-01-10T14:41:47Z
alex-ppg marked the issue as sufficient quality report
#2 - c4-judge
2024-01-20T14:47:46Z
dmvt marked the issue as grade-b
#3 - c4-judge
2024-01-20T18:37:10Z
dmvt marked the issue as grade-a