Skip to content

Commit d17f50b

Browse files
authored
fix: conversion of Ekubo x128 to Wad (#604)
This commit fixes a bug in the conversion of Ekubo's oracle price to Wad. Unlike Ekubo Core, the Ekubo's oracle price already returns the squared price.
1 parent 5808049 commit d17f50b

File tree

4 files changed

+26
-35
lines changed

4 files changed

+26
-35
lines changed

src/core/receptor.cairo

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub mod receptor {
99
use opus::interfaces::IReceptor::IReceptor;
1010
use opus::interfaces::IShrine::{IShrineDispatcher, IShrineDispatcherTrait};
1111
use opus::types::QuoteTokenInfo;
12-
use opus::utils::math::{median_of_three, scale_x128_to_wad};
12+
use opus::utils::math::{median_of_three, ekubo_oracle_price_to_wad};
1313
use starknet::{ContractAddress, get_block_timestamp};
1414
use wadray::{Wad, WAD_DECIMALS};
1515

@@ -62,7 +62,7 @@ pub mod receptor {
6262
}
6363

6464
//
65-
// Events
65+
// Events
6666
//
6767

6868
#[event]
@@ -166,7 +166,7 @@ pub mod receptor {
166166
let quote_token_info: QuoteTokenInfo = self.quote_tokens.read(index);
167167
let quote: u256 = oracle_extension
168168
.get_price_x128_over_last(cash, quote_token_info.address, twap_duration);
169-
let scaled_quote: Wad = scale_x128_to_wad(quote, quote_token_info.decimals);
169+
let scaled_quote: Wad = ekubo_oracle_price_to_wad(quote, quote_token_info.decimals);
170170

171171
quotes.append(scaled_quote);
172172
index += 1;

src/tests/receptor/test_receptor.cairo

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,9 @@ mod test_receptor {
236236
// actual mainnet values from 1727418625 start time to 1727429425 end time
237237
// converted in python
238238
let prices: Span<u256> = array![
239-
340309250276362099785975626643777172060, // 1.000158012403645039602034587 DAI / CASH
240-
340527434977254803682969657, // 1.001440899252887204535902704 USDC / CASH
241-
340328625112763872478829777, // 1.000271899695698999556601210 USDT / CASH
239+
340309250276362099785975626643777172060, // 1.000079003081079 DAI / CASH
240+
340527434977254803682969657, // 1.0007201902894171 USDC / CASH
241+
340328625112763872478829777, // 1.000135940607925 USDT / CASH
242242
]
243243
.span();
244244
receptor_utils::set_next_prices(
@@ -251,9 +251,9 @@ mod test_receptor {
251251
let quotes: Span<Wad> = receptor.get_quotes();
252252
let expected_yin_spot_price: Wad = *quotes[2];
253253
let mut expected_prices: Span<Wad> = array![
254-
1000158012403645039_u128.into(), // DAI
255-
1001440899252887204_u128.into(), // USDC
256-
1000271899695698999_u128.into(), // USDT
254+
1000079003081079000_u128.into(), // DAI
255+
1000720190289417000_u128.into(), // USDC
256+
1000135940607925000_u128.into(), // USDT
257257
]
258258
.span();
259259
let error_margin: Wad = 200_u128.into();

src/tests/utils/test_math.cairo

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ mod test_math {
22
use core::integer::BoundedInt;
33
use core::num::traits::Zero;
44
use opus::tests::common::assert_equalish;
5-
use opus::utils::math::{median_of_three, pow, scale_x128_to_wad, sqrt};
5+
use opus::utils::math::{median_of_three, pow, ekubo_oracle_price_to_wad, sqrt};
66
use wadray::{Ray, RAY_ONE, Wad};
77

88
#[test]
@@ -89,29 +89,29 @@ mod test_math {
8989
}
9090

9191
#[test]
92-
fn test_scale_x128_to_wad() {
92+
fn test_ekubo_oracle_price_to_wad() {
9393
let error_margin: Wad = 200_u128.into();
9494

9595
// 18 decimals
9696
let x128_val: u256 = 340351451218700252552422283729072753607;
97-
let actual: Wad = scale_x128_to_wad(x128_val, 18);
98-
let expected: Wad = 1000406082226072611_u128.into();
97+
let actual: Wad = ekubo_oracle_price_to_wad(x128_val, 18);
98+
let expected: Wad = 1000203020504373800_u128.into();
9999
assert_equalish(actual, expected, error_margin, 'wrong x128 to wad #1');
100100

101101
let x128_val: u256 = 339351451218700252552422283729072753607;
102-
let actual: Wad = scale_x128_to_wad(x128_val, 18);
103-
let expected: Wad = 994536053393236430_u128.into();
102+
let actual: Wad = ekubo_oracle_price_to_wad(x128_val, 18);
103+
let expected: Wad = 997264284627318000_u128.into();
104104
assert_equalish(actual, expected, error_margin, 'wrong x128 to wad #2');
105105

106106
// 6 decimals
107107
let x128_val: u256 = 340245254854570020996364378;
108-
let actual: Wad = scale_x128_to_wad(x128_val, 6);
109-
let expected: Wad = 999781886772824962_u128.into();
108+
let actual: Wad = ekubo_oracle_price_to_wad(x128_val, 6);
109+
let expected: Wad = 999890937439091300_u128.into();
110110
assert_equalish(actual, expected, error_margin, 'wrong x128 to wad #3');
111111

112112
let x128_val: u256 = 341245254854570020996364378;
113-
let actual: Wad = scale_x128_to_wad(x128_val, 6);
114-
let expected: Wad = 1005667353683370322_u128.into();
113+
let actual: Wad = ekubo_oracle_price_to_wad(x128_val, 6);
114+
let expected: Wad = 1002829673316147100_u128.into();
115115
assert_equalish(actual, expected, error_margin, 'wrong x128 to wad #4');
116116
}
117117

src/utils/math.cairo

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,13 @@ pub fn div_u128_by_ray(lhs: u128, rhs: Ray) -> u128 {
4747

4848
// If the quote token has less than 18 decimal precision, then the
4949
// x128 value needs to be scaled up by the quote token's decimals
50-
// https://docs.ekubo.org/integration-guides/reference/reading-pool-price
51-
pub fn scale_x128_to_wad(n: u256, decimals: u8) -> Wad {
52-
let decimals_diff: u8 = WAD_DECIMALS - decimals;
53-
54-
// Scale value up to Wad precision first to avoid precision loss during division
55-
let wad_scale: u256 = WAD_SCALE.into();
56-
let scaled: u256 = n * wad_scale * pow(10, decimals_diff).into();
57-
let sqrt: u256 = scaled / TWO_POW_128.into();
58-
59-
// `sqrt` is of Wad precision here so the result will be of 10 ** 36 precision
60-
let sq: u512 = u256_wide_mul(sqrt, sqrt);
61-
62-
// Scale the value back to Wad precision
63-
let (val, _) = u512_safe_div_rem_by_u256(sq, wad_scale.try_into().unwrap());
64-
65-
let val: u256 = val.try_into().unwrap();
50+
// See https://docs.ekubo.org/integration-guides/reference/reading-pool-price
51+
// for conversion of x128 values from the Ekubo Core. However, take note that
52+
// Ekubo oracle extension already gives the squared price.
53+
pub fn ekubo_oracle_price_to_wad(n: u256, decimals: u8) -> Wad {
54+
// we multiply by WAD_SCALE to get it into Wad precision and then mul again
55+
// by the appropriate precision, all before dividing to prevent precision loss
56+
let val: u256 = n * WAD_SCALE.into() * pow(10, WAD_DECIMALS - decimals).into() / TWO_POW_128.into();
6657
val.try_into().unwrap()
6758
}
6859

0 commit comments

Comments
 (0)