Platform: Code4rena
Start Date: 10/03/2022
Pot Size: $75,000 USDT
Total HM: 25
Participants: 54
Period: 7 days
Judge: pauliax
Total Solo HM: 10
Id: 97
League: ETH
Rank: 10/54
Findings: 5
Award: $2,033.26
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: cmichel
Also found by: PPrieditis, kyliek, pedroais
Liquidity provider will be unable to withdraw tokens that were previously supported
In liquidityProviders.sol users can provide liquidity only in supported tokens. This is checked when the user provides liquidity and checked again when he withdraws the liquidity.
If a user provides liquidity in a supported token and the token is then removed from the list he won't be able to withdraw the funds.
Supported tokens should only be checked in deposit and not in withdrawal. If the token was never supported the NFT won't be valid so the withdraw function will revert anyways.
I consider this a medium issue : Assets not at direct risk, but the function of the protocol or its availability could be impacted, or leak value with a hypothetical attack path with stated assumptions, but external requirements.
Only check the token is supported when depositing and not in withdrawals.
#0 - ankurdubey521
2022-03-30T16:15:43Z
Duplicate of #54
#1 - pauliax
2022-04-11T13:08:28Z
#54
99.257 USDT - $99.26
https://github.com/code-423n4/2022-03-biconomy/blob/db8a1fdddd02e8cc209a4c73ffbb3de210e4a81a/contracts/hyphen/LiquidityFarming.sol#L169 https://github.com/code-423n4/2022-03-biconomy/blob/db8a1fdddd02e8cc209a4c73ffbb3de210e4a81a/contracts/hyphen/LiquidityFarming.sol#L289
The owner of the contract can lock user principal in liquidity farming
The setRewardPerSecond function gives the owner the power to change the rewardPerSecond for the stacking without any maximum amount. The owner could setRewardPerSecond to a very high number for any base token.
If the stacker then wants to withdraw their liquidity NFT they are forced to call the updatePool function. This function calls getUpdatedAccTokenPerShare which computes an accumulator variable by adding the different reward amounts multiplied by the time in seconds. If rewardPerSecond is high enough the accumulator can be made close to max uint which will make the arithmetic will revert and the user's principal will be locked.
The math to compute the accumulator is unchecked so it won't revert but if the accumulator is made high enough it will revert when multiplied by the precision constant. https://github.com/code-423n4/2022-03-biconomy/blob/db8a1fdddd02e8cc209a4c73ffbb3de210e4a81a/contracts/hyphen/LiquidityFarming.sol#L274
For example, if rewardPerSecond is set to 2**250 and a few seconds later to 0 the accumulator will be in that order of magnitude, and accumulator * ACC_TOKEN_PRECISION will revert the withdraw transaction.
I consider this a medium issue given it's a critical attack but it's only performable by the contract's governance.
Add a maximum amount to reward per second to protect from overflows.
#0 - pauliax
2022-05-03T18:06:24Z
An unbounded loop allows an attacker to lock stacker's principal in liquidity farming
In the liquidityFarming contract, deposits can be made for someone without their permission. This should be fine as deposits can only benefit the receiver. The problem is in the withdraw function which loops through all NFTs locked by the user until it finds the desired one.
An attacker could deposit multiple NFTS with dust amounts (just some wei of base token for each nft) to make the withdrawal loop very big and surpass the block gas limit. The receiver could be locked from withdrawing their true principal.
#0 - ankurdubey521
2022-03-30T09:43:33Z
Duplicate of #24
#1 - pauliax
2022-04-26T11:35:18Z
933.8474 USDT - $933.85
https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityPool.sol#L171 https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityPool.sol#L273
Detailed description of the impact of this finding.
When a user calls the deposit function the reward amount is calculated and an event is emited with amount+reward as the transfer amount. The function checks amount is smaller than the max amount.
An executor then listens to this event and calls sendFundsToUser with rewards + amount as the amount parameter. This function checks amount+reward is smaller than max amount.
This is a problem because the amount transferred may be in the limit but amount + reward could pass the limit and the executor won't be able to send the transaction. The user will lose the funds. Both checks should be made with the reward or without the reward but the checks should be the same for this not to happen.
Step by step : Max transfer is set to 50 for token A Bob transfers 49 tokens, this will pass since 49<50. The reward is calculated in 2 tokens. The executor then calls sendFundsToUser with 52. This transaction will revert and user will lose their tokens.
This value of amount includes rewards but the previous check didn't include rewards: https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityPool.sol#L273
Both checks should be made over the same amount = amount + rewards
#0 - ankurdubey521
2022-03-30T10:45:12Z
We handle this issue by setting a slightly larger limit in the transfer config of each token on the destination chain.
#1 - ankurdubey521
2022-03-30T10:46:34Z
Also duplicate of #142
#2 - pauliax
2022-04-28T20:28:43Z
Even though the sponsor is already aware of and mitigates this issue, it could still be fixed algorithmically to prevent accidental loss of funds. I am leaving this as of medium severity.
🌟 Selected for report: Dravee
Also found by: 0v3rf10w, 0x1f8b, 0xDjango, 0xNazgul, 0xngndev, 0xwags, Cantor_Dust, CertoraInc, IllIllI, Jujic, Kenshin, Kiep, PPrieditis, TerrierLover, Tomio, WatchPug, antonttc, benk10, berndartmueller, bitbopper, csanuragjain, defsec, gzeon, hagrid, hickuphh3, kenta, minhquanym, oyc_109, pedroais, peritoflores, rfa, robee, saian, samruna, sirhashalot, throttle, wuwe1, z3s
61.6265 USDT - $61.63
Variables that are set in the initializer and can't change (there is no method to change them ) should be immutable to save gas : https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityFarming.sol#L26 https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityFarming.sol#L27
Variables that weigh less than 32 bytes should be grouped together to save storage slots (and gas): https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityFarming.sol#L29 (Address is 20 bytes and bool is 1 byte, they should be declared next to each other in the struct to save a slot)
Public functions that are never used inside the SAME contract should be external to save gas : https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityFarming.sol#L169
Unnecessary checks :
Checking approval is useless. If the approval isn't made the safeTransfer will revert anyway:
Same for this one : https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityProviders.sol#L270
If the NFT is already staked then it will be held inside the contract and the transfer will fail so checking this does nothing : https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityFarming.sol#L211
If the contract doesn't have enough balance the transfer will fail and success will be false which will make the transaction revert. This check does nothing : https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityPool.sol#L288
The same for this one if the balance is not enough safeTransfer will revert : https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityPool.sol#L292
Instead of setting = false delete could be used wich will free storage and return some gas : https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/token/TokenManager.sol#L101
#0 - pauliax
2022-05-09T09:37:26Z
1 is not valid and 3 is valid only in some cases.