Missing address(0) checks for immutable state variables passed in constructors
In most of the-standard
contracts, immutable
variables value as argument is passed in constructor. Immutable variables are used as gas saving measure and if it is once set it can not be changed. The value of immutable
variables is permanent in contract. If address(0) is passed in such variables then the contract would need to be redployed in worst case. Its better to prevent such errors by require
validations so that such issues can be prevented.
Instance 1 at L-35 in SmartVaultV3.sol
which can be checked here
It should be changed to,
Instance 2 at L-35 in LiquidationPool.sol
which can be checked here
It should be changed to,
Since immutable state varibales once set in constructor can not be reset, if address(0) is passed as arguments for such variables then contract would be required to re-deployed.
Manual review
Changes already mentioned in Vulnerability Details
section to mitigate such issues.
LiquidationPool.distributeAssets()
will always fail if manager does not approve allowance to LiquidationPool contractLiquidationPool.distributeAssets()
will always fail if manager does not approve allowance to LiquidationPool contract
In LiquidationPool.distributeAssets()
is used to distribute the assets to different holders. distributeAssets()
token transfer logic is given as below,
The tokens are transferred to LiquidationPool
contract from manager address and then these tokens can be claimed by holders by calling claim()
function. The issue here is, the LiquidationPool
contract requires allowance from manager otherwise the asset distribution will always fail.
The function is called in LiquidationPoolManager.sol
contract by LiquidationPoolManager.runLiquidation()
for liquidation run to specific tokenId.
Another issue is that, LiquidationPool.distributeAssets()
can be called by anyone, however this should not be desired behavior.
LiquidationPool.distributeAssets()
will always fail if manager does not approve allowance to LiquidationPool contract and the function can be called by anyone so it should only be called by manager address only i.e LiquidationPoolManager.sol
contract.
Manual review
It is recommended to peform below changes in code,
approve can fail for non standard ERC20 tokens like USDT
Per the discussion with sponsors(@ewan),
In accepted tokens, will USDT and USDC be added in future as these are the most used tokens in crypto??
The response received:
no plans to yet, but it is possible yep
Therefore, all possible issues pertaining to USDC/USDT should be resolved in current code base as these tokens can be a part of accepted token by tokenManager. Therefore, below issue is specifically highlighting the issue with USDT approval failure in current code base.
Some tokens (like USDT) do not work when changing the allowance from an existing non-zero allowance value. For example Tether (USDT)’s approve() function will revert if the current approval is not zero, to protect against front-running changes of approvals. Link to usdt contract reference(SLOC 199-209)
approve is actually vulnerable to a sandwich attack as explained in the following document and this check for allowance doesn't actually avoid it.
Reference document link- https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit
In ERC20, front running attack is possible via approve() function,
Reference link for better understanding- https://blog.smartdec.net/erc20-approve-issue-in-simple-words-a41aaf47bca6
In the protocol, all functions using approve() must be first approved by zero. In LiquidationPoolManager.sol
, runLiquidation() approves the ERC20 tokens i.e accepted tokens by tokenManager to pool contract.
approve will fail in case of USDT due to not approving 0 first.
Manual review
Use OpenZeppelin’s SafeERC20 i.e forceApprove()
or depending on openzeppelin version used etc, Alternatively, approve 0 first in case of USDT.
swap() should be payable to handle the ether transfer
In SmartVaultV3.swap()
is used to swap between two tokens. The swap fee is handled in eth or erc20 tokens.
Now, look into executeNativeSwapAndFee(params, swapFee)
The swap function would revert due to failure of swap fee in eth as the low level call function is expecting the value
param in eth. Therefore the function should be payable to handle the swap fee in eth.
ether transfer would fail or revert if swap() does not have payable modifier
Manual review
Add payable keyword on swap()
claimRewards()
would fail if the msg.sender is blacklisted in case of USDC/USDTclaim()
would fail if the msg.sender is blacklisted in case of USDC/USDT
Per the discussion with sponsors(@ewan),
In accepted tokens, will USDT and USDC be added in future as these are the most used tokens in crypto??
The response received:
no plans to yet, but it is possible yep
Therefore, issues pertaining to USDC/USDT should be accepted here. Below issue is highlighted if the claimer address is blacklisted by USDC/USDT.
USDC tokens have a blacklist() function which is used to blacklist any address by USDC admin. This can be checked here
Similarly, USDT tokens have a addToBlockedList() function which is used to blacklist any address by USDT admin. This can be checked here
It must be noted that USDC and USDT are widely used tokens in the crypto market and the potential accepted tokens as confirmed by sponsor discord message.
claim() function transfers the
Consider a scenario,
User wants to claim rewards and he calls LiquidationPool.claimRewards()
The amount of rewards pertaining to user is fetched and it is transfered to msg.sender
by transfer() function.
The isssue arises if the user address i,e msg.sender is found to be blacklisted by USDC/USDT then the claimRewards() will always revert.
USDC/USDT contracts always check whether the calling address i.e msg.sender is blacklisted or not.
If this scenario happens which is very much likely the user won't be able to convert his rewards. It will break the one of the important functionality of contract.
claimRewards will fail and the rewards of users will be permanantly locked in contract if the claimer address is blacklisted
Manual review
Recommend to allow the recipient address as function argument and allow user to transfer the rewards tokens to his desired address.
For example:
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.