diff --git a/pallets/asset-index/src/lib.rs b/pallets/asset-index/src/lib.rs index aae50e7d30..0c436535fa 100644 --- a/pallets/asset-index/src/lib.rs +++ b/pallets/asset-index/src/lib.rs @@ -42,7 +42,7 @@ pub mod pallet { use xcm::opaque::v0::MultiLocation; use pallet_asset_depository::MultiAssetDepository; - use pallet_price_feed::{AssetPricePair, PriceFeed}; + use pallet_price_feed::{AssetPricePair, Price, PriceFeed}; use pallet_remote_asset_manager::RemoteAssetManager; use crate::traits::WithdrawalFee; @@ -190,6 +190,13 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { T::AdminOrigin::ensure_origin(origin.clone())?; let caller = ensure_signed(origin)?; + + // Store intial price pair if not exists + T::PriceFeed::ensure_price( + asset_id, + Price::from_inner(value.saturating_mul(units).into()), + )?; + >::add_asset( &asset_id, &units, diff --git a/pallets/asset-index/src/mock.rs b/pallets/asset-index/src/mock.rs index 85c39bc51f..cd94cf650c 100644 --- a/pallets/asset-index/src/mock.rs +++ b/pallets/asset-index/src/mock.rs @@ -178,11 +178,7 @@ pub const ASSET_B_PRICE_MULTIPLIER: Balance = 3; pub struct MockPriceFeed; impl PriceFeed for MockPriceFeed { fn get_price(quote: AssetId) -> Result, DispatchError> { - if quote == UNKNOWN_ASSET_ID { - Err(pallet_asset_index::Error::::UnsupportedAsset.into()) - } else { - Self::get_price_pair(PINT_ASSET_ID, quote) - } + Self::get_price_pair(PINT_ASSET_ID, quote) } fn get_price_pair( @@ -190,7 +186,8 @@ impl PriceFeed for MockPriceFeed { quote: AssetId, ) -> Result, DispatchError> { let price = match quote { - ASSET_A_ID => { + // includes unknown asset id since we don't need to mock initial price pair here + ASSET_A_ID | UNKNOWN_ASSET_ID => { Price::checked_from_rational(600, 600 / ASSET_A_PRICE_MULTIPLIER).unwrap() } ASSET_B_ID => { @@ -200,6 +197,11 @@ impl PriceFeed for MockPriceFeed { }; Ok(AssetPricePair { base, quote, price }) } + + fn ensure_price(_: AssetId, _: Price) -> Result, DispatchError> { + // pass all unknown asset ids + Self::get_price(UNKNOWN_ASSET_ID) + } } // Build genesis storage according to the mock runtime. diff --git a/pallets/asset-index/src/tests.rs b/pallets/asset-index/src/tests.rs index 643d18ba2e..54187e855b 100644 --- a/pallets/asset-index/src/tests.rs +++ b/pallets/asset-index/src/tests.rs @@ -148,13 +148,6 @@ fn deposit_works_with_user_balance() { fn deposit_fails_for_unknown_assets() { let initial_balances: Vec<(AccountId, Balance)> = vec![(ADMIN_ACCOUNT_ID, 0)]; new_test_ext(initial_balances).execute_with(|| { - assert_ok!(AssetIndex::add_asset( - Origin::signed(ADMIN_ACCOUNT_ID), - ASSET_A_ID, - 100, - AssetAvailability::Liquid(MultiLocation::Null), - 5 - )); assert_noop!( AssetIndex::deposit(Origin::signed(ASHLEY), UNKNOWN_ASSET_ID, 1_000), pallet::Error::::UnsupportedAsset @@ -163,7 +156,7 @@ fn deposit_fails_for_unknown_assets() { } #[test] -fn deposit_fails_for_when_price_feed_unavailable() { +fn deposit_ok_for_when_price_feed_unavailable() { let initial_balances: Vec<(AccountId, Balance)> = vec![(ADMIN_ACCOUNT_ID, 0)]; new_test_ext(initial_balances).execute_with(|| { assert_ok!(AssetIndex::add_asset( @@ -173,10 +166,12 @@ fn deposit_fails_for_when_price_feed_unavailable() { AssetAvailability::Liquid(MultiLocation::Null), 5 )); - assert_noop!( - AssetIndex::deposit(Origin::signed(ASHLEY), UNKNOWN_ASSET_ID, 1_000), - pallet::Error::::UnsupportedAsset - ); + assert_ok!(AssetDepository::deposit(&UNKNOWN_ASSET_ID, &ASHLEY, 1_000)); + assert_ok!(AssetIndex::deposit( + Origin::signed(ASHLEY), + UNKNOWN_ASSET_ID, + 1 + ),); }) } @@ -191,16 +186,11 @@ fn deposit_fails_on_overflowing() { AssetAvailability::Liquid(MultiLocation::Null), 5 )); - assert_ok!(AssetDepository::deposit(&ASSET_A_ID, &ASHLEY, Balance::MAX)); + assert_noop!( AssetIndex::deposit(Origin::signed(ASHLEY), ASSET_A_ID, Balance::MAX), pallet::Error::::AssetVolumeOverflow ); - assert_ok!(AssetIndex::deposit( - Origin::signed(ASHLEY), - ASSET_A_ID, - 1_000 - )); }) } diff --git a/pallets/price-feed/src/lib.rs b/pallets/price-feed/src/lib.rs index 21afa7290d..eb88157468 100644 --- a/pallets/price-feed/src/lib.rs +++ b/pallets/price-feed/src/lib.rs @@ -76,6 +76,15 @@ pub mod pallet { pub type AssetFeeds = StorageMap<_, Blake2_128Concat, T::AssetId, FeedIdFor, OptionQuery>; + #[pallet::storage] + /// (AssetId) -> AssetPricePair + /// + /// This storage stores the initial price pair for quote assets based on `SelfAssetId` + /// + /// * insert: adding a new asset with no price pair been set yet + pub type InitialPricePairs = + StorageMap<_, Blake2_128Concat, T::AssetId, AssetPricePair, OptionQuery>; + #[pallet::genesis_config] pub struct GenesisConfig where @@ -272,6 +281,26 @@ pub mod pallet { Ok(AssetPricePair { base, quote, price }) } + + /// Ensure quote asset has an initial price pair + /// + /// * Set initial price pair if feed not exists + fn ensure_price( + quote: T::AssetId, + price: Price, + ) -> Result, DispatchError> { + let pair = AssetPricePair { + base: T::SelfAssetId::get(), + quote: quote.clone(), + price, + }; + + if Self::get_price_pair(T::SelfAssetId::get(), quote.clone()).is_err() { + >::insert(quote, pair.clone()); + } + + Ok(pair) + } } /// Trait for the asset-index pallet extrinsic weights. diff --git a/pallets/price-feed/src/traits.rs b/pallets/price-feed/src/traits.rs index d710a862de..df1f016e95 100644 --- a/pallets/price-feed/src/traits.rs +++ b/pallets/price-feed/src/traits.rs @@ -1,7 +1,7 @@ // Copyright 2021 ChainSafe Systems // SPDX-License-Identifier: LGPL-3.0-only -use crate::types::AssetPricePair; +use crate::types::{AssetPricePair, Price}; use frame_support::dispatch::DispatchError; /// An interface to access price data @@ -14,4 +14,8 @@ pub trait PriceFeed { base: AssetId, quote: AssetId, ) -> Result, DispatchError>; + + /// Set initial price pair if feed not exists + fn ensure_price(quote: AssetId, units: Price) + -> Result, DispatchError>; }