mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 21:01:02 +00:00
XCM: PayOverXcm config (#6900)
* Move XCM query functionality to trait * Fix tests * Add PayOverXcm implementation * fix the PayOverXcm trait to compile * moved doc comment out of trait implmeentation and to the trait * PayOverXCM documentation * Change documentation a bit * Added empty benchmark methods implementation and changed docs * update PayOverXCM to convert AccountIds to MultiLocations * Implement benchmarking method * Change v3 to latest * Descend origin to an asset sender (#6970) * descend origin to an asset sender * sender as tuple of dest and sender * Add more variants to the QueryResponseStatus enum * Change Beneficiary to Into<[u8; 32]> * update PayOverXcm to return concrete errors and use AccountId as sender * use polkadot-primitives for AccountId * fix dependency to use polkadot-core-primitives * force Unpaid instruction to the top of the instructions list * modify report_outcome to accept interior argument * use new_query directly for building final xcm query, instead of report_outcome * fix usage of new_query to use the XcmQueryHandler * fix usage of new_query to use the XcmQueryHandler * tiny method calling fix * xcm query handler (#7198) * drop redundant query status * rename ReportQueryStatus to OuterQueryStatus * revert rename of QueryResponseStatus * update mapping * Update xcm/xcm-builder/src/pay.rs Co-authored-by: Gavin Wood <gavin@parity.io> * Updates * Docs * Fix benchmarking stuff * Destination can be determined based on asset_kind * Tweaking API to minimise clones * Some repotting and docs --------- Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com> Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com> Co-authored-by: Anthony Alaribe <anthony.alaribe@parity.io> Co-authored-by: Gavin Wood <gavin@parity.io>
This commit is contained in:
committed by
GitHub
parent
50b53fcac3
commit
a0e2aaad78
@@ -546,6 +546,7 @@ pub mod pallet_test_notifier {
|
||||
use pallet_xcm::ensure_response;
|
||||
use sp_runtime::DispatchResult;
|
||||
use xcm::latest::prelude::*;
|
||||
use xcm_executor::traits::QueryHandler as XcmQueryHandler;
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
@@ -581,7 +582,7 @@ pub mod pallet_test_notifier {
|
||||
let id = who
|
||||
.using_encoded(|mut d| <[u8; 32]>::decode(&mut d))
|
||||
.map_err(|_| Error::<T>::BadAccountFormat)?;
|
||||
let qid = pallet_xcm::Pallet::<T>::new_query(
|
||||
let qid = <pallet_xcm::Pallet<T> as XcmQueryHandler>::new_query(
|
||||
Junction::AccountId32 { network: None, id },
|
||||
100u32.into(),
|
||||
Here,
|
||||
|
||||
@@ -52,8 +52,8 @@ use frame_system::pallet_prelude::*;
|
||||
pub use pallet::*;
|
||||
use xcm_executor::{
|
||||
traits::{
|
||||
CheckSuspension, ClaimAssets, DropAssets, MatchesFungible, OnResponse,
|
||||
VersionChangeNotifier, WeightBounds,
|
||||
CheckSuspension, ClaimAssets, DropAssets, MatchesFungible, OnResponse, QueryHandler,
|
||||
QueryResponseStatus, VersionChangeNotifier, WeightBounds,
|
||||
},
|
||||
Assets,
|
||||
};
|
||||
@@ -1126,6 +1126,66 @@ pub mod pallet {
|
||||
/// The maximum number of distinct assets allowed to be transferred in a single helper extrinsic.
|
||||
const MAX_ASSETS_FOR_TRANSFER: usize = 2;
|
||||
|
||||
impl<T: Config> QueryHandler for Pallet<T> {
|
||||
type QueryId = u64;
|
||||
type BlockNumber = T::BlockNumber;
|
||||
type Error = XcmError;
|
||||
type UniversalLocation = T::UniversalLocation;
|
||||
|
||||
/// Attempt to create a new query ID and register it as a query that is yet to respond.
|
||||
fn new_query(
|
||||
responder: impl Into<MultiLocation>,
|
||||
timeout: T::BlockNumber,
|
||||
match_querier: impl Into<MultiLocation>,
|
||||
) -> Self::QueryId {
|
||||
Self::do_new_query(responder, None, timeout, match_querier).into()
|
||||
}
|
||||
|
||||
/// To check the status of the query, use `fn query()` passing the resultant `QueryId`
|
||||
/// value.
|
||||
fn report_outcome(
|
||||
message: &mut Xcm<()>,
|
||||
responder: impl Into<MultiLocation>,
|
||||
timeout: Self::BlockNumber,
|
||||
) -> Result<Self::QueryId, Self::Error> {
|
||||
let responder = responder.into();
|
||||
let destination = Self::UniversalLocation::get()
|
||||
.invert_target(&responder)
|
||||
.map_err(|()| XcmError::LocationNotInvertible)?;
|
||||
let query_id = Self::new_query(responder, timeout, Here);
|
||||
let response_info = QueryResponseInfo { destination, query_id, max_weight: Weight::zero() };
|
||||
let report_error = Xcm(vec![ReportError(response_info)]);
|
||||
message.0.insert(0, SetAppendix(report_error));
|
||||
Ok(query_id)
|
||||
}
|
||||
|
||||
/// Removes response when ready and emits [Event::ResponseTaken] event.
|
||||
fn take_response(query_id: Self::QueryId) -> QueryResponseStatus<Self::BlockNumber> {
|
||||
match Queries::<T>::get(query_id) {
|
||||
Some(QueryStatus::Ready { response, at }) => match response.try_into() {
|
||||
Ok(response) => {
|
||||
Queries::<T>::remove(query_id);
|
||||
Self::deposit_event(Event::ResponseTaken { query_id });
|
||||
QueryResponseStatus::Ready { response, at }
|
||||
},
|
||||
Err(_) => QueryResponseStatus::UnexpectedVersion,
|
||||
},
|
||||
Some(QueryStatus::Pending { timeout, .. }) => QueryResponseStatus::Pending { timeout },
|
||||
Some(_) => QueryResponseStatus::UnexpectedVersion,
|
||||
None => QueryResponseStatus::NotFound,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn expect_response(id: Self::QueryId, response: Response) {
|
||||
let response = response.into();
|
||||
Queries::<T>::insert(
|
||||
id,
|
||||
QueryStatus::Ready { response, at: frame_system::Pallet::<T>::block_number() },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
fn do_reserve_transfer_assets(
|
||||
origin: OriginFor<T>,
|
||||
@@ -1497,36 +1557,6 @@ impl<T: Config> Pallet<T> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Consume `message` and return another which is equivalent to it except that it reports
|
||||
/// back the outcome.
|
||||
///
|
||||
/// - `message`: The message whose outcome should be reported.
|
||||
/// - `responder`: The origin from which a response should be expected.
|
||||
/// - `timeout`: The block number after which it is permissible for `notify` not to be
|
||||
/// called even if a response is received.
|
||||
///
|
||||
/// `report_outcome` may return an error if the `responder` is not invertible.
|
||||
///
|
||||
/// It is assumed that the querier of the response will be `Here`.
|
||||
///
|
||||
/// To check the status of the query, use `fn query()` passing the resultant `QueryId`
|
||||
/// value.
|
||||
pub fn report_outcome(
|
||||
message: &mut Xcm<()>,
|
||||
responder: impl Into<MultiLocation>,
|
||||
timeout: T::BlockNumber,
|
||||
) -> Result<QueryId, XcmError> {
|
||||
let responder = responder.into();
|
||||
let destination = T::UniversalLocation::get()
|
||||
.invert_target(&responder)
|
||||
.map_err(|()| XcmError::LocationNotInvertible)?;
|
||||
let query_id = Self::new_query(responder, timeout, Here);
|
||||
let response_info = QueryResponseInfo { destination, query_id, max_weight: Weight::zero() };
|
||||
let report_error = Xcm(vec![ReportError(response_info)]);
|
||||
message.0.insert(0, SetAppendix(report_error));
|
||||
Ok(query_id)
|
||||
}
|
||||
|
||||
/// Consume `message` and return another which is equivalent to it except that it reports
|
||||
/// back the outcome and dispatches `notify` on this chain.
|
||||
///
|
||||
@@ -1568,15 +1598,6 @@ impl<T: Config> Pallet<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Attempt to create a new query ID and register it as a query that is yet to respond.
|
||||
pub fn new_query(
|
||||
responder: impl Into<MultiLocation>,
|
||||
timeout: T::BlockNumber,
|
||||
match_querier: impl Into<MultiLocation>,
|
||||
) -> u64 {
|
||||
Self::do_new_query(responder, None, timeout, match_querier)
|
||||
}
|
||||
|
||||
/// Attempt to create a new query ID and register it as a query that is yet to respond, and
|
||||
/// which will call a dispatchable when a response happens.
|
||||
pub fn new_notify_query(
|
||||
@@ -1591,20 +1612,6 @@ impl<T: Config> Pallet<T> {
|
||||
Self::do_new_query(responder, Some(notify), timeout, match_querier)
|
||||
}
|
||||
|
||||
/// Attempt to remove and return the response of query with ID `query_id`.
|
||||
///
|
||||
/// Returns `None` if the response is not (yet) available.
|
||||
pub fn take_response(query_id: QueryId) -> Option<(Response, T::BlockNumber)> {
|
||||
if let Some(QueryStatus::Ready { response, at }) = Queries::<T>::get(query_id) {
|
||||
let response = response.try_into().ok()?;
|
||||
Queries::<T>::remove(query_id);
|
||||
Self::deposit_event(Event::ResponseTaken { query_id });
|
||||
Some((response, at))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Note that a particular destination to whom we would like to send a message is unknown
|
||||
/// and queue it for version discovery.
|
||||
fn note_unknown_version(dest: &MultiLocation) {
|
||||
|
||||
@@ -50,6 +50,7 @@ pub mod pallet_test_notifier {
|
||||
use frame_system::pallet_prelude::*;
|
||||
use sp_runtime::DispatchResult;
|
||||
use xcm::latest::prelude::*;
|
||||
use xcm_executor::traits::QueryHandler;
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
@@ -85,7 +86,7 @@ pub mod pallet_test_notifier {
|
||||
let id = who
|
||||
.using_encoded(|mut d| <[u8; 32]>::decode(&mut d))
|
||||
.map_err(|_| Error::<T>::BadAccountFormat)?;
|
||||
let qid = crate::Pallet::<T>::new_query(
|
||||
let qid = <crate::Pallet<T> as QueryHandler>::new_query(
|
||||
Junction::AccountId32 { network: None, id },
|
||||
100u32.into(),
|
||||
querier,
|
||||
|
||||
@@ -28,7 +28,7 @@ use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, Hash};
|
||||
use xcm::{latest::QueryResponseInfo, prelude::*};
|
||||
use xcm_builder::AllowKnownQueryResponses;
|
||||
use xcm_executor::{
|
||||
traits::{Properties, ShouldExecute},
|
||||
traits::{Properties, QueryHandler, QueryResponseStatus, ShouldExecute},
|
||||
XcmExecutor,
|
||||
};
|
||||
|
||||
@@ -170,7 +170,8 @@ fn report_outcome_works() {
|
||||
})
|
||||
);
|
||||
|
||||
let response = Some((Response::ExecutionResult(None), 1));
|
||||
let response =
|
||||
QueryResponseStatus::Ready { response: Response::ExecutionResult(None), at: 1 };
|
||||
assert_eq!(XcmPallet::take_response(0), response);
|
||||
});
|
||||
}
|
||||
@@ -270,7 +271,8 @@ fn custom_querier_works() {
|
||||
})
|
||||
);
|
||||
|
||||
let response = Some((Response::ExecutionResult(None), 1));
|
||||
let response =
|
||||
QueryResponseStatus::Ready { response: Response::ExecutionResult(None), at: 1 };
|
||||
assert_eq!(XcmPallet::take_response(0), response);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ polkadot-test-runtime = { path = "../../runtime/test-runtime" }
|
||||
default = ["std"]
|
||||
runtime-benchmarks = [
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks"
|
||||
"frame-system/runtime-benchmarks",
|
||||
"xcm-executor/runtime-benchmarks",
|
||||
]
|
||||
std = [
|
||||
"log/std",
|
||||
|
||||
@@ -28,8 +28,9 @@ pub mod test_utils;
|
||||
|
||||
mod location_conversion;
|
||||
pub use location_conversion::{
|
||||
Account32Hash, AccountId32Aliases, AccountKey20Aliases, ChildParachainConvertsVia,
|
||||
GlobalConsensusParachainConvertsFor, ParentIsPreset, SiblingParachainConvertsVia,
|
||||
Account32Hash, AccountId32Aliases, AccountKey20Aliases, AliasesIntoAccountId32,
|
||||
ChildParachainConvertsVia, GlobalConsensusParachainConvertsFor, ParentIsPreset,
|
||||
SiblingParachainConvertsVia,
|
||||
};
|
||||
|
||||
mod origin_conversion;
|
||||
@@ -95,3 +96,6 @@ pub use universal_exports::{
|
||||
ExporterFor, HaulBlob, HaulBlobError, HaulBlobExporter, NetworkExportTable,
|
||||
SovereignPaidRemoteExporter, UnpaidLocalExporter, UnpaidRemoteExporter,
|
||||
};
|
||||
|
||||
mod pay;
|
||||
pub use pay::{FixedLocation, LocatableAssetId, PayAccountId32OnChainOverXcm, PayOverXcm};
|
||||
|
||||
@@ -232,6 +232,26 @@ impl<Network: Get<Option<NetworkId>>, AccountId: From<[u8; 32]> + Into<[u8; 32]>
|
||||
}
|
||||
}
|
||||
|
||||
/// Conversion implementation which converts from a `[u8; 32]`-based `AccountId` into a
|
||||
/// `MultiLocation` consisting solely of a `AccountId32` junction with a fixed value for its
|
||||
/// network (provided by `Network`) and the `AccountId`'s `[u8; 32]` datum for the `id`.
|
||||
pub struct AliasesIntoAccountId32<Network, AccountId>(PhantomData<(Network, AccountId)>);
|
||||
impl<'a, Network: Get<Option<NetworkId>>, AccountId: Clone + Into<[u8; 32]> + Clone>
|
||||
Convert<&'a AccountId, MultiLocation> for AliasesIntoAccountId32<Network, AccountId>
|
||||
{
|
||||
fn convert(who: &AccountId) -> Result<MultiLocation, &'a AccountId> {
|
||||
Ok(AccountId32 { network: Network::get(), id: who.clone().into() }.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Network: Get<Option<NetworkId>>, AccountId: Into<[u8; 32]> + Clone>
|
||||
Convert<AccountId, MultiLocation> for AliasesIntoAccountId32<Network, AccountId>
|
||||
{
|
||||
fn convert(who: AccountId) -> Result<MultiLocation, AccountId> {
|
||||
Ok(AccountId32 { network: Network::get(), id: who.into() }.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AccountKey20Aliases<Network, AccountId>(PhantomData<(Network, AccountId)>);
|
||||
impl<Network: Get<Option<NetworkId>>, AccountId: From<[u8; 20]> + Into<[u8; 20]> + Clone>
|
||||
Convert<MultiLocation, AccountId> for AccountKey20Aliases<Network, AccountId>
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
// Copyright Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! `PayOverXcm` struct for paying through XCM and getting the status back.
|
||||
|
||||
use frame_support::traits::{
|
||||
tokens::{Pay, PaymentStatus},
|
||||
Get,
|
||||
};
|
||||
use sp_runtime::traits::Convert;
|
||||
use sp_std::{marker::PhantomData, vec};
|
||||
use xcm::{opaque::lts::Weight, prelude::*};
|
||||
use xcm_executor::traits::{QueryHandler, QueryResponseStatus};
|
||||
|
||||
/// Implementation of the `frame_support::traits::tokens::Pay` trait, to allow
|
||||
/// for XCM-based payments of a given `Balance` of some asset ID existing on some chain under
|
||||
/// ownership of some `Interior` location of the local chain to a particular `Beneficiary`. The
|
||||
/// `AssetKind` value can be converted into both the XCM `AssetId` (via and `Into` bound) and the
|
||||
/// the destination chain's location, via the `AssetKindToLocatableAsset` type parameter.
|
||||
///
|
||||
/// This relies on the XCM `TransferAsset` instruction. A trait `BeneficiaryRefToLocation` must be
|
||||
/// provided in order to convert the `Beneficiary` reference into a location usable by
|
||||
/// `TransferAsset`.
|
||||
///
|
||||
/// `PayOverXcm::pay` is asynchronous, and returns a `QueryId` which can then be used in
|
||||
/// `check_payment` to check the status of the XCM transaction.
|
||||
///
|
||||
/// See also `PayAccountId32OverXcm` which is similar to this except that `BeneficiaryRefToLocation`
|
||||
/// need not be supplied and `Beneficiary` must implement `Into<[u8; 32]>`.
|
||||
pub struct PayOverXcm<
|
||||
Interior,
|
||||
Router,
|
||||
Querier,
|
||||
Timeout,
|
||||
Beneficiary,
|
||||
AssetKind,
|
||||
AssetKindToLocatableAsset,
|
||||
BeneficiaryRefToLocation,
|
||||
>(
|
||||
PhantomData<(
|
||||
Interior,
|
||||
Router,
|
||||
Querier,
|
||||
Timeout,
|
||||
Beneficiary,
|
||||
AssetKind,
|
||||
AssetKindToLocatableAsset,
|
||||
BeneficiaryRefToLocation,
|
||||
)>,
|
||||
);
|
||||
impl<
|
||||
Interior: Get<InteriorMultiLocation>,
|
||||
Router: SendXcm,
|
||||
Querier: QueryHandler,
|
||||
Timeout: Get<Querier::BlockNumber>,
|
||||
Beneficiary: Clone,
|
||||
AssetKind,
|
||||
AssetKindToLocatableAsset: Convert<AssetKind, LocatableAssetId>,
|
||||
BeneficiaryRefToLocation: for<'a> Convert<&'a Beneficiary, MultiLocation>,
|
||||
> Pay
|
||||
for PayOverXcm<
|
||||
Interior,
|
||||
Router,
|
||||
Querier,
|
||||
Timeout,
|
||||
Beneficiary,
|
||||
AssetKind,
|
||||
AssetKindToLocatableAsset,
|
||||
BeneficiaryRefToLocation,
|
||||
>
|
||||
{
|
||||
type Beneficiary = Beneficiary;
|
||||
type AssetKind = AssetKind;
|
||||
type Balance = u128;
|
||||
type Id = Querier::QueryId;
|
||||
type Error = xcm::latest::Error;
|
||||
|
||||
fn pay(
|
||||
who: &Self::Beneficiary,
|
||||
asset_kind: Self::AssetKind,
|
||||
amount: Self::Balance,
|
||||
) -> Result<Self::Id, Self::Error> {
|
||||
let locatable = AssetKindToLocatableAsset::convert(asset_kind);
|
||||
let LocatableAssetId { asset_id, location: asset_location } = locatable;
|
||||
let destination = Querier::UniversalLocation::get()
|
||||
.invert_target(&asset_location)
|
||||
.map_err(|()| Self::Error::LocationNotInvertible)?;
|
||||
let beneficiary = BeneficiaryRefToLocation::convert(&who);
|
||||
|
||||
let query_id = Querier::new_query(asset_location, Timeout::get(), Interior::get());
|
||||
|
||||
let message = Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
SetAppendix(Xcm(vec![ReportError(QueryResponseInfo {
|
||||
destination,
|
||||
query_id,
|
||||
max_weight: Weight::zero(),
|
||||
})])),
|
||||
DescendOrigin(Interior::get()),
|
||||
TransferAsset {
|
||||
beneficiary,
|
||||
assets: vec![MultiAsset { id: asset_id, fun: Fungibility::Fungible(amount) }]
|
||||
.into(),
|
||||
},
|
||||
]);
|
||||
|
||||
let (ticket, _) = Router::validate(&mut Some(asset_location), &mut Some(message))?;
|
||||
Router::deliver(ticket)?;
|
||||
Ok(query_id.into())
|
||||
}
|
||||
|
||||
fn check_payment(id: Self::Id) -> PaymentStatus {
|
||||
use QueryResponseStatus::*;
|
||||
match Querier::take_response(id) {
|
||||
Ready { response, .. } => match response {
|
||||
Response::ExecutionResult(None) => PaymentStatus::Success,
|
||||
Response::ExecutionResult(Some(_)) => PaymentStatus::Failure,
|
||||
_ => PaymentStatus::Unknown,
|
||||
},
|
||||
Pending { .. } => PaymentStatus::InProgress,
|
||||
NotFound | UnexpectedVersion => PaymentStatus::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn ensure_successful(_: &Self::Beneficiary, _: Self::AssetKind, _: Self::Balance) {
|
||||
// We cannot generally guarantee this will go through successfully since we don't have any
|
||||
// control over the XCM transport layers. We just assume that the benchmark environment
|
||||
// will be sending it somewhere sensible.
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn ensure_concluded(id: Self::Id) {
|
||||
Querier::expect_response(id, Response::ExecutionResult(None));
|
||||
}
|
||||
}
|
||||
|
||||
/// Specialization of the [PayOverXcm] trait to allow `[u8; 32]`-based `AccountId` values to be
|
||||
/// paid on a remote chain.
|
||||
///
|
||||
/// Implementation of the [frame_support::traits::tokens::Pay] trait, to allow
|
||||
/// for XCM payments of a given `Balance` of `AssetKind` existing on a `DestinationChain` under
|
||||
/// ownership of some `Interior` location of the local chain to a particular `Beneficiary`.
|
||||
///
|
||||
/// This relies on the XCM `TransferAsset` instruction. `Beneficiary` must implement
|
||||
/// `Into<[u8; 32]>` (as 32-byte `AccountId`s generally do), and the actual XCM beneficiary will be
|
||||
/// the location consisting of a single `AccountId32` junction with an appropriate account and no
|
||||
/// specific network.
|
||||
///
|
||||
/// `PayOverXcm::pay` is asynchronous, and returns a `QueryId` which can then be used in
|
||||
/// `check_payment` to check the status of the XCM transaction.
|
||||
pub type PayAccountId32OnChainOverXcm<
|
||||
DestinationChain,
|
||||
Interior,
|
||||
Router,
|
||||
Querier,
|
||||
Timeout,
|
||||
Beneficiary,
|
||||
AssetKind,
|
||||
> = PayOverXcm<
|
||||
Interior,
|
||||
Router,
|
||||
Querier,
|
||||
Timeout,
|
||||
Beneficiary,
|
||||
AssetKind,
|
||||
crate::AliasesIntoAccountId32<(), Beneficiary>,
|
||||
FixedLocation<DestinationChain>,
|
||||
>;
|
||||
|
||||
/// Simple struct which contains both an XCM `location` and `asset_id` to identift an asset which
|
||||
/// exists on some chain.
|
||||
pub struct LocatableAssetId {
|
||||
/// The asset's ID.
|
||||
pub asset_id: AssetId,
|
||||
/// The (relative) location in which the asset ID is meaningful.
|
||||
pub location: MultiLocation,
|
||||
}
|
||||
|
||||
/// Adapter `struct` which implements a conversion from any `AssetKind` into a [LocatableAsset]
|
||||
/// value using a fixed `Location` for the `location` field.
|
||||
pub struct FixedLocation<Location>(sp_std::marker::PhantomData<Location>);
|
||||
impl<Location: Get<MultiLocation>, AssetKind: Into<AssetId>> Convert<AssetKind, LocatableAssetId>
|
||||
for FixedLocation<Location>
|
||||
{
|
||||
fn convert(value: AssetKind) -> LocatableAssetId {
|
||||
LocatableAssetId { asset_id: value.into(), location: Location::get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_builds() {}
|
||||
@@ -38,7 +38,7 @@ pub use token_matching::{
|
||||
Error, MatchesFungible, MatchesFungibles, MatchesNonFungible, MatchesNonFungibles,
|
||||
};
|
||||
mod on_response;
|
||||
pub use on_response::{OnResponse, VersionChangeNotifier};
|
||||
pub use on_response::{OnResponse, QueryHandler, QueryResponseStatus, VersionChangeNotifier};
|
||||
mod should_execute;
|
||||
pub use should_execute::{CheckSuspension, Properties, ShouldExecute};
|
||||
mod transact_asset;
|
||||
|
||||
@@ -14,8 +14,17 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::Xcm;
|
||||
use core::result;
|
||||
use frame_support::{
|
||||
dispatch::fmt::Debug,
|
||||
pallet_prelude::{Get, TypeInfo},
|
||||
};
|
||||
use parity_scale_codec::{FullCodec, MaxEncodedLen};
|
||||
use sp_arithmetic::traits::Zero;
|
||||
use xcm::latest::{
|
||||
Error as XcmError, MultiLocation, QueryId, Response, Result as XcmResult, Weight, XcmContext,
|
||||
Error as XcmError, InteriorMultiLocation, MultiLocation, QueryId, Response,
|
||||
Result as XcmResult, Weight, XcmContext,
|
||||
};
|
||||
|
||||
/// Define what needs to be done upon receiving a query response.
|
||||
@@ -94,3 +103,63 @@ impl VersionChangeNotifier for () {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// The possible state of an XCM query response.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum QueryResponseStatus<BlockNumber> {
|
||||
/// The response has arrived, and includes the inner Response and the block number it arrived at.
|
||||
Ready { response: Response, at: BlockNumber },
|
||||
/// The response has not yet arrived, the XCM might still be executing or the response might be in transit.
|
||||
Pending { timeout: BlockNumber },
|
||||
/// No response with the given `QueryId` was found, or the response was already queried and removed from local storage.
|
||||
NotFound,
|
||||
/// Got an unexpected XCM version.
|
||||
UnexpectedVersion,
|
||||
}
|
||||
|
||||
/// Provides methods to expect responses from XCMs and query their status.
|
||||
pub trait QueryHandler {
|
||||
type QueryId: From<u64>
|
||||
+ FullCodec
|
||||
+ MaxEncodedLen
|
||||
+ TypeInfo
|
||||
+ Clone
|
||||
+ Eq
|
||||
+ PartialEq
|
||||
+ Debug
|
||||
+ Copy;
|
||||
type BlockNumber: Zero;
|
||||
type Error;
|
||||
type UniversalLocation: Get<InteriorMultiLocation>;
|
||||
|
||||
/// Attempt to create a new query ID and register it as a query that is yet to respond.
|
||||
fn new_query(
|
||||
responder: impl Into<MultiLocation>,
|
||||
timeout: Self::BlockNumber,
|
||||
match_querier: impl Into<MultiLocation>,
|
||||
) -> QueryId;
|
||||
|
||||
/// Consume `message` and return another which is equivalent to it except that it reports
|
||||
/// back the outcome.
|
||||
///
|
||||
/// - `message`: The message whose outcome should be reported.
|
||||
/// - `responder`: The origin from which a response should be expected.
|
||||
/// - `timeout`: The block number after which it is permissible to return `NotFound` from `take_response`.
|
||||
///
|
||||
/// `report_outcome` may return an error if the `responder` is not invertible.
|
||||
///
|
||||
/// It is assumed that the querier of the response will be `Here`.
|
||||
/// The response can be queried with `take_response`.
|
||||
fn report_outcome(
|
||||
message: &mut Xcm<()>,
|
||||
responder: impl Into<MultiLocation>,
|
||||
timeout: Self::BlockNumber,
|
||||
) -> result::Result<Self::QueryId, Self::Error>;
|
||||
|
||||
/// Attempt to remove and return the response of query with ID `query_id`.
|
||||
fn take_response(id: Self::QueryId) -> QueryResponseStatus<Self::BlockNumber>;
|
||||
|
||||
/// Makes sure to expect a response with the given id.
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn expect_response(id: Self::QueryId, response: Response);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user