Platform: Code4rena
Start Date: 13/02/2024
Pot Size: $24,500 USDC
Total HM: 5
Participants: 84
Period: 6 days
Judge: 0xA5DF
Id: 331
League: ETH
Rank: 19/84
Findings: 2
Award: $267.84
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: nuthan2x
Also found by: 0x0bserver, AM, CaeraDenoir, DanielArmstrong, JrNet, Kirkeelee, KmanOfficial, Krace, Limbooo, Meera, SovaSlava, SpicyMeatball, TheSavageTeddy, agadzhalov, aslanbek, atoko, csanuragjain, d3e4, imare, jesjupyter, juancito, kartik_giri_47538, kutugu, max10afternoon, offside0011, pkqs90, turvy_fuzz, xchen1130, zhaojohnson, ziyou-
25.7286 USDC - $25.73
https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L198-L237 https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L257-L281 https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L441-L445
This is NOT a "Centralization Risk"
Here, the protocol can reach an invalid state, breaking accounting and the invariant of the distributableERC20s
array being synchronized with the erc20EntitlementPerUnit
array
Accounting being broken means mis-distribution of funds
When initially calling LiquidInfrastructureERC20.distribute(), there's a call to _beginDistribution which builds the erc20EntitlementPerUnit
array depending on the distributableERC20s
array LiquidInfrastructureERC20.sol#L271-L277
While a LockedForDistribution
variable is indeed set to true
during this distribution (LiquidInfrastructureERC20.sol#L262), there are no checks preventing a call to setDistributableERC20s():
File: LiquidInfrastructureERC20.sol 441: function setDistributableERC20s( 442: address[] memory _distributableERC20s 443: ) public onlyOwner { 444: distributableERC20s = _distributableERC20s; 445: }
This means that, when resuming the distribution, the erc20EntitlementPerUnit
may mismatch the distributableERC20s
, which may lead to mismanaging funds (especially since the balance of the distributableERC20s
is so important (LiquidInfrastructureERC20.sol#L272-L274) and can be mismatched in terms of decimals (like sending way too much USDC, being 6 decimals, by it replacing in the array the index of a 18 decimals ERC20 token) making entitlement potentially way too big (LiquidInfrastructureERC20.sol#L275) or even if it happened to be from the same size: the balances from the holders may mismatch witch would distribute funds wrongly.
Manual review
setDistributableERC20s
on LockedForDistribution
LockedForDistribution
to false
in setDistributableERC20s
so that _beginDistribution gets called again which would delete erc20EntitlementPerUnit and make the distribution start again from a clean slateInvalid Validation
#0 - c4-pre-sort
2024-02-20T04:25:40Z
0xRobocop marked the issue as duplicate of #151
#1 - c4-pre-sort
2024-02-20T04:38:28Z
0xRobocop marked the issue as duplicate of #260
#2 - c4-judge
2024-03-04T15:13:08Z
0xA5DF marked the issue as satisfactory
#3 - c4-judge
2024-03-08T15:26:19Z
0xA5DF changed the severity to 2 (Med Risk)
🌟 Selected for report: SpicyMeatball
Also found by: BowTiedOriole, Breeje, CaeraDenoir, JohnSmith, Krace, Meera, PumpkingWok, SovaSlava, SpicyMeatball, d3e4, juancito, kutugu, nuthan2x, rokinot, rouhsamad, web3pwn
242.1143 USDC - $242.11
https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L413 https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L382-L383 https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L380 https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L362-L364 https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L359
DOS on withdrawFromManagedNFTs
which is a funds leaving the protocol
type of function
=> loss of funds
On the first call to withdrawFromManagedNFTs
, nextWithdrawal
is equal to 0, which makes the WithdrawalStarted
event being emitted (LiquidInfrastructureERC20.sol#L362-L364)
After each call, The nextWithdrawal
gets incremented depending on the nextWithdrawal + numWithdrawals
value or the ManagedNFTs.length
depending on which of these is smaller.
If we take a scenario with those starting parameters:
ManagedNFTs.length == 10 numWithdrawals == 9 nextWithdrawal == 0
Then the final nextWithdrawal
will be equal to 9
here
However, the calls to LiquidInfrastructureERC20.releaseManagedNFT
aren't protected against the withdrawFromManagedNFTs
being ongoing. By definition, this function Transfers a LiquidInfrastructureNFT contract out of the control of this contract
It means that, by releasing 2 ManagedNFTs
through LiquidInfrastructureERC20.releaseManagedNFT
, we'll have the following state:
ManagedNFTs.length == 8 nextWithdrawal == 9
Which will effectively make it impossible to reset nextWithdrawal
to 0 again by the protocol's means (LiquidInfrastructureERC20.sol#L382-L383)
Why will it be impossible by the protocol itself? For 2 reasons:
Notice here that it'll be impossible to add the released NFTs again as their owner would not be LiquidInfrastructureERC20
anymore.
Notice also that there aren't any public mint()
or safeMint()
methods on LiquidInfrastructureNFT
: therefore it won't be possible to just mint other NFTs. It'll be required for the protocol to request to the released NFT's owner to send it back to the protocol.
Manual review
releaseManagedNFT
on nextWithdrawal > 0
File: LiquidInfrastructureERC20.sol - 382: if (nextWithdrawal == ManagedNFTs.length) { + 382: if (nextWithdrawal >= ManagedNFTs.length) { 383: nextWithdrawal = 0;
DoS
#0 - c4-pre-sort
2024-02-21T04:41:22Z
0xRobocop marked the issue as duplicate of #130
#1 - c4-judge
2024-03-03T12:58:52Z
0xA5DF marked the issue as satisfactory
#2 - c4-judge
2024-03-03T13:06:11Z
0xA5DF changed the severity to 2 (Med Risk)