Issue ID | Description |
---|---|
L-01 | Hardcoded scrvUSD address prevents multi-chain deployment |
L-02 | Attacker can easily sandwich via raw_price() due to lack of smoothening |
L-03 | Incorrect storage slot mappings lead to extraction of wrong parameters |
L-04 | Unbounded loop in price parameter calculation enables DoS |
L-05 | Conservative oracle price change limit enhances protocol security beyond documentation |
L-06 | APY limit exceeds documented threshold leading to potential protocol mispricing |
Take a look at ScrvusdVerifierV1::SCRVUSD
The ScrvusdVerifierV1 contract hardcodes the scrvUSD token address, which is used for extracting parameters from Ethereum state proofs. The README indicates the protocol is designed to be compatible with multiple EVM blockchains, but this hardcoded address will only be valid on the chain where it was initially deployed.
This hardcoded address prevents the protocol from being deployed on multiple chains as intended. On any chain other than the one where 0x0655977FEb2f289A4aB78af67BAB0d17aAb84367 represents the scrvUSD token, the verifier will:
Extract data from an unrelated contract (if the address exists)
Fail with "scrvUSD account does not exist" (if the address doesn't exist)
Use incorrect parameters to update the oracle price
Either scenario compromises the entire oracle system on non-primary chains, breaking cross-chain compatibility.
Replace the hardcoded constant with a constructor parameter:
raw_price()
due to lack of smootheningProtocol includes heavy documentation both in the core docs and in the walkthrough video of the protocol to indicate that a smoothening of price logic is need so as to close up the vector of sandwiching post an update, issue however is that where as we correctly apply that logic to the three _price_v0
, _price_v1
, and _price_v2
functions, we do not do that to the main entry querieer point of prices, i.e raw_price()
, cause via it we just calculate the price directly without smoothening allowing attackers to sandwich price updates || arbitrage opportunities and economic attacks.
First take a look at ScrvusdOracleV2::raw_price()
Evidently, there are no smoothening logic applied. In contrast, the other price getter functions in the contract use a smoothening mechanism to prevent price manipulation:
As hinted under Summary, the protocol documentation and walkthrough video clearly indicate that price smoothening is necessary to prevent sandwiching attacks. The issue is that while this protection is implemented for the _price_v0
, _price_v1
, and _price_v2
functions, it is explicitly omitted from the raw_price
function as indicated by the comment "without smoothening" in the function's docstring.
This inconsistency creates a vulnerability where the raw_price
function can be manipulated via sandwiching attacks, even though the other price functions are protected, whcih goes against:
https://docs.curve.fi/scrvusd/crosschain/oracle-v0/oracle/#oracle-acceleration
Oracle Acceleration¶
Because the rates are stored over time, the price can change suddenly and can lead to sandwich attacks. To prevent this, the max_acceleration parameter is used to limit the rate of price updates.
The security on blocking attackers from sandwiching price update transactions is broken, creating immediate arbitrage opportunities, where we can argue that the difference between smoothened and unsmoothed prices would be exploited for profit, since in real sence the value of raw_price() and price_v2() should be very close.
Manual review
Apply the same smoothening logic to the raw_price
.
Take a look at ScrvusdVerifierV1::PARAM_SLOTS
The verifier contract defines hardcoded storage slot indices that don't match the actual storage layout of the scrvUSD contract at 0x0655977FEb2f289A4aB78af67BAB0d17aAb84367. After examining the deployed contract on Etherscan, there is a significant mismatch between these slot numbers and the actual storage layout.
This mismatch causes the verifier to extract incorrect data from storage proofs, leading to:
Updating the oracle with completely incorrect parameter values
Price calculations based on wrong data
Potential manipulation or corruption of the oracle price feed
System instability due to unexpected parameter values
This effectively breaks the entire price verification mechanism, rendering the oracle unreliable for all systems that depend on it, including StableSwap pools and other integrations.
Perform a thorough audit of the scrvUSD contract's actual storage layout
Update the storage slot indices to match the verified contract:
Alternatively, implement a more robust approach by fetching the storage layout dynamically or creating a configuration mechanism that allows updating these slot indices if the contract layout changes.
The loop could iterate up to MAX_V2_DURATION times (192 iterations), potentially causing gas issues.
Potential denial of service through gas exhaustion and failed price updates during high gas prices
Manual review
Implement a more efficient calculation method that doesn't require iteration
Add an additional bound on the number of iterations
Consider using a different mathematical approach for calculating accumulated values
Take a look at ScrvusdOracleV2::init()
While the README documentation states a safe threshold of 0.5 bps per block:
The actual implementation limits price changes to 0.24 bps per block, which is significantly more conservative than the documented 0.5 bps threshold.
This discrepancy between documentation and implementation is actually positive for security. The more restrictive limit of 0.24 bps per block (vs the documented 0.5 bps) means the oracle price changes even more gradually than described, providing enhanced protection against price manipulation and arbitrage attacks.
The conservative implementation gives StableSwap pools using this oracle additional safety margin, as the price can change at less than half the rate that was determined to be safe in the documentation.
Update the README documentation to match the actual implementation of 0.24 bps per block to ensure accurate documentation of the security properties:
Take a look at ScrvusdOracleV2::init()
The README documentation explicitly states that "scrvUSD will never be over 60% APR":
However, the code comment acknowledges that the current configuration allows for up to 63% APY, exceeding the documented limit by 3%.
This discrepancy means the oracle can allow price growth beyond what's documented as the maximum expected rate. While a 3% difference might seem minor, in DeFi protocols handling significant amounts of value, this can lead to:
Higher than expected price movements
Potential mispricing in pools and derivatives using this oracle
Incorrect risk assessments by protocols integrating with scrvUSD based on documented limits
The actual implementation allows for faster price growth than what was communicated, which could affect risk calculations and pricing models of dependent systems.
Either adjust the code to match the documented 60% APR limit by reducing the max_price_increment
value or update the documentation to accurately reflect the 63% APY limit in the implementation:
Alternatively, modify the README to state:
Ensuring consistency between documented limitations and actual implementation is crucial for proper risk assessment by integrators.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.