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: 31/84
Findings: 2
Award: $106.29
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xloscar01
Also found by: 0xAadi, 0xpiken, BowTiedOriole, Breeje, Fassi_Security, JohnSmith, Limbooo, SpicyMeatball, Tendency, Topmark, ZanyBonzy, aslanbek, atoko, jesjupyter, matejdb, max10afternoon, n0kto, peanuts, pkqs90, rouhsamad, thank_you, zhaojohnson
80.5583 USDC - $80.56
https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/main/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L216 https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/main/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L270-L277
Reward tokens are distributed incorrectly among LiquidInfrastructureERC20 token holders, potentially resulting in a portion of the reward tokens remaining perpetually locked within the LiquidInfrastructureERC20 contract.
If an account is initially approved as a LiquidInfrastructureERC20 token holder and receives some tokens, but is subsequently revoked as a token holder, this account will still possess tokens while no longer being recognized as an approved holder.
In the initial phase of distribution, the erc20EntitlementPerUnit
is calculated by dividing the total supply of LiquidInfrastructureERC20 tokens, regardless of unapproved holders still holding some tokens.
> uint256 supply = this.totalSupply(); for (uint i = 0; i < distributableERC20s.length; i++) { uint256 balance = IERC20(distributableERC20s[i]).balanceOf( address(this) ); > uint256 entitlement = balance / supply; erc20EntitlementPerUnit.push(entitlement); }
However, during the distribution phase, only approved holders are allocated the reward tokens. This means if there are accounts that, despite holding tokens, are no longer approved as token holders, their entitled share of reward tokens will not be distributed and will instead remain within the LiquidInfrastructureERC20 contract.
for (i = nextDistributionRecipient; i < limit; i++) { address recipient = holders[i]; > if (isApprovedHolder(recipient)) { uint256[] memory receipts = new uint256[]( distributableERC20s.length ); for (uint j = 0; j < distributableERC20s.length; j++) { IERC20 toDistribute = IERC20(distributableERC20s[j]); uint256 entitlement = erc20EntitlementPerUnit[j] * this.balanceOf(recipient); if (toDistribute.transfer(recipient, entitlement)) { receipts[j] = entitlement; } } emit Distribution(recipient, distributableERC20s, receipts); } }
One could argue that the undistributed tokens will be allocated in the next distribution cycle. However, ultimately, there will always be a residual amount of tokens that remain locked in the LiquidInfrastructureERC20 contract. Therefore, this issue is considered to have medium severity.
Manual review.
Keep track of the amount of LiquidInfrastructureERC20 tokens held solely by approved holders. This can be done by subtracting from the total supply when a holder is disapproved and adding it back when a holder is approved.
Token-Transfer
#0 - c4-pre-sort
2024-02-22T05:24:27Z
0xRobocop marked the issue as duplicate of #703
#1 - c4-judge
2024-03-04T14:41:55Z
0xA5DF marked the issue as satisfactory
🌟 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/main/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L224 https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/main/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L220-L227
If the owner modifies distributableERC20s
during the token distribution process, it could disrupt the entire distribution mechanism, leading to the allocation of incorrect amounts of tokens.
In the process of distributing reward tokens, there are two steps:
erc20EntitlementPerUnit
.Step 1:
for (uint i = 0; i < distributableERC20s.length; i++) { uint256 balance = IERC20(distributableERC20s[i]).balanceOf( address(this) ); uint256 entitlement = balance / supply; erc20EntitlementPerUnit.push(entitlement); }
Step 2:
for (uint j = 0; j < distributableERC20s.length; j++) { IERC20 toDistribute = IERC20(distributableERC20s[j]); uint256 entitlement = erc20EntitlementPerUnit[j] * this.balanceOf(recipient); if (toDistribute.transfer(recipient, entitlement)) { receipts[j] = entitlement; } }
However, if distributableERC20s
is modified in the middle of the distribution process, it could fall out of alignment with erc20EntitlementPerUnit
, leading to an incorrect subsequent distribution phase.
distributableERC20s
.Manual review.
Don't allow setting distributableERC20s
during distribution.
function setDistributableERC20s( address[] memory _distributableERC20s ) public onlyOwner { + require(!LockedForDistribution, "distribution in progress"); distributableERC20s = _distributableERC20s; }
Invalid Validation
#0 - c4-pre-sort
2024-02-20T07:13:16Z
0xRobocop marked the issue as duplicate of #260
#1 - c4-judge
2024-03-04T15:30:44Z
0xA5DF marked the issue as satisfactory
#2 - c4-judge
2024-03-08T15:13:03Z
0xA5DF changed the severity to 3 (High Risk)
#3 - c4-judge
2024-03-08T15:26:19Z
0xA5DF changed the severity to 2 (Med Risk)