Skip to content

Commit ca57555

Browse files
authored
Merge pull request #387 from Meet-hybrid/feature/multi-hop-fixedpoint-guards
feat: enhance fixed-point normalization and add test cases for edge v…
2 parents b7df9ca + b3579bc commit ca57555

9 files changed

Lines changed: 552 additions & 5 deletions

contracts/price-oracle/src/math.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,18 +128,41 @@ pub fn normalize_to_seven(value: i128, input_decimals: u32) -> Result<i128, Erro
128128
/// normalize_to_nine(1_000_000_000_00, 11) => 1_000_000_000 (scale down)
129129
/// ```
130130
pub fn normalize_to_nine(value: i128, native_decimals: u32) -> Result<i128, Error> {
131+
// This normalization is a critical fixed-point boundary.
132+
// To reduce rounding/truncation drift when this function is used
133+
// inside multi-step rate translations, we do the multiply/divide
134+
// sequence in a temporarily up-scaled space.
135+
131136
const TARGET: u32 = 9;
132-
if native_decimals < TARGET {
137+
const INTERIOR_SCALE: i128 = 1_000_000_000_000_000; // 10^15
138+
139+
// NOTE: INTERIOR_SCALE is chosen so that the final result remains within
140+
// the project's 9-decimal fixed-point footprint by dividing back down
141+
// after the translation.
142+
143+
let scaled = value
144+
.checked_mul(INTERIOR_SCALE)
145+
.ok_or(Error::PriceMathOverflow)?;
146+
147+
let normalized_in_interior_space = if native_decimals < TARGET {
133148
let diff = TARGET - native_decimals;
134149
let multiplier = 10_i128.checked_pow(diff).ok_or(Error::PriceMathOverflow)?;
135-
value.checked_mul(multiplier).ok_or(Error::PriceMathOverflow)
150+
scaled
151+
.checked_mul(multiplier)
152+
.ok_or(Error::PriceMathOverflow)?
136153
} else if native_decimals > TARGET {
137154
let diff = native_decimals - TARGET;
138155
let divisor = 10_i128.checked_pow(diff).ok_or(Error::PriceMathOverflow)?;
139-
value.checked_div(divisor).ok_or(Error::PriceMathOverflow)
156+
scaled
157+
.checked_div(divisor)
158+
.ok_or(Error::PriceMathOverflow)?
140159
} else {
141-
Ok(value)
142-
}
160+
scaled
161+
};
162+
163+
normalized_in_interior_space
164+
.checked_div(INTERIOR_SCALE)
165+
.ok_or(Error::PriceMathOverflow)
143166
}
144167

145168
/// Calculate the inverse of a price (e.g., NGN/XLM → XLM/NGN).

test_snapshots/test/test_cancel_upgrade.1.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
},
4141
{
4242
"u64": 0
43+
},
44+
{
45+
"u64": 18446744073709551615
4346
}
4447
]
4548
}
@@ -382,6 +385,9 @@
382385
},
383386
{
384387
"u64": 0
388+
},
389+
{
390+
"u64": 18446744073709551615
385391
}
386392
]
387393
}

test_snapshots/test/test_execute_upgrade_after_timelock.1.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
},
4141
{
4242
"u64": 0
43+
},
44+
{
45+
"u64": 18446744073709551615
4346
}
4447
]
4548
}
@@ -361,6 +364,9 @@
361364
},
362365
{
363366
"u64": 0
367+
},
368+
{
369+
"u64": 18446744073709551615
364370
}
365371
]
366372
}

0 commit comments

Comments
 (0)