Platform: Code4rena
Start Date: 24/03/2022
Pot Size: $75,000 USDC
Total HM: 15
Participants: 59
Period: 7 days
Judge: gzeon
Id: 103
League: ETH
Rank: 43/59
Findings: 2
Award: $177.18
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: hake
Also found by: 0v3rf10w, 0xDjango, 0xkatana, BouSalman, CertoraInc, Dravee, Hawkeye, IllIllI, JMukesh, Jujic, Kenshin, PPrieditis, Picodes, PranavG, Ruhum, SolidityScan, VAD37, WatchPug, aga7hokakological, catchup, csanuragjain, cthulhu_cult, defsec, dimitri, hickuphh3, hubble, hyh, kenta, kirk-baird, obront, peritoflores, rayn, robee, saian, samruna, shenwilly, shw, sorrynotsorry, tchkvsky, teryanarmen, ych18
114.1846 USDC - $114.18
The _executeSwaps function requires the swapData.approveTo and swapData.callTo to be approved in the dexWhitelist mapping. But there are several fields of the SwapData struct that are not whitelisted:
The riskiest of these non-whitelisted values is callData, which allows any function of the whitelisted callTo contracts to be called. Even if the intent is for a user to only call one or two different swap functions with the DEX, the lack of limitations on the callData value allows any function to be called. Issues can even be caused if the whitelisted DEX address is a proxy address and the DEX adds new functions that cause LiFi to become insecure.
Only swapData.approveTo and swapData.callTo are validated in _executeSwaps https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/Swapper.sol#L16
_executeSwaps is an internal function, but the swapTokensGeneric function is a public entrypoint which permits any _swapData value https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/GenericSwapFacet.sol#L22
Manual analysis
The design of GenericSwapFacet is very open ended today, but it should be more restricted. Because the purpose of this contract is to perform swaps with specific whitelisted DEXes, a whitelist preventing calls to other DEX functions should be added. This whitelist for callData should be checked at the same time as approveTo and callTo.
The diamond proxy EIP-2535 documentation states the dangers of adding/replacing functions using the diamondCut function. The documentation suggests limiting who, when, and the transparency of changes. The only limit currently in place is limiting who can use diamondCut to the contract owner, but a rogue contract owner could quickly rug users of LiFi because there are no checks on the owner's privileges.
The EIP2535 documentation explaining these risks https://eips.ethereum.org/EIPS/eip-2535#security-of-diamond-storage
The only check in place to prevent arbitrary changes to diamond storage is to permit only the contract owner to call diamondCut. There are no protections for when these changes can take place, no timelock to prevent surprise changes without notice, and no checks and balances for the owner's power. These are centralization risks and can cause problems for the users of the protocol if the owner is either compromised or acting maliciously. https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/DiamondCutFacet.sol#L19
Manual analysis
Apply recommendations from EIP2535 to limit when diamondCut changes can occur. Make it clear to users what the purpose of this function is, when upgrades will happen, and add a timelock if this could increase user trust that no sudden changes will happen.
The approveERC20 function in LibAsset sets an infinite allowance for each token/bridge pair that it is called on. Doing this increases the risk of LiFi token holdings, because a security vulnerability in a trusted bridge could drain the tokens with infinite allowance from LiFi.
The approveERC20 function sets the allowance to MAX_INT for every token that is sent using AnySwap, Hop, NXTP, or CBridge https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Libraries/LibAsset.sol#L68
This implies that these bridges are completely trusted by LiFi, and if any of these bridges has a security issue, LiFi may be vulnerable because of this allowance setting.
Manual analysis
Set the allowance to the token value sent in this transaction. It will use more gas but will increase the security of the protocol.
#0 - H3xept
2022-04-11T11:30:23Z
Duplicate of #108
🌟 Selected for report: Dravee
Also found by: 0v3rf10w, 0xDjango, 0xNazgul, 0xkatana, ACai, CertoraInc, FSchmoede, Funen, Hawkeye, IllIllI, Jujic, Kenshin, PPrieditis, Picodes, SolidityScan, TerrierLover, Tomio, WatchPug, catchup, csanuragjain, defsec, dimitri, hake, hickuphh3, kenta, minhquanym, obront, peritoflores, rayn, rfa, robee, saian, samruna, tchkvsky, teryanarmen, ych18
63.0043 USDC - $63.00
Strings in solidity are handled in 32 byte chunks. A require string longer than 32 bytes uses more gas. Shortening these strings will save gas.
Several cases of this gas optimization were found. These are a few examples, but more may exist
Manual analysis
Shorten require strings
There are two asserts in WithdrawFacet that are not necessary. The transfer that happens immediately after the assert will revert if there is insufficient balance, so the assert does not provide any value.
The redundant assert calls are found at WithdrawFacet https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/WithdrawFacet.sol#L30 https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/WithdrawFacet.sol#L34
Manual analysis
Remove the two asserts to save gas
Declaring a function as external instead of public saves gas
Some functions can be declared external instead of public
Slither
Change function from public to external to save gas
#0 - H3xept
2022-04-08T15:07:58Z
Duplicate of #197
#1 - H3xept
2022-04-11T10:28:41Z
Duplicate of #100