Spectre.io team requested the review of the contract under the repository HODLVaultSeviceContract referenced by the commit bb51ac9001b4316562601afc6415210b1bc196cc: HODLVaultSeviceContract.sol.
The rest of the contracts in the repositories are assumed to be audited.
-
H1 - line 310 - Admin index loss - Once an admin is about to be removed, the last admin in
hodlAdminsAddressListis being used to replace the place of the admin to be removed. Then, the last admin is being popped fromhodlAdminsAddressListbecause it is already in the place (index) of the removed admin. The value of the removed admin inhodlAdminsAddressListIndicesis being cleared with0but the last admin's value used to replace its place is not updated. This will end up with wrong indices, admins that can't be removed, and/or undesired admins being removed. Consider updating the index value of the admin used to override inhodlAdminsAddressListIndicesto keep the indices up to date. Update: The team fixed it on 0b1d477e7fec56db570ca41fefe6345176c534ef by updating the index value fo the admin used to override. -
H2 - line 375 - Deposited amount mismatch - If
tokenAddressis a deflationary token or a token where the amount received after a transfer differs fromamount, the balance of the smart contract after the transfer will be different than the amount and fees computed. Therefore, users may not be able to withdraw their deposits. Consider checking the balance before and after thetransferFromto get the actual deposited amount, and calculate the fees. Update: The team fixed it on 0b1d477e7fec56db570ca41fefe6345176c534ef but added a new vulnerability where after calling _calculateDepositFee_for the second time,acceptis not being checked. Also callingcalculateDepositFeetwice is not necessary. -
H3 - line 382, 385, 410, 465, 482, 487 - Possible critical overflow - Even as it is unlikely to happen if a
tokenAddressreaches the maximumuintpossible for its accumulative fees, will occur an overflow resetting its value starting from0again. The same may happen withdepositCountand/orwithdrawalCountwhere if an overflow occurs, deposits and/or withdraws will have the same id, ending up with old ones being overridden by new ones. Moreover,tokenWiseDepositValue, andpenaltyFeesTokenBalances[deposit.token]will endup with the same issue breaking bonuses calculation. Consider using SafeMath to prevent possible overflows. Update: The team fixed it on ced8378b1424d99b33c01f2a351902ac7bb3019d but the usage of SafeMath is no longer necessary as Solidity 0.8 comes with default underflow and overflow checks
-
M1 - lines 257 - In the constructor of the contract, the admin is not added to the
hodlAdminsAddressListIndicescreating inconsistencies and different issues when trying to remove admins. See N4 for a way to solve this. Update: The team fixed it on 0b1d477e7fec56db570ca41fefe6345176c534ef by adding an internal function to add admins. -
M2 - lines 274 and 304 - Possible loss of fee collector - If by a human mistake the new fee collector is the ZERO address, then no one will be able to recover it. Some ERC20 contracts prevent transferring to the ZERO address blocking withdrawals from old and further deposits. Following this vector, if the
feeCollectoris the ZERO address and there is only one admin different from the ZERO address, that admin can callremoveAdminusingmsg.senderequal toadminto remove themself from the admin list ending up with no admins and therefore without a way to set new ones. Update: The team fixed it on 0b1d477e7fec56db570ca41fefe6345176c534ef by preventing thenewFeeCollectorto be the ZERO address. -
M3 - line 289 - The same admins can be added more than once. This will create inconsistencies and different issues when trying to remove admins. Consider checking if the admin about to be added was already added. Update: The team fixed it on 0b1d477e7fec56db570ca41fefe6345176c534ef by checking if the admin about to be added is already an admin. Take note that there is a typo in the require message
lreadyinstead ofalready. -
M4 - line 476 - By using flash loans, the user that is withdrawing a deposit can have balance of
SXUTTokenequal or higher thanbonusEligibleSXUTMinBalance. Consider usingbalanceOfAtifSXUTTokensupports balance snapshots or any other strategy to check that the user is a legitSXUTTokenholder. Update: The team fixed it on 0b1d477e7fec56db570ca41fefe6345176c534ef by usingbalanceOfAt. Take note that with just the previous block will work too.
-
L1 - line 280 - The new fee collector is being added as an admin in _
hodlAdmins_but not added tohodlAdminsAddressList. ThefeeCollectoras admin could not be removed. Consider adding the new fee collector to the list too in order to keep the consistency of the contract variables. Plus: see N4 for a better approach. -
L3 - lines 379, 503, and 506 - Lack of support for tokens without a return value - To be compatible with the smart contract, an allowed token should return
trueupon a successful transfer. This is not met in some implementations, like OMG (OmniseGO), BNB (Binance coin), and other not fully ERC20 compliant tokens. Consider using the Open Zeppelin SafeERC20 lib. You can read more about it here: https://decentraland.org/blog/technology/safe-erc20-transfers/. Update: The team fixed it on ced8378b1424d99b33c01f2a351902ac7bb3019d by using using the Open Zeppelin SafeERC20 lib. Consider using it too fordepositToken.transfer(feeCollector, depositFee). -
L3 - line 432 - Inefficient and risky storage update - The deposit is being copied and modified in memory to be "saved" with all of the changes based on the withdrawal's parameters at the end of the
withdrawDepositfunction. If for any reason reentrancy occurs, the value of the deposit will be always the same until it gets saved at the end allowing an attacker to withdraw more funds than the allowed one. As this is a function where funds are transferred out of the contract based on the named variables' values, consider changingmemorytostorageto keep the deposit updated in the contract storage at every property change and also adding a reentrancy guard to avoid any further violation. In the current implementation, the only calls are toSXUTTokenandtokenwhich makes possible a reentrancy attack by those contracts. Update: The team fixed it on ced8378b1424d99b33c01f2a351902ac7bb3019d by adding a reentrancy guard plus changingmemorytostorage. Note thatdeposits[depositId] = depositis no longer necessary. -
L4 - line 458 - Difference fees computed -
deposit.withdrawalFeeis being update withfeebut then if it is an early withdrawal, thewithdrawalFeeis being updated withfee - poolFee. This is creating a difference between what is stored vs what is transferred to thefeeCollector.
-
N1 - line 240 - Hardcoded Token address - If the contract needs to be deployed in another chain and the
SXUTTokenhas a different address in that chain, the contract code must be changed. Consider initializingSXUTTokenin the contract constructor. -
N2 - line 249 and 250 - Unused events -
depositPartiallyWithdarwnanddepositWithdarwnare defined but not used. Consider removing them from the code. Update: The team started using them on ced8378b1424d99b33c01f2a351902ac7bb3019d butdepositEarlyWithdarwnis never going to be called because it has the same if logic asdepositPartiallyWithdarwn -
N3 - lines 254 and 255 - Obsolete initialization - Every
uintis initialized with0, it is the default value, when the contract is deployed. Update: The team fixed it on ced8378b1424d99b33c01f2a351902ac7bb3019d by removing the initializations. -
N4 - lines 257 and 258 - The contract creator is being added as an admin duplicating code that is at
addAdmin. Consider using aaddAdmininternal version removing the check about the sender being a current admin to avoid code duplication and human mistakes by keeping only one source of truth. Update: The team fixed it on 0b1d477e7fec56db570ca41fefe6345176c534ef. -
N5 - lines 264, 571, 580 - Obsolete variable - Storage variables can be returned directly instead of assign and return new variables. Update: The team fixed it on ced8378b1424d99b33c01f2a351902ac7bb3019d
-
N6 - lines 324, 333 - Obsolete and unused return variable:
hodlAdminsList,isAdmin. Update: The team fixed it on ced8378b1424d99b33c01f2a351902ac7bb3019d -
N7 - lines 338, 390, 722, 723, 747, 748 - TODO in the code. Update: The team fixed it on ced8378b1424d99b33c01f2a351902ac7bb3019d
-
N8 - lines 346, 434, 470, 471, 474, 475 - Commented code. Update: The team fixed it on ced8378b1424d99b33c01f2a351902ac7bb3019d but the comment in the line 346, now 212, not.
-
N9 - line 359 - Obsolete variable -
getBonusEligibleSXUTBalancecan just returnbonusEligibleSXUTMinBalancewithout the need of assigning a new variable. -
N10 - line 394 - Forced owner - The owner of the deposit is
msg.sender. Users may need to set a different address as the owner/manager of it. Consider adding a beneficiary parameter who acts as the owner of the deposit. -
N11 - line 402 - Period was already cast in line 388. Consider using it to reduce gas consumption.
-
N12 - line 408 - Possible overflow - Even as it is unlikely to happen, if a
tokenWiseDepositCountreaches the maximumuintpossible, will occur an overflow resetting its value starting from0again. Consider using SafeMath to prevent possible overflows. Update: The team fixed it on 0b1d477e7fec56db570ca41fefe6345176c534ef -
N13 - line 443 - Obsolete variable assigment.
amounthas alreadyamountvalue. -
N14 - line 452, 458, 463, 464, 465, 479, 505, 506, 731, 735, 756, 760 - Missing the usage of SafeMath. Update: The team fixed it on 0b1d477e7fec56db570ca41fefe6345176c534ef but the line 452 (now 322), 463 (now 333), 464 (now 334), 479 (now 346), 505 (now 371), 506 (now 372), 731 (now 607), 735 (now 607), 756 (now 626), 760 (now 630) not. The contract is using Solidity 0.8. Therefore, the usage of SafeMath is not necessary.
-
N15 - lines 518, 529, 540, 551, and 562 - Obsolete is-admin check -
msg.sendercan be impersonated oneth_call's. Moreover, everything in the blockchain is public. Consider usingprivatevariables only when you are considering for example contract inheritance. -
N16 - line 647 - Wrong dev notation - The method is to get the details of a withdrawal, not a deposit.
-
N17 - line 704 - typo:
percenatge.
- Tests: Consider adding tests for every function. Unit and integration tests help to find bugs and make the contract more solid.