Platform: Code4rena
Start Date: 03/10/2023
Pot Size: $24,500 USDC
Total HM: 6
Participants: 62
Period: 3 days
Judge: LSDan
Total Solo HM: 3
Id: 288
League: ETH
Rank: 18/62
Findings: 2
Award: $113.58
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: adriro
Also found by: 0x3b, 0xAadi, 0xDING99YA, 0xTheC0der, 0xWaitress, 0xdice91, 100su, 3docSec, BRONZEDISC, BoRonGod, Eurovickk, GKBG, HChang26, IceBear, JP_Courses, MatricksDeCoder, Mike_Bello90, SovaSlava, Topmark, albahaca, cookedcookee, gzeon, hunter_w3b, kutugu, lukejohn, marqymarq10, matrix_0wl, orion, pep7siup, radev_sw, sces60107, taner2344, tpiliposian, wahedtalash77, xAriextz, zpan
78.3912 USDC - $78.39
Number | Issue | Instances |
---|---|---|
NC-1 | Overwriting set values for poolIdx and weekFrom | 1 |
NC-2 | Potential gas issues with setConcRewards | 1 |
NC-3 | Improvement in LiquidityMiningPath#claimConcentratedRewards() | 1 |
NC-4 | Function interruption due to "Already claimed" check | 2 |
NC-5 | DoS in accrueAmbientPositionTimeWeightedLiquidity() | 1 |
NC-6 | Skipped weeks issue in accrueAmbientPositionTimeWeightedLiquidity() | 1 |
NC-7 | Checks absence on lookupPosition | 1 |
NC-8 | Event emitting in claimAmbientRewards() | 1 |
NC-9 | Floating point arithmetic concern | 1 |
poolIdx
and weekFrom
There's no check to prevent overwriting already set values for a particular poolIdx
and weekFrom
combination in the concRewardPerWeek_
mapping. This might lead to unforeseen results and data integrity issues.
Example:
concRewardPerWeek_[1][5] = 20;
has already been set,setConcRewards
with poolIdx = 1
, weekFrom = 5
, and weeklyReward = 10
,concRewardPerWeek_[1][5] = 10;
.
If there's a desire to prevent overwriting already set values, an additional check would need to be added, such as:require(concRewardPerWeek_[poolIdx][weekFrom] == 0, "Reward already set for this week");
This would throw an exception and halt the function if there's already a reward set for the given pool and week.
setConcRewards
The usage of the while
loop without bounds can result in high gas consumption, making transactions expensive or even impossible to process in extreme cases. It also opens up possibilities for malicious actors to grief the contract.
Gas Griefing:
A malicious actor (or even an unintentional user) could call this function with a very large range between weekFrom
and weekTo
, causing the function to execute the while
loop an excessive number of times. This could result in a transaction that consumes a vast amount of gas, potentially causing the transaction to run out of gas or become prohibitively expensive to execute.
If there are no restrictions on who can call this function (the governance check is commented out), it could be used as an attack vector by malicious users to "grief" the contract, by repeatedly calling the function with large ranges to waste the gas of any monitoring or interacting services.
Gas Limit Issues:
If the range between weekFrom
and weekTo
is large enough, it could exceed the block gas limit. This would make it impossible for the transaction to be processed, effectively blocking the operation.
Gas Griefing Explained:
Gas griefing is when someone purposely makes certain actions on the Ethereum network (like using a function in a smart contract) use up more gas, or computing power, than they should. This can cause problems and inconveniences.
Impacts:
In simple words, gas griefing is like someone intentionally causing traffic jams on a highway, making journeys longer and more costly for everyone. It's essential to design roads (or in this case, smart contracts) to prevent these jams and keep everything running smoothly.
Recommendation:
Add an Upper Bound: Limit the difference between weekFrom
and weekTo
to a maximum value, e.g.,
require(weekTo - weekFrom <= MAX_WEEKS_RANGE, "Range too large");
where MAX_WEEKS_RANGE
is a predefined constant that sets a reasonable maximum number of weeks for which rewards can be set in a single transaction.
Re-enable Governance Check: If it's intended for only certain addresses (like a governance mechanism) to call this function, ensure that you uncomment and properly configure the require
check for permissions. This way, you can limit potential abuse by restricting who can set rewards.
LiquidityMiningPath#claimConcentratedRewards()
The direct usage of msg.sender
in this function can be optimized. It's generally good practice to provide flexibility and make functions more versatile by allowing parameters instead.
These checks, though essential for security, might end up stopping the entire function process. An alternative design should be considered, perhaps skipping the loop iteration instead of stopping the whole function.
require( !concLiquidityRewardsClaimed_[poolIdx][posKey][week], "Already claimed" );
require( !ambLiquidityRewardsClaimed_[poolIdx][posKey][week], "Already claimed" );
accrueAmbientPositionTimeWeightedLiquidity()
A potential Denial of Service could occur if the difference between lastAccrued
and block.timestamp
is significantly large, making the function very gas-heavy.
accrueAmbientPositionTimeWeightedLiquidity()
In case this function isn't called for a prolonged period, weeks in between might not be correctly accounted for, potentially resulting in inaccurate reward calculations.
lookupPosition
Assuming always correct data without validation might result in unexpected behaviors. Validation checks should be added.
claimAmbientRewards()
Emitting events is crucial for dApps to monitor and track. Their omission might make interaction and monitoring difficult.
Solidity doesn't handle floating-point numbers natively. It's essential to be cautious when performing divisions to avoid precision loss.
#0 - c4-pre-sort
2023-10-09T17:21:13Z
141345 marked the issue as sufficient quality report
#1 - c4-judge
2023-10-18T23:07:44Z
dmvt marked the issue as grade-a
🌟 Selected for report: 0xweb3boy
Also found by: 0xdice91, Banditx0x, JP_Courses, ZanyBonzy, albahaca, cookedcookee, hunter_w3b, invitedtea, radev_sw, sandy
35.1935 USDC - $35.19
Purpose: Canto introduces a new liquidity mining feature specifically for Ambient Finance.
Implementation: This feature is a sidecar contract integrated into Ambient using their proxy contract pattern.
New Contracts:
About LiquidityMining Sidecar:
Incentive Mechanism:
Rewards:
Implementation Details:
Ambient Overview:
Noteworthy Sidecar Contracts:
LiquidityMining.sol#accrueAmbientPositionTimeWeightedLiquidity()
:Let's assume:
WEEK
= 7 days = 604800 seconds.time
(the starting point from which we are updating) = some arbitrary timestamp representing a Wednesday at noon.block.timestamp
(current Ethereum block's timestamp) = a timestamp representing 9 days later, which is a Friday evening.This means the loop will have to account for a full week from the starting Wednesday to the next Wednesday, and then an additional 2 days up to Friday evening.
Initial Data:
time
= 1,678,688,000 (Wednesday, Noon)block.timestamp
= 1,693,568,000 (Friday, 9 days later)liquidity
= 1000 (just an arbitrary value for this example)First Iteration:
currWeek
= time
rounded down to the last Sunday = 1,676,160,000 (Last Sunday)nextWeek
= currWeek
+ WEEK
= 1,682,960,000 (Next Sunday)dt
= Since nextWeek
(Next Sunday) is before block.timestamp
(Friday, 9 days later), we'll take a full week, i.e., nextWeek - time
= 1,682,960,000 - 1,678,688,000 = 4,272,000 seconds (5 days from Wednesday Noon to Sunday).dt * liquidity
= 4,272,000 * 1000.time
by dt
: time
= 1,678,688,000 + 4,272,000 = 1,682,960,000 (Next Sunday).Second Iteration:
currWeek
= time
(which is now the Next Sunday) = 1,682,960,000.nextWeek
= currWeek
+ WEEK
= 1,689,760,000 (Sunday after the Next Sunday).dt
= Since block.timestamp
(Friday, 9 days later) is before nextWeek
(Sunday after the Next Sunday), we'll take the difference from the current time
(Next Sunday) up to block.timestamp
(Friday, 9 days later) = 1,693,568,000 - 1,682,960,000 = 10,608,000 seconds (2 days from Sunday to Tuesday).dt * liquidity
= 10,608,000 * 1000.time
by dt
: time
= 1,682,960,000 + 10,608,000 = 1,693,568,000 (Friday, 9 days later).At this point, time
is equal to block.timestamp
, so the loop ends.
Results:
claimAmbientRewards()
Function Walkthrough:owner
is 0x1234567890abcdef1234567890abcdef12345678
.poolIdx
is a hypothetical index (hash) 0xabcdef1234567890
.weeksToClaim
array: [100, 101]
(These are hypothetical week numbers).block.timestamp
is 102
.Initialization:
curve
is fetched from curves_
based on poolIdx
.accrueAmbientPositionTimeWeightedLiquidity()
and accrueAmbientGlobalTimeWeightedLiquidity()
are called.posKey
(a unique identifier for the user's position) is generated using encodePosKey()
.Rewards Calculation:
Start loop for each week in weeksToClaim
:
For week = 100
:
timeWeightedWeeklyGlobalAmbLiquidity_
. Let's assume this is 1000
.10
.ambRewardPerWeek_
). Let's assume it's 500
.5
. Add this to rewardsToSend
.For week = 101
:
4
as the reward for this week.rewardsToSend
. Now, rewardsToSend = 9
.Sending Rewards:
rewardsToSend
is greater than 0.9
will be sent to the owner
.The owner would receive 9
as rewards for the weeks [100, 101]
in our hypothetical scenario.
LiquidityMining
and LiquidityMiningPath
contracts?CurveState
Struct.20 hours
#0 - c4-pre-sort
2023-10-09T17:24:21Z
141345 marked the issue as sufficient quality report
#1 - c4-judge
2023-10-19T16:33:02Z
dmvt marked the issue as grade-b