@@ -8,6 +8,7 @@ mod u256;
88pub use u256:: { hi_lo, LoHi , U256 } ;
99
1010use crate :: error:: ExecutorQuoterError ;
11+ use crate :: state:: { ChainInfo , QuoteBody } ;
1112use pinocchio:: program_error:: ProgramError ;
1213
1314/// Quote decimals (prices stored with 10^10 precision)
@@ -75,14 +76,16 @@ pub fn pow10(exp: u8) -> U256 {
7576/// Returns None on overflow.
7677#[ inline]
7778pub fn normalize ( amount : U256 , from : u8 , to : u8 ) -> Option < U256 > {
78- if from > to {
79- let divisor = pow10 ( from - to) ;
80- amount. checked_div ( divisor)
81- } else if from < to {
82- let multiplier = pow10 ( to - from) ;
83- amount. checked_mul ( multiplier)
84- } else {
85- Some ( amount)
79+ match from. cmp ( & to) {
80+ core:: cmp:: Ordering :: Greater => {
81+ let divisor = pow10 ( from - to) ;
82+ amount. checked_div ( divisor)
83+ }
84+ core:: cmp:: Ordering :: Less => {
85+ let multiplier = pow10 ( to - from) ;
86+ amount. checked_mul ( multiplier)
87+ }
88+ core:: cmp:: Ordering :: Equal => Some ( amount) ,
8689 }
8790}
8891
@@ -111,12 +114,8 @@ pub fn div_decimals(a: U256, b: U256, decimals: u8) -> Option<U256> {
111114/// Returns the required payment in SVM native token decimals (lamports for SOL).
112115///
113116/// # Arguments
114- /// * `base_fee` - Base fee in quote decimals (10^10)
115- /// * `src_price` - Source chain token USD price (10^10)
116- /// * `dst_price` - Destination chain token USD price (10^10)
117- /// * `dst_gas_price` - Destination chain gas price
118- /// * `dst_gas_price_decimals` - Decimals for dst_gas_price
119- /// * `dst_native_decimals` - Decimals for destination chain native token
117+ /// * `quote_body` - Quote body containing prices and fees
118+ /// * `chain_info` - Chain info containing decimal configurations
120119/// * `gas_limit` - Total gas limit from relay instructions
121120/// * `msg_value` - Total message value from relay instructions
122121///
@@ -126,38 +125,37 @@ pub fn div_decimals(a: U256, b: U256, decimals: u8) -> Option<U256> {
126125/// # Errors
127126/// Returns `MathOverflow` on arithmetic overflow or division by zero.
128127pub fn estimate_quote (
129- base_fee : u64 ,
130- src_price : u64 ,
131- dst_price : u64 ,
132- dst_gas_price : u64 ,
133- dst_gas_price_decimals : u8 ,
134- dst_native_decimals : u8 ,
128+ quote_body : & QuoteBody ,
129+ chain_info : & ChainInfo ,
135130 gas_limit : u128 ,
136131 msg_value : u128 ,
137132) -> Result < u64 , ProgramError > {
138133 let overflow_err = || -> ProgramError { ExecutorQuoterError :: MathOverflow . into ( ) } ;
139134
140- let total_u256 = estimate_quote_u256 ( base_fee , src_price , dst_price , dst_gas_price , dst_gas_price_decimals , dst_native_decimals , gas_limit, msg_value) ?;
135+ let total_u256 = estimate_quote_u256 ( quote_body , chain_info , gas_limit, msg_value) ?;
141136
142- // 6. Convert from EVM_DECIMAL_RESOLUTION to SVM_DECIMAL_RESOLUTION
143- let result = normalize ( total_u256, EVM_DECIMAL_RESOLUTION , SVM_DECIMAL_RESOLUTION ) . ok_or_else ( overflow_err) ?;
137+ // Convert from EVM_DECIMAL_RESOLUTION to SVM_DECIMAL_RESOLUTION
138+ let result = normalize ( total_u256, EVM_DECIMAL_RESOLUTION , SVM_DECIMAL_RESOLUTION )
139+ . ok_or_else ( overflow_err) ?;
144140
145- // 7. Convert to u64 (should fit for reasonable quote values)
141+ // Convert to u64 (should fit for reasonable quote values)
146142 result
147143 . try_into_u64 ( )
148144 . ok_or_else ( || ExecutorQuoterError :: MathOverflow . into ( ) )
149145}
150146
151147fn estimate_quote_u256 (
152- base_fee : u64 ,
153- src_price : u64 ,
154- dst_price : u64 ,
155- dst_gas_price : u64 ,
156- dst_gas_price_decimals : u8 ,
157- dst_native_decimals : u8 ,
148+ quote_body : & QuoteBody ,
149+ chain_info : & ChainInfo ,
158150 gas_limit : u128 ,
159151 msg_value : u128 ,
160152) -> Result < U256 , ProgramError > {
153+ let base_fee = quote_body. base_fee ;
154+ let src_price = quote_body. src_price ;
155+ let dst_price = quote_body. dst_price ;
156+ let dst_gas_price = quote_body. dst_gas_price ;
157+ let dst_gas_price_decimals = chain_info. gas_price_decimals ;
158+ let dst_native_decimals = chain_info. native_decimals ;
161159 let overflow_err = || -> ProgramError { ExecutorQuoterError :: MathOverflow . into ( ) } ;
162160
163161 // 1. Base fee conversion: normalize from QUOTE_DECIMALS to EVM_DECIMAL_RESOLUTION
@@ -223,59 +221,83 @@ fn estimate_quote_u256(
223221mod tests {
224222 use super :: * ;
225223
224+ fn make_quote_body ( base_fee : u64 , src_price : u64 , dst_price : u64 , dst_gas_price : u64 ) -> QuoteBody {
225+ QuoteBody {
226+ discriminator : 0 ,
227+ _padding : [ 0 ; 3 ] ,
228+ chain_id : 1 ,
229+ bump : 0 ,
230+ _reserved : 0 ,
231+ dst_price,
232+ src_price,
233+ dst_gas_price,
234+ base_fee,
235+ }
236+ }
237+
238+ fn make_chain_info ( gas_price_decimals : u8 , native_decimals : u8 ) -> ChainInfo {
239+ ChainInfo {
240+ discriminator : 0 ,
241+ enabled : 1 ,
242+ chain_id : 1 ,
243+ gas_price_decimals,
244+ native_decimals,
245+ bump : 0 ,
246+ _reserved : 0 ,
247+ }
248+ }
249+
226250 #[ test]
227251 fn test_estimate_quote_eth_to_sol ( ) {
228- let result_18 = estimate_quote_u256 (
252+ let quote_body = make_quote_body (
229253 100 , // base_fee
230254 2650000000 , // src_price (SOL ~$265)
231255 160000000 , // dst_price (ETH ~$16 - test values)
232256 399146 , // dst_gas_price
233- 15 , // gas_price_decimals
234- 18 , // dst_native_decimals (ETH)
235- 250000 , // gas_limit
236- 0 , // msg_value
237- )
238- . unwrap ( ) ;
257+ ) ;
258+ let chain_info = make_chain_info ( 15 , 18 ) ; // gas_price_decimals, native_decimals (ETH)
259+
260+ let result_18 = estimate_quote_u256 ( & quote_body, & chain_info, 250000 , 0 ) . unwrap ( ) ;
239261
240262 // Result is in lamports (9 decimals). Convert back to 18 decimals for comparison.
241263 let expected_18 = U256 :: from_u64 ( 6034845283018u64 ) ;
242264 // Allow for truncation: result should be within 10^9 of expected
243265 assert ! ( result_18. checked_sub( expected_18) . is_some( ) ) ;
244- assert ! ( result_18. checked_add( U256 :: from_u64( 1_000_000_000 - 1 ) ) . unwrap( ) . checked_sub( expected_18) . is_some( ) ) ;
266+ assert ! ( result_18
267+ . checked_add( U256 :: from_u64( 1_000_000_000 - 1 ) )
268+ . unwrap( )
269+ . checked_sub( expected_18)
270+ . is_some( ) ) ;
245271 }
246272
247273 #[ test]
248274 fn test_estimate_quote_with_msg_value ( ) {
275+ let quote_body = make_quote_body ( 100 , 2650000000 , 160000000 , 399146 ) ;
276+ let chain_info = make_chain_info ( 15 , 18 ) ;
277+
249278 let result_18 = estimate_quote_u256 (
250- 100 , // base_fee
251- 2650000000 , // src_price
252- 160000000 , // dst_price
253- 399146 , // dst_gas_price
254- 15 , // gas_price_decimals
255- 18 , // dst_native_decimals
256- 250000 , // gas_limit
279+ & quote_body,
280+ & chain_info,
281+ 250000 ,
257282 1_000_000_000_000_000_000 , // 1 ETH in wei
258283 )
259284 . unwrap ( ) ;
260285
261286 let expected_18 = U256 :: from_u64 ( 60383393335849055 ) ;
262287 assert ! ( result_18. checked_sub( expected_18) . is_some( ) ) ;
263- assert ! ( result_18. checked_add( U256 :: from_u64( 1_000_000_000 - 1 ) ) . unwrap( ) . checked_sub( expected_18) . is_some( ) ) ;
288+ assert ! ( result_18
289+ . checked_add( U256 :: from_u64( 1_000_000_000 - 1 ) )
290+ . unwrap( )
291+ . checked_sub( expected_18)
292+ . is_some( ) ) ;
264293 }
265294
266295 #[ test]
267296 fn test_estimate_quote_zero_gas_limit ( ) {
268- let result = estimate_quote (
269- 100 , // base_fee
270- 2650000000 , // src_price
271- 160000000 , // dst_price
272- 399146 , // dst_gas_price
273- 15 , // gas_price_decimals
274- 18 , // dst_native_decimals
275- 0 , // gas_limit = 0
276- 0 , // msg_value
277- )
278- . unwrap ( ) ;
297+ let quote_body = make_quote_body ( 100 , 2650000000 , 160000000 , 399146 ) ;
298+ let chain_info = make_chain_info ( 15 , 18 ) ;
299+
300+ let result = estimate_quote ( & quote_body, & chain_info, 0 , 0 ) . unwrap ( ) ;
279301
280302 // base_fee = 100 at QUOTE_DECIMALS (10)
281303 // Converted to 9 decimals = 10 lamports
@@ -284,32 +306,25 @@ mod tests {
284306
285307 #[ test]
286308 fn test_estimate_quote_zero_src_price ( ) {
287- let result = estimate_quote (
288- 100 ,
289- 0 , // zero src_price
290- 160000000 ,
291- 399146 ,
292- 15 ,
293- 18 ,
294- 250000 ,
295- 0 ,
296- ) ;
309+ let quote_body = make_quote_body ( 100 , 0 , 160000000 , 399146 ) ; // zero src_price
310+ let chain_info = make_chain_info ( 15 , 18 ) ;
311+
312+ let result = estimate_quote ( & quote_body, & chain_info, 250000 , 0 ) ;
297313
298314 assert ! ( result. is_err( ) ) ;
299315 }
300316
301317 #[ test]
302318 fn test_estimate_quote_overflow_returns_error ( ) {
303- let result = estimate_quote (
304- u64:: MAX , // max base_fee
305- 1 , // tiny src_price (makes conversion huge)
306- u64:: MAX , // max dst_price
307- u64:: MAX , // max gas_price
308- 0 , // no decimal scaling (makes values larger)
309- 0 , // no decimal scaling
310- u128:: MAX , // max gas_limit
311- u128:: MAX , // max msg_value
319+ let quote_body = make_quote_body (
320+ u64:: MAX , // max base_fee
321+ 1 , // tiny src_price (makes conversion huge)
322+ u64:: MAX , // max dst_price
323+ u64:: MAX , // max gas_price
312324 ) ;
325+ let chain_info = make_chain_info ( 0 , 0 ) ; // no decimal scaling (makes values larger)
326+
327+ let result = estimate_quote ( & quote_body, & chain_info, u128:: MAX , u128:: MAX ) ;
313328
314329 assert ! ( result. is_err( ) ) ;
315330 }
0 commit comments