Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: low
Valid

Issues when handling tokens in `fulfillSwap()` function

Vulnerability Details

In StabilityBranch#fulfillSwap()function, it use Chainlink's Data Stream to verify price:

//@note https://docs.chain.link/data-streams/reference/interfaces
function verifyOffchainPrice(Data storage self, bytes memory priceData) internal returns (UD60x18 priceX18) {
bytes memory reportData = ChainlinkUtil.getReportData(priceData);
IVerifierProxy chainlinkVerifier = self.chainlinkVerifier;
(FeeAsset memory fee) = ChainlinkUtil.getEthVericationFee(chainlinkVerifier, reportData);
bytes memory verifiedPricetData = ChainlinkUtil.verifyReport(chainlinkVerifier, fee, priceData);
PremiumReport memory premiumReport = abi.decode(verifiedPricetData, (PremiumReport));
if (block.timestamp > premiumReport.validFromTimestamp + self.maxVerificationDelay) {
revert Errors.DataStreamReportExpired();
}
priceX18 = ud60x18(int256(premiumReport.price).toUint256());
}

From scope, these tokens are supported:

- ETH
- WETH
- WEETH
- WSTETH
- WBTC
- USDC
- USDT
- USDE
- SUSDE

But there are 2 problems at here:

  • SUSDE and WEETH token do not exist in data stream, lead to unable to verify price of these tokens. WBTC do not have also, but we should not assume WBTC = BTC, because WBTC has depegged down to 0.98 before

  • Assuming all price returned have 18 decimals:

    // get price from report in 18 dec
    ctx.priceX18 = stabilityConfiguration.verifyOffchainPrice(priceData);

Despite with some tokens in scope that already have data stream feeds, its deciamls is 18 already, but with token mentioned above, its price can be 8 decimals when data stream's feed created because price decimals could be 8 or 18 link:

struct ReportV3 {
bytes32 feedId; // The stream ID the report has data for.
uint32 validFromTimestamp; // Earliest timestamp for which price is applicable.
uint32 observationsTimestamp; // Latest timestamp for which price is applicable.
uint192 nativeFee; // Base cost to validate a transaction using the report, denominated in the chain’s native token (e.g., WETH/ETH).
uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK.
uint32 expiresAt; // Latest timestamp where the report can be verified onchain.
int192 price; // DON consensus median price (8 or 18 decimals). // <--
int192 bid; // Simulated price impact of a buy order up to the X% depth of liquidity utilisation (8 or 18 decimals).
int192 ask; // Simulated price impact of a sell order up to the X% depth of liquidity utilisation (8 or 18 decimals).
}

Impact

Some token in scope could not be used to fulfill swap, and its price can have wrong decimals if its data stream is created later

Recommendations

Currently,dont interact with token that do not have data stream feeds, and do not assume its price is always 18 decimals.

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Some feeds are not available on Chainlink

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.