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: 34/59
Findings: 2
Award: $260.24
🌟 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
194.8188 USDC - $194.82
We found multiple functions which were missing zero address checks. It is advised to add a zero address check at all possible functions setting an address. In solidity, any error caused to set the value to default, and for an address variable, the default value is zero address. Hence any fund or privilege gets pointed to zero address, and it will be unrecoverable. The contract was found to be using floating pragma, which allows the contract to be compiled to multiple versions and hence can cause inconsistencies or errors on different versions. The contract was found to be using a calldata for analytics purposes and did not validate the data. A malicious user could abuse this to pollute the analytics logs with irrelevant data. Another issue was related to an admin function that does not check the array length. In a smart contract, it is always advised to have an array length check when performing a loop operation. We also found some non-critical findings like lack of input validation in amount during the withdraw function. It is recommended to allow withdrawal of greater than 0 value.
Use of Floating Pragma Version
The contract was found to be using a floating pragma which is not considered safe as it can be compiled with all the versions described.
Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/AnyswapFacet.sol#L2) Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/CBridgeFacet.sol#L2) Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/DexManagerFacet.sol#L2) Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/DiamondCutFacet.sol#L2) Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/DiamondLoupeFacet.sol#L2) Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/GenericSwapFacet.sol#L2) Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/HopFacet.sol#L2) Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/NXTPFacet.sol#L2) Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/OwnershipFacet.sol#L2) Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/Swapper.sol#L2) Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/WithdrawFacet.sol#L2) Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/LiFiDiamond.sol#L2) Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Libraries/LibAsset.sol#L2) Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Libraries/LibDiamond.sol#L2) Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Libraries/LibStorage.sol#L2) Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Libraries/LibSwap.sol#L2) Pragma version^0.8.7 (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Libraries/LibUtil.sol#L2)
Use strict pragma version like
Pragma version 0.8.7
Multiple functions Lacking Zero address checks
Address type parameters should include a zero-address check; otherwise, contract functionality may become inaccessible, or tokens burned forever.
Tokens may become inaccessible or burnt forever without a zero-address check.
Below is the list of functions lacking zero address checks
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/AnyswapFacet.sol#L35-L66
Function startBridgeTokensViaAnyswap
has address router
& recipient
that is missing zero address checks
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/CBridgeFacet.sol#L42-L48
Function initCbridge
has address input _cBridge
, which is lacking zero address checks.
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/CBridgeFacet.sol#L57-L84
Function startBridgeTokensViaCBridge
has address input receiver
, which is lacking zero address checks.
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/DexManagerFacet.sol#L17-L26
Function addDex
has address input _dex
, which is lacking zero address checks.
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/DexManagerFacet.sol#L30-L40
Function batchAddDex
has address input _dexs
, which is lacking zero address checks.
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/DiamondCutFacet.sol#L14-L21
Function diamondCut
has address input _init
, which is lacking zero address checks.
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/HopFacet.sol#L61-L87
Function startBridgeTokensViaHop
has address input recipient
, which is lacking zero address checks.
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/NXTPFacet.sol#L124-L140
Function completeBridgeTokensViaNXTP
has address input assetId
, and receiver
which is lacking zero address checks.
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/NXTPFacet.sol#L150-L171
Function swapAndCompleteBridgeTokensViaNXTP
has address input _cBridge
and receiver
which is lacking zero address checks.
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/OwnershipFacet.sol#L8-L11
Function transferOwnership
has address input _newOwner
, which is lacking zero address checks.
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/CBridgeFacet.sol#L42-L48
Address zero address check to all the missing places.
Event pollution
The values inside _lifiData
are not being validated in any of the functions. This data is being used for tracking, analytics, and monitoring. The functions in which the lifiData are being used are external or public. These functions can be called by external users concurrently, due to which the event logs will be polluted if a malicious actor enters random data inside the _lifiData.
A malicious actor can pollute event logs by concurrently calling the function and emitting random events.
This affects all the contract using the user supplied calldata input _lifiData
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/GenericSwapFacet.sol#L22-L44
Validate the calldata value against the value actually processed rather than any user-supplied value.
No limit on the input array length
Ethereum is a very resource-constrained environment. Prices per computational step are orders of magnitude higher than with centralized providers. Moreover, Ethereum miners impose a limit on the total number of gas consumed in a block. If array.length is large enough, the function exceeds the block gas limit, and transactions calling it will never be confirmed.
This becomes a security issue, if an external actor influences array.length.
E.g., if array enumerates all registered addresses, an adversary can register many addresses, causing the problem described above.
Go to https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/DexManagerFacet.sol#L30-L40 we will notice there is no length validation for array _dexs
Either explicitly or just due to normal operation, the number of iterations in a loop can grow beyond the block gas limit which can cause the complete contract to be stalled at a certain point. Therefore, loops with big or unknown number of steps should always be avoided.
LibDiamond.enforceIsContractOwner() should be called before in initNXTP function
The initNXTP
at https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/NXTPFacet.sol#L33-L37 is an admin called function. However Storage storage s = getStorage();
is called before the admin function which would be usless if the caller is not an admin.
Notice Storage storage s = getStorage();
being called before LibDiamond.enforceIsContractOwner();
https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/NXTPFacet.sol#L33-L37
LibDiamond.enforceIsContractOwner();
can be called before to avoid unessary execution of Storage storage s = getStorage();
Missing input validation in amount
The contract does not check if the value of amount is greater than 0.
Go to https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/WithdrawFacet.sol#L23 we will notice there is no input validation for _amount
Add a require statement to check if _amount
greater than 0
🌟 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
65.4185 USDC - $65.42
The contract was found to be using struct variables in random order. Since structs use packing and assign storage slots accordingly, lower to higher-order will save gas as lower variables get packed.
We found many functions were declared as public but were never internally used. Such functions can always be marked as external
instead of public
as external
functions consume less gas than public
functions.
We also noticed the contract uses power operations at a few places instead of using the exponential form directly, which would save gas.
Structs can be rearranged from lower to higher to save gas.
If the variable types in the struct are arranged in a lower to a higher order, the gas used is lesser than when they are arranged in any other order. The main goal here is the reduction of gas requirements when deploying these smart contracts, and saving costs in each place will eventually add up. The consequences of the use of the Tight Variable Packing pattern have to be evaluated before implementing it blindly. The big benefit comes from the substantial amount of gas that can potentially be saved over the lifetime of a contract.
At line (https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/CBridgeFacet.sol#L26-L33) We will notice that the structs are ordered as below
address receiver; address token; uint256 amount; uint64 dstChainId; uint64 nonce; uint32 maxSlippage;
This will consume 370235 gas
However the struct is arranged in lower to higher order. The lower ones will get packed in a single slot and hence it will save gas.
uint32 maxSlippage; uint64 dstChainId; uint64 nonce; uint256 amount; address receiver; address token;
The above arrangement will consume 370199 gas
Arrange the struct variable from lower to higher order.
Functions that can be external instead of public
Public functions that are never called by a contract should be declared external in order to conserve gas.
Smart Contracts are required to have effective Gas usage as they cost real money, and each function should be monitored for the amount of gas it costs to make it gas efficient.
Public
functions cost more Gas than external
functions.
The following functions can be declared external:
setRewardForwarding
(https://github.com/code-423n4/2022-02-redacted-cartel/blob/main/contracts/TokemakBribe.sol#L298-L302)swapAndStartBridgeTokensViaAnyswap
(https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/AnyswapFacet.sol#L74-L123)startBridgeTokensViaCBridge
(https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/CBridgeFacet.sol#L57-L84)swapAndStartBridgeTokensViaCBridge
(https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/CBridgeFacet.sol#L92-L134)swapTokensGeneric
(https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/GenericSwapFacet.sol#L22-L44)startBridgeTokensViaHop
(https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/HopFacet.sol#L61-L87)swapAndStartBridgeTokensViaHop
(https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/HopFacet.sol#L95-L126)startBridgeTokensViaNXTP
(https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/NXTPFacet.sol#L46-L76)swapAndStartBridgeTokensViaNXTP
(https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/NXTPFacet.sol#L85-L115)completeBridgeTokensViaNXTP
(https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/NXTPFacet.sol#L124-L140)swapAndCompleteBridgeTokensViaNXTP
(https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/NXTPFacet.sol#L150-L171)withdraw
(https://github.com/code-423n4/2022-03-lifinance/blob/main/src/Facets/WithdrawFacet.sol#L20-L38)Use the external
state visibility for functions that are never called from the contract.
Use of power operations instead of their exponential form
It is unnecessary to perform power operations when a number can be directly represented in an exponential form as it will save gas.
Use exponential form instead of power operations to save gas.
#0 - H3xept
2022-04-04T08:16:46Z
Fixed in lifinance/lifi-contracts@26443af0142afdb131b6b3ab278fac29670b7b0e
#1 - H3xept
2022-04-04T08:18:34Z
This won't compile as the intermediate result 2e256 does not fin in uint256.
#2 - H3xept
2022-04-08T15:06:03Z
Duplicate of #197