diff --git a/pallets/fee-share/src/mock.rs b/pallets/fee-share/src/mock.rs index a885cf11b..dd7f3f444 100644 --- a/pallets/fee-share/src/mock.rs +++ b/pallets/fee-share/src/mock.rs @@ -278,6 +278,14 @@ impl PriceFeeder for MockPriceFeeder { fn get_normal_price(_asset_id: &CurrencyId) -> Option { todo!() } + + fn get_oracle_amount_by_currency_and_amount_in( + _currency_in: &CurrencyId, + _amount_in: bifrost_primitives::Balance, + _currency_out: &CurrencyId, + ) -> Option { + todo!() + } } pub struct ParaInfo; diff --git a/pallets/lend-market/src/mock.rs b/pallets/lend-market/src/mock.rs index 42283470c..831f66497 100644 --- a/pallets/lend-market/src/mock.rs +++ b/pallets/lend-market/src/mock.rs @@ -258,6 +258,14 @@ impl PriceFeeder for MockPriceFeeder { fn get_normal_price(_asset_id: &CurrencyId) -> Option { todo!() } + + fn get_oracle_amount_by_currency_and_amount_in( + _currency_in: &CurrencyId, + _amount_in: Balance, + _currency_out: &CurrencyId, + ) -> Option { + todo!() + } } parameter_types! { diff --git a/pallets/leverage-staking/src/mock.rs b/pallets/leverage-staking/src/mock.rs index a4c8fd0d5..e823bf8fe 100644 --- a/pallets/leverage-staking/src/mock.rs +++ b/pallets/leverage-staking/src/mock.rs @@ -422,6 +422,14 @@ impl PriceFeeder for MockPriceFeeder { fn get_normal_price(_asset_id: &CurrencyId) -> Option { todo!() } + + fn get_oracle_amount_by_currency_and_amount_in( + _currency_in: &CurrencyId, + _amount_in: Balance, + _currency_out: &CurrencyId, + ) -> Option { + todo!() + } } parameter_types! { diff --git a/pallets/prices/src/lib.rs b/pallets/prices/src/lib.rs index 105ac1e8a..f0aa7842f 100644 --- a/pallets/prices/src/lib.rs +++ b/pallets/prices/src/lib.rs @@ -193,6 +193,11 @@ impl Pallet { }) } + fn get_storage_price(asset_id: &CurrencyId) -> Option { + EmergencyPrice::::get(asset_id) + .or_else(|| T::Source::get(asset_id).and_then(|price| Some(price.value))) + } + fn get_asset_mantissa(asset_id: &CurrencyId) -> Option { 10u128.checked_pow( asset_id @@ -248,6 +253,25 @@ impl PriceFeeder for Pallet { .and_then(|price| Some(price.value.into_inner().saturating_div(decimals))) }) } + + /// Get the amount of currencies according to the oracle price data. + fn get_oracle_amount_by_currency_and_amount_in( + currency_in: &CurrencyId, + amount_in: Balance, + currency_out: &CurrencyId, + ) -> Option { + let currency_in_mantissa = Self::get_asset_mantissa(currency_in)?; + let currency_out_mantissa = Self::get_asset_mantissa(currency_out)?; + let currency_in_price = Self::get_storage_price(currency_in)?; + let currency_out_price = Self::get_storage_price(currency_out)?; + let total_value = currency_in_price + .mul(FixedU128::from_inner(amount_in)) + .div(FixedU128::from_inner(currency_in_mantissa)); + let amount_out = total_value + .mul(FixedU128::from_inner(currency_out_mantissa)) + .div(currency_out_price); + Some(amount_out.into_inner()) + } } impl EmergencyPriceFeeder for Pallet { diff --git a/pallets/prices/src/mock.rs b/pallets/prices/src/mock.rs index 3d6a24755..157ce59cf 100644 --- a/pallets/prices/src/mock.rs +++ b/pallets/prices/src/mock.rs @@ -25,7 +25,7 @@ use sp_runtime::{traits::IdentityLookup, FixedPointNumber}; use bifrost_asset_registry::AssetIdMaps; pub use bifrost_primitives::{ currency::{FIL, VFIL}, - DOT, KSM, VDOT, + DOT, KSM, MANTA, VDOT, }; use bifrost_primitives::{Moment, ASTR, BNC, DOT_U, GLMR}; use sp_runtime::BuildStorage; @@ -54,10 +54,18 @@ pub struct MockDataProvider; impl DataProvider for MockDataProvider { fn get(asset_id: &CurrencyId) -> Option { match *asset_id { + BNC => Some(TimeStampedPrice { + value: Price::from_inner(200_000_000_000_000_000), + timestamp: 0, + }), DOT => Some(TimeStampedPrice { value: Price::saturating_from_integer(100), timestamp: 0 }), KSM => Some(TimeStampedPrice { value: Price::saturating_from_integer(500), timestamp: 0 }), + MANTA => Some(TimeStampedPrice { + value: Price::from_inner(600_000_000_000_000_000), + timestamp: 0, + }), VDOT => Some(TimeStampedPrice { value: Price::from_inner(15000000000_0000000000), timestamp: 0, diff --git a/pallets/prices/src/tests.rs b/pallets/prices/src/tests.rs index d81680730..df945cd2f 100644 --- a/pallets/prices/src/tests.rs +++ b/pallets/prices/src/tests.rs @@ -15,7 +15,8 @@ //! Unit tests for the prices pallet. use super::*; -use bifrost_primitives::VKSM; +use bifrost_asset_registry::AssetMetadata; +use bifrost_primitives::{BNC, MANTA, VKSM}; use frame_support::{assert_noop, assert_ok}; use mock::{RuntimeEvent, *}; use sp_runtime::{traits::BadOrigin, FixedPointNumber}; @@ -194,3 +195,77 @@ fn get_foreign_token_price_work() { assert_eq!(Prices::get_price(&FIL), Prices::get_price(&VFIL)); }); } + +#[test] +fn fixed_u128() { + new_test_ext().execute_with(|| { + let bnc_decimal = 10u128.pow(12); + let bnc_amount = 100 * 10u128.pow(12); + let bnc_price = FixedU128::from_inner(200_000_000_000_000_000); + // 100 * 0.2 = 20 U + let dot_decimal = 10u128.pow(10); + let dot_amount = 5 * 10u128.pow(10); + let dot_price = FixedU128::from(4); + + let bnc_total_value = + bnc_price / FixedU128::from_inner(bnc_decimal) * FixedU128::from_inner(bnc_amount); + let dot_total_value = + dot_price / FixedU128::from_inner(dot_decimal) * FixedU128::from_inner(dot_amount); + let dot_amount_fixed_u128 = + dot_total_value * FixedU128::from_inner(dot_decimal) / dot_price; + assert_eq!(bnc_total_value, dot_total_value); + println!("{:?}", bnc_total_value); + println!("{:?}", dot_amount_fixed_u128); + assert_eq!(dot_amount, dot_amount_fixed_u128.into_inner()); + }) +} + +#[test] +fn get_oracle_amount_by_currency_and_amount_in() { + new_test_ext().execute_with(|| { + assert_ok!(AssetRegistry::do_register_metadata( + MANTA, + &AssetMetadata { + name: b"Manta".to_vec(), + symbol: b"Manta".to_vec(), + decimals: 18, + minimal_balance: 1_000_000_000_000_000u128, + } + )); + // 100 * 0.2 = 20u + let bnc_amount = 100 * 10u128.pow(12); + // 0.2 DOT + assert_eq!( + Some(2_000_000_000), + Prices::get_oracle_amount_by_currency_and_amount_in(&BNC, bnc_amount, &DOT) + ); + // 0.04 KSM + assert_eq!( + Some(40_000_000_000), + Prices::get_oracle_amount_by_currency_and_amount_in(&BNC, bnc_amount, &KSM) + ); + // 33.33333333333333333333 + assert_eq!( + Some(33_333_333_333_333_333_333), + Prices::get_oracle_amount_by_currency_and_amount_in(&BNC, bnc_amount, &MANTA) + ); + + // 0.01 * 0.2 = 0.002 U + let bnc_amount = 10u128.pow(10); + // 0.00002 DOT * 100 = 0.002 U + assert_eq!( + Some(200_000), + Prices::get_oracle_amount_by_currency_and_amount_in(&BNC, bnc_amount, &DOT) + ); + // 0.000004 KSM * 500 = 0.002 U + assert_eq!( + Some(4_000_000), + Prices::get_oracle_amount_by_currency_and_amount_in(&BNC, bnc_amount, &KSM) + ); + // 0.003333333333333333333333 MANTA + assert_eq!( + Some(3_333_333_333_333_333), + Prices::get_oracle_amount_by_currency_and_amount_in(&BNC, bnc_amount, &MANTA) + ); + }); +} diff --git a/pallets/traits/src/lib.rs b/pallets/traits/src/lib.rs index b36d98329..a7e1ff9d9 100644 --- a/pallets/traits/src/lib.rs +++ b/pallets/traits/src/lib.rs @@ -1,6 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std)] -use bifrost_primitives::{CurrencyId, PriceDetail}; +use bifrost_primitives::{Balance, CurrencyId, PriceDetail}; use num_bigint::{BigUint, ToBigUint}; pub mod evm; @@ -15,6 +15,11 @@ pub trait EmergencyCallFilter { pub trait PriceFeeder { fn get_price(asset_id: &CurrencyId) -> Option; fn get_normal_price(asset_id: &CurrencyId) -> Option; + fn get_oracle_amount_by_currency_and_amount_in( + currency_in: &CurrencyId, + amount_in: Balance, + currency_out: &CurrencyId, + ) -> Option; } pub trait EmergencyPriceFeeder {