4c8f281051
- Remove nightly-only features from .rustfmt.toml and vendor/ss58-registry/rustfmt.toml - Removed features: imports_granularity, wrap_comments, comment_width, reorder_impl_items, spaces_around_ranges, binop_separator, match_arm_blocks, trailing_semicolon, trailing_comma - Format all 898 affected files with stable rustfmt - Ensures long-term reliability without nightly toolchain dependency
256 lines
9.5 KiB
Rust
256 lines
9.5 KiB
Rust
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// This file is part of Pezkuwi.
|
|
|
|
// Pezkuwi 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.
|
|
|
|
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
//! Adapters to work with `pezframe_support::traits::Currency` through XCM.
|
|
|
|
#![allow(deprecated)]
|
|
|
|
use super::MintLocation;
|
|
use core::{fmt::Debug, marker::PhantomData, result};
|
|
use pezframe_support::traits::{ExistenceRequirement::AllowDeath, Get, WithdrawReasons};
|
|
use pezsp_runtime::traits::CheckedSub;
|
|
use xcm::latest::{Asset, Error as XcmError, Location, Result, XcmContext};
|
|
use xcm_executor::{
|
|
traits::{ConvertLocation, MatchesFungible, TransactAsset},
|
|
AssetsInHolding,
|
|
};
|
|
|
|
/// Asset transaction errors.
|
|
enum Error {
|
|
/// The given asset is not handled. (According to [`XcmError::AssetNotFound`])
|
|
AssetNotHandled,
|
|
/// `Location` to `AccountId` conversion failed.
|
|
AccountIdConversionFailed,
|
|
}
|
|
|
|
impl From<Error> for XcmError {
|
|
fn from(e: Error) -> Self {
|
|
use XcmError::FailedToTransactAsset;
|
|
match e {
|
|
Error::AssetNotHandled => XcmError::AssetNotFound,
|
|
Error::AccountIdConversionFailed => FailedToTransactAsset("AccountIdConversionFailed"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Simple adapter to use a currency as asset transactor. This type can be used as `type
|
|
/// AssetTransactor` in `xcm::Config`.
|
|
///
|
|
/// # Example
|
|
/// ```
|
|
/// use codec::Decode;
|
|
/// use pezframe_support::{parameter_types, PalletId};
|
|
/// use pezsp_runtime::traits::{AccountIdConversion, TrailingZeroInput};
|
|
/// use xcm::latest::prelude::*;
|
|
/// use pezstaging_xcm_builder::{ParentIsPreset, CurrencyAdapter, IsConcrete};
|
|
///
|
|
/// /// Our chain's account id.
|
|
/// type AccountId = pezsp_runtime::AccountId32;
|
|
///
|
|
/// /// Our relay chain's location.
|
|
/// parameter_types! {
|
|
/// pub RelayChain: Location = Parent.into();
|
|
/// pub CheckingAccount: AccountId = PalletId(*b"checking").into_account_truncating();
|
|
/// }
|
|
///
|
|
/// /// Some items that implement `ConvertLocation<AccountId>`. Can be more, but for now we just assume we accept
|
|
/// /// messages from the parent (relay chain).
|
|
/// pub type LocationConverter = (ParentIsPreset<AccountId>);
|
|
///
|
|
/// /// Just a dummy implementation of `Currency`. Normally this would be `Balances`.
|
|
/// pub type CurrencyImpl = ();
|
|
///
|
|
/// /// Final currency adapter. This can be used in `xcm::Config` to specify how asset related transactions happen.
|
|
/// pub type AssetTransactor = CurrencyAdapter<
|
|
/// // Use this `Currency` impl instance:
|
|
/// CurrencyImpl,
|
|
/// // The matcher: use the currency when the asset is a concrete asset in our relay chain.
|
|
/// IsConcrete<RelayChain>,
|
|
/// // The local converter: default account of the parent relay chain.
|
|
/// LocationConverter,
|
|
/// // Our chain's account ID type.
|
|
/// AccountId,
|
|
/// // The checking account. Can be any deterministic inaccessible account.
|
|
/// CheckingAccount,
|
|
/// >;
|
|
/// ```
|
|
#[deprecated = "Use `FungibleAdapter` instead"]
|
|
pub struct CurrencyAdapter<Currency, Matcher, AccountIdConverter, AccountId, CheckedAccount>(
|
|
PhantomData<(Currency, Matcher, AccountIdConverter, AccountId, CheckedAccount)>,
|
|
);
|
|
|
|
impl<
|
|
Currency: pezframe_support::traits::Currency<AccountId>,
|
|
Matcher: MatchesFungible<Currency::Balance>,
|
|
AccountIdConverter: ConvertLocation<AccountId>,
|
|
AccountId: Clone, // can't get away without it since Currency is generic over it.
|
|
CheckedAccount: Get<Option<(AccountId, MintLocation)>>,
|
|
> CurrencyAdapter<Currency, Matcher, AccountIdConverter, AccountId, CheckedAccount>
|
|
{
|
|
fn can_accrue_checked(_checked_account: AccountId, _amount: Currency::Balance) -> Result {
|
|
Ok(())
|
|
}
|
|
fn can_reduce_checked(checked_account: AccountId, amount: Currency::Balance) -> Result {
|
|
let new_balance = Currency::free_balance(&checked_account)
|
|
.checked_sub(&amount)
|
|
.ok_or(XcmError::NotWithdrawable)?;
|
|
Currency::ensure_can_withdraw(
|
|
&checked_account,
|
|
amount,
|
|
WithdrawReasons::TRANSFER,
|
|
new_balance,
|
|
)
|
|
.map_err(|error| {
|
|
tracing::debug!(target: "xcm::currency_adapter", ?error, "Failed to ensure can withdraw");
|
|
XcmError::NotWithdrawable
|
|
})
|
|
}
|
|
fn accrue_checked(checked_account: AccountId, amount: Currency::Balance) {
|
|
let _ = Currency::deposit_creating(&checked_account, amount);
|
|
Currency::deactivate(amount);
|
|
}
|
|
fn reduce_checked(checked_account: AccountId, amount: Currency::Balance) {
|
|
let ok =
|
|
Currency::withdraw(&checked_account, amount, WithdrawReasons::TRANSFER, AllowDeath)
|
|
.is_ok();
|
|
if ok {
|
|
Currency::reactivate(amount);
|
|
} else {
|
|
pezframe_support::defensive!(
|
|
"`can_check_in` must have returned `true` immediately prior; qed"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<
|
|
Currency: pezframe_support::traits::Currency<AccountId>,
|
|
Matcher: MatchesFungible<Currency::Balance>,
|
|
AccountIdConverter: ConvertLocation<AccountId>,
|
|
AccountId: Clone + Debug, // can't get away without it since Currency is generic over it.
|
|
CheckedAccount: Get<Option<(AccountId, MintLocation)>>,
|
|
> TransactAsset
|
|
for CurrencyAdapter<Currency, Matcher, AccountIdConverter, AccountId, CheckedAccount>
|
|
{
|
|
fn can_check_in(origin: &Location, what: &Asset, _context: &XcmContext) -> Result {
|
|
tracing::trace!(target: "xcm::currency_adapter", ?origin, ?what, "can_check_in origin");
|
|
// Check we handle this asset.
|
|
let amount: Currency::Balance =
|
|
Matcher::matches_fungible(what).ok_or(Error::AssetNotHandled)?;
|
|
match CheckedAccount::get() {
|
|
Some((checked_account, MintLocation::Local)) => {
|
|
Self::can_reduce_checked(checked_account, amount)
|
|
},
|
|
Some((checked_account, MintLocation::NonLocal)) => {
|
|
Self::can_accrue_checked(checked_account, amount)
|
|
},
|
|
None => Ok(()),
|
|
}
|
|
}
|
|
|
|
fn check_in(origin: &Location, what: &Asset, _context: &XcmContext) {
|
|
tracing::trace!(target: "xcm::currency_adapter", ?origin, ?what, "check_in origin");
|
|
if let Some(amount) = Matcher::matches_fungible(what) {
|
|
match CheckedAccount::get() {
|
|
Some((checked_account, MintLocation::Local)) => {
|
|
Self::reduce_checked(checked_account, amount)
|
|
},
|
|
Some((checked_account, MintLocation::NonLocal)) => {
|
|
Self::accrue_checked(checked_account, amount)
|
|
},
|
|
None => (),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn can_check_out(dest: &Location, what: &Asset, _context: &XcmContext) -> Result {
|
|
tracing::trace!(target: "xcm::currency_adapter", ?dest, ?what, "can_check_out");
|
|
let amount = Matcher::matches_fungible(what).ok_or(Error::AssetNotHandled)?;
|
|
match CheckedAccount::get() {
|
|
Some((checked_account, MintLocation::Local)) => {
|
|
Self::can_accrue_checked(checked_account, amount)
|
|
},
|
|
Some((checked_account, MintLocation::NonLocal)) => {
|
|
Self::can_reduce_checked(checked_account, amount)
|
|
},
|
|
None => Ok(()),
|
|
}
|
|
}
|
|
|
|
fn check_out(dest: &Location, what: &Asset, _context: &XcmContext) {
|
|
tracing::trace!(target: "xcm::currency_adapter", ?dest, ?what, "check_out");
|
|
if let Some(amount) = Matcher::matches_fungible(what) {
|
|
match CheckedAccount::get() {
|
|
Some((checked_account, MintLocation::Local)) => {
|
|
Self::accrue_checked(checked_account, amount)
|
|
},
|
|
Some((checked_account, MintLocation::NonLocal)) => {
|
|
Self::reduce_checked(checked_account, amount)
|
|
},
|
|
None => (),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn deposit_asset(what: &Asset, who: &Location, _context: Option<&XcmContext>) -> Result {
|
|
tracing::trace!(target: "xcm::currency_adapter", ?what, ?who, "deposit_asset");
|
|
// Check we handle this asset.
|
|
let amount = Matcher::matches_fungible(&what).ok_or(Error::AssetNotHandled)?;
|
|
let who =
|
|
AccountIdConverter::convert_location(who).ok_or(Error::AccountIdConversionFailed)?;
|
|
let _imbalance = Currency::deposit_creating(&who, amount);
|
|
Ok(())
|
|
}
|
|
|
|
fn withdraw_asset(
|
|
what: &Asset,
|
|
who: &Location,
|
|
_maybe_context: Option<&XcmContext>,
|
|
) -> result::Result<AssetsInHolding, XcmError> {
|
|
tracing::trace!(target: "xcm::currency_adapter", ?what, ?who, "withdraw_asset");
|
|
// Check we handle this asset.
|
|
let amount = Matcher::matches_fungible(what).ok_or(Error::AssetNotHandled)?;
|
|
let who =
|
|
AccountIdConverter::convert_location(who).ok_or(Error::AccountIdConversionFailed)?;
|
|
let _ = Currency::withdraw(&who, amount, WithdrawReasons::TRANSFER, AllowDeath).map_err(
|
|
|error| {
|
|
tracing::debug!(target: "xcm::currency_adapter", ?error, ?who, ?amount, "Failed to withdraw asset");
|
|
XcmError::FailedToTransactAsset(error.into())
|
|
},
|
|
)?;
|
|
Ok(what.clone().into())
|
|
}
|
|
|
|
fn internal_transfer_asset(
|
|
asset: &Asset,
|
|
from: &Location,
|
|
to: &Location,
|
|
_context: &XcmContext,
|
|
) -> result::Result<AssetsInHolding, XcmError> {
|
|
tracing::trace!(target: "xcm::currency_adapter", ?asset, ?from, ?to, "internal_transfer_asset");
|
|
let amount = Matcher::matches_fungible(asset).ok_or(Error::AssetNotHandled)?;
|
|
let from =
|
|
AccountIdConverter::convert_location(from).ok_or(Error::AccountIdConversionFailed)?;
|
|
let to =
|
|
AccountIdConverter::convert_location(to).ok_or(Error::AccountIdConversionFailed)?;
|
|
Currency::transfer(&from, &to, amount, AllowDeath).map_err(|error| {
|
|
tracing::debug!(target: "xcm::currency_adapter", ?error, ?from, ?to, ?amount, "Failed to transfer asset");
|
|
XcmError::FailedToTransactAsset(error.into())
|
|
})?;
|
|
Ok(asset.clone().into())
|
|
}
|
|
}
|