Skip to content

Commit e805395

Browse files
Add FungibleAdapter (#2684)
In the move from the old `Currency` traits to the new `fungible/s` family of traits, we already had the `FungiblesAdapter` and `NonFungiblesAdapter` for multiple fungible and non fungible assets respectively. However, for handling only one fungible asset, we were missing a `FungibleAdapter`, and so used the old `CurrencyAdapter` instead. This PR aims to fill in that gap, and provide the new adapter for more updated examples. I marked the old `CurrencyAdapter` as deprecated as part of this PR, and I'll change it to the new `FungibleAdapter` in a following PR. The two stages are separated so as to not bloat this PR with some name fixes in tests. --------- Co-authored-by: command-bot <>
1 parent 067bc40 commit e805395

File tree

10 files changed

+358
-23
lines changed

10 files changed

+358
-23
lines changed

pallet-xcm-benchmarks/src/fungible/mock.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ impl xcm_executor::traits::MatchesFungible<u64> for MatchAnyFungible {
103103
}
104104

105105
// Use balances as the asset transactor.
106+
#[allow(deprecated)]
106107
pub type AssetTransactor = xcm_builder::CurrencyAdapter<
107108
Balances,
108109
MatchAnyFungible,

pallet-xcm/src/mock.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ pub use sp_std::{
3232
cell::RefCell, collections::btree_map::BTreeMap, fmt::Debug, marker::PhantomData,
3333
};
3434
use xcm::prelude::*;
35+
#[allow(deprecated)]
36+
use xcm_builder::CurrencyAdapter as XcmCurrencyAdapter;
3537
use xcm_builder::{
3638
AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
3739
AllowTopLevelPaidExecutionFrom, Case, ChildParachainAsNative, ChildParachainConvertsVia,
38-
ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, DescribeAllTerminal,
39-
FixedRateOfFungible, FixedWeightBounds, FungiblesAdapter, HashedDescription, IsConcrete,
40-
MatchedConvertedConcreteId, NoChecking, SignedAccountId32AsNative, SignedToAccountId32,
41-
SovereignSignedViaLocation, TakeWeightCredit, XcmFeeManagerFromComponents, XcmFeeToAccount,
40+
ChildSystemParachainAsSuperuser, DescribeAllTerminal, FixedRateOfFungible, FixedWeightBounds,
41+
FungiblesAdapter, HashedDescription, IsConcrete, MatchedConvertedConcreteId, NoChecking,
42+
SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
43+
XcmFeeManagerFromComponents, XcmFeeToAccount,
4244
};
4345
use xcm_executor::{
4446
traits::{Identity, JustTry},
@@ -428,6 +430,7 @@ pub type ForeignAssetsConvertedConcreteId = MatchedConvertedConcreteId<
428430
JustTry,
429431
>;
430432

433+
#[allow(deprecated)]
431434
pub type AssetTransactors = (
432435
XcmCurrencyAdapter<Balances, IsConcrete<RelayLocation>, SovereignAccountOf, AccountId, ()>,
433436
FungiblesAdapter<

xcm-builder/src/currency_adapter.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
//! Adapters to work with `frame_support::traits::Currency` through XCM.
1818
19+
#![allow(deprecated)]
20+
1921
use super::MintLocation;
2022
use frame_support::traits::{ExistenceRequirement::AllowDeath, Get, WithdrawReasons};
2123
use sp_runtime::traits::CheckedSub;
@@ -85,6 +87,7 @@ impl From<Error> for XcmError {
8587
/// CheckingAccount,
8688
/// >;
8789
/// ```
90+
#[deprecated = "Use `FungibleAdapter` instead"]
8891
pub struct CurrencyAdapter<Currency, Matcher, AccountIdConverter, AccountId, CheckedAccount>(
8992
PhantomData<(Currency, Matcher, AccountIdConverter, AccountId, CheckedAccount)>,
9093
);
Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
// Copyright (C) Parity Technologies (UK) Ltd.
2+
// This file is part of Polkadot.
3+
4+
// Polkadot is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
9+
// Polkadot is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
14+
// You should have received a copy of the GNU General Public License
15+
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
16+
17+
//! Adapters to work with [`frame_support::traits::fungible`] through XCM.
18+
19+
use super::MintLocation;
20+
use frame_support::traits::{
21+
tokens::{
22+
fungible, Fortitude::Polite, Precision::Exact, Preservation::Preserve, Provenance::Minted,
23+
},
24+
Get,
25+
};
26+
use sp_std::{marker::PhantomData, prelude::*, result};
27+
use xcm::latest::prelude::*;
28+
use xcm_executor::traits::{ConvertLocation, Error as MatchError, MatchesFungible, TransactAsset};
29+
30+
/// [`TransactAsset`] implementation that allows the use of a [`fungible`] implementation for
31+
/// handling an asset in the XCM executor.
32+
/// Only works for transfers.
33+
pub struct FungibleTransferAdapter<Fungible, Matcher, AccountIdConverter, AccountId>(
34+
PhantomData<(Fungible, Matcher, AccountIdConverter, AccountId)>,
35+
);
36+
impl<
37+
Fungible: fungible::Mutate<AccountId>,
38+
Matcher: MatchesFungible<Fungible::Balance>,
39+
AccountIdConverter: ConvertLocation<AccountId>,
40+
AccountId: Eq + Clone,
41+
> TransactAsset for FungibleTransferAdapter<Fungible, Matcher, AccountIdConverter, AccountId>
42+
{
43+
fn internal_transfer_asset(
44+
what: &MultiAsset,
45+
from: &MultiLocation,
46+
to: &MultiLocation,
47+
_context: &XcmContext,
48+
) -> result::Result<xcm_executor::Assets, XcmError> {
49+
log::trace!(
50+
target: "xcm::fungible_adapter",
51+
"internal_transfer_asset what: {:?}, from: {:?}, to: {:?}",
52+
what, from, to
53+
);
54+
// Check we handle the asset
55+
let amount = Matcher::matches_fungible(what).ok_or(MatchError::AssetNotHandled)?;
56+
let source = AccountIdConverter::convert_location(from)
57+
.ok_or(MatchError::AccountIdConversionFailed)?;
58+
let dest = AccountIdConverter::convert_location(to)
59+
.ok_or(MatchError::AccountIdConversionFailed)?;
60+
Fungible::transfer(&source, &dest, amount, Preserve)
61+
.map_err(|error| XcmError::FailedToTransactAsset(error.into()))?;
62+
Ok(what.clone().into())
63+
}
64+
}
65+
66+
/// [`TransactAsset`] implementation that allows the use of a [`fungible`] implementation for
67+
/// handling an asset in the XCM executor.
68+
/// Works for everything but transfers.
69+
pub struct FungibleMutateAdapter<Fungible, Matcher, AccountIdConverter, AccountId, CheckingAccount>(
70+
PhantomData<(Fungible, Matcher, AccountIdConverter, AccountId, CheckingAccount)>,
71+
);
72+
73+
impl<
74+
Fungible: fungible::Mutate<AccountId>,
75+
Matcher: MatchesFungible<Fungible::Balance>,
76+
AccountIdConverter: ConvertLocation<AccountId>,
77+
AccountId: Eq + Clone,
78+
CheckingAccount: Get<Option<(AccountId, MintLocation)>>,
79+
> FungibleMutateAdapter<Fungible, Matcher, AccountIdConverter, AccountId, CheckingAccount>
80+
{
81+
fn can_accrue_checked(checking_account: AccountId, amount: Fungible::Balance) -> XcmResult {
82+
Fungible::can_deposit(&checking_account, amount, Minted)
83+
.into_result()
84+
.map_err(|_| XcmError::NotDepositable)
85+
}
86+
87+
fn can_reduce_checked(checking_account: AccountId, amount: Fungible::Balance) -> XcmResult {
88+
Fungible::can_withdraw(&checking_account, amount)
89+
.into_result(false)
90+
.map_err(|_| XcmError::NotWithdrawable)
91+
.map(|_| ())
92+
}
93+
94+
fn accrue_checked(checking_account: AccountId, amount: Fungible::Balance) {
95+
let ok = Fungible::mint_into(&checking_account, amount).is_ok();
96+
debug_assert!(ok, "`can_accrue_checked` must have returned `true` immediately prior; qed");
97+
}
98+
99+
fn reduce_checked(checking_account: AccountId, amount: Fungible::Balance) {
100+
let ok = Fungible::burn_from(&checking_account, amount, Exact, Polite).is_ok();
101+
debug_assert!(ok, "`can_reduce_checked` must have returned `true` immediately prior; qed");
102+
}
103+
}
104+
105+
impl<
106+
Fungible: fungible::Mutate<AccountId>,
107+
Matcher: MatchesFungible<Fungible::Balance>,
108+
AccountIdConverter: ConvertLocation<AccountId>,
109+
AccountId: Eq + Clone,
110+
CheckingAccount: Get<Option<(AccountId, MintLocation)>>,
111+
> TransactAsset
112+
for FungibleMutateAdapter<Fungible, Matcher, AccountIdConverter, AccountId, CheckingAccount>
113+
{
114+
fn can_check_in(
115+
_origin: &MultiLocation,
116+
what: &MultiAsset,
117+
_context: &XcmContext,
118+
) -> XcmResult {
119+
log::trace!(
120+
target: "xcm::fungible_adapter",
121+
"can_check_in origin: {:?}, what: {:?}",
122+
_origin, what
123+
);
124+
// Check we handle this asset
125+
let amount = Matcher::matches_fungible(what).ok_or(MatchError::AssetNotHandled)?;
126+
match CheckingAccount::get() {
127+
Some((checking_account, MintLocation::Local)) =>
128+
Self::can_reduce_checked(checking_account, amount),
129+
Some((checking_account, MintLocation::NonLocal)) =>
130+
Self::can_accrue_checked(checking_account, amount),
131+
None => Ok(()),
132+
}
133+
}
134+
135+
fn check_in(_origin: &MultiLocation, what: &MultiAsset, _context: &XcmContext) {
136+
log::trace!(
137+
target: "xcm::fungible_adapter",
138+
"check_in origin: {:?}, what: {:?}",
139+
_origin, what
140+
);
141+
if let Some(amount) = Matcher::matches_fungible(what) {
142+
match CheckingAccount::get() {
143+
Some((checking_account, MintLocation::Local)) =>
144+
Self::reduce_checked(checking_account, amount),
145+
Some((checking_account, MintLocation::NonLocal)) =>
146+
Self::accrue_checked(checking_account, amount),
147+
None => (),
148+
}
149+
}
150+
}
151+
152+
fn can_check_out(_dest: &MultiLocation, what: &MultiAsset, _context: &XcmContext) -> XcmResult {
153+
log::trace!(
154+
target: "xcm::fungible_adapter",
155+
"check_out dest: {:?}, what: {:?}",
156+
_dest,
157+
what
158+
);
159+
let amount = Matcher::matches_fungible(what).ok_or(MatchError::AssetNotHandled)?;
160+
match CheckingAccount::get() {
161+
Some((checking_account, MintLocation::Local)) =>
162+
Self::can_accrue_checked(checking_account, amount),
163+
Some((checking_account, MintLocation::NonLocal)) =>
164+
Self::can_reduce_checked(checking_account, amount),
165+
None => Ok(()),
166+
}
167+
}
168+
169+
fn check_out(_dest: &MultiLocation, what: &MultiAsset, _context: &XcmContext) {
170+
log::trace!(
171+
target: "xcm::fungible_adapter",
172+
"check_out dest: {:?}, what: {:?}",
173+
_dest,
174+
what
175+
);
176+
if let Some(amount) = Matcher::matches_fungible(what) {
177+
match CheckingAccount::get() {
178+
Some((checking_account, MintLocation::Local)) =>
179+
Self::accrue_checked(checking_account, amount),
180+
Some((checking_account, MintLocation::NonLocal)) =>
181+
Self::reduce_checked(checking_account, amount),
182+
None => (),
183+
}
184+
}
185+
}
186+
187+
fn deposit_asset(
188+
what: &MultiAsset,
189+
who: &MultiLocation,
190+
_context: Option<&XcmContext>,
191+
) -> XcmResult {
192+
log::trace!(
193+
target: "xcm::fungible_adapter",
194+
"deposit_asset what: {:?}, who: {:?}",
195+
what, who,
196+
);
197+
let amount = Matcher::matches_fungible(what).ok_or(MatchError::AssetNotHandled)?;
198+
let who = AccountIdConverter::convert_location(who)
199+
.ok_or(MatchError::AccountIdConversionFailed)?;
200+
Fungible::mint_into(&who, amount)
201+
.map_err(|error| XcmError::FailedToTransactAsset(error.into()))?;
202+
Ok(())
203+
}
204+
205+
fn withdraw_asset(
206+
what: &MultiAsset,
207+
who: &MultiLocation,
208+
_context: Option<&XcmContext>,
209+
) -> result::Result<xcm_executor::Assets, XcmError> {
210+
log::trace!(
211+
target: "xcm::fungible_adapter",
212+
"deposit_asset what: {:?}, who: {:?}",
213+
what, who,
214+
);
215+
let amount = Matcher::matches_fungible(what).ok_or(MatchError::AssetNotHandled)?;
216+
let who = AccountIdConverter::convert_location(who)
217+
.ok_or(MatchError::AccountIdConversionFailed)?;
218+
Fungible::burn_from(&who, amount, Exact, Polite)
219+
.map_err(|error| XcmError::FailedToTransactAsset(error.into()))?;
220+
Ok(what.clone().into())
221+
}
222+
}
223+
224+
/// [`TransactAsset`] implementation that allows the use of a [`fungible`] implementation for
225+
/// handling an asset in the XCM executor.
226+
/// Works for everything, transfers and teleport bookkeeping.
227+
pub struct FungibleAdapter<Fungible, Matcher, AccountIdConverter, AccountId, CheckingAccount>(
228+
PhantomData<(Fungible, Matcher, AccountIdConverter, AccountId, CheckingAccount)>,
229+
);
230+
impl<
231+
Fungible: fungible::Mutate<AccountId>,
232+
Matcher: MatchesFungible<Fungible::Balance>,
233+
AccountIdConverter: ConvertLocation<AccountId>,
234+
AccountId: Eq + Clone,
235+
CheckingAccount: Get<Option<(AccountId, MintLocation)>>,
236+
> TransactAsset
237+
for FungibleAdapter<Fungible, Matcher, AccountIdConverter, AccountId, CheckingAccount>
238+
{
239+
fn can_check_in(origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) -> XcmResult {
240+
FungibleMutateAdapter::<
241+
Fungible,
242+
Matcher,
243+
AccountIdConverter,
244+
AccountId,
245+
CheckingAccount,
246+
>::can_check_in(origin, what, context)
247+
}
248+
249+
fn check_in(origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) {
250+
FungibleMutateAdapter::<
251+
Fungible,
252+
Matcher,
253+
AccountIdConverter,
254+
AccountId,
255+
CheckingAccount,
256+
>::check_in(origin, what, context)
257+
}
258+
259+
fn can_check_out(dest: &MultiLocation, what: &MultiAsset, context: &XcmContext) -> XcmResult {
260+
FungibleMutateAdapter::<
261+
Fungible,
262+
Matcher,
263+
AccountIdConverter,
264+
AccountId,
265+
CheckingAccount,
266+
>::can_check_out(dest, what, context)
267+
}
268+
269+
fn check_out(dest: &MultiLocation, what: &MultiAsset, context: &XcmContext) {
270+
FungibleMutateAdapter::<
271+
Fungible,
272+
Matcher,
273+
AccountIdConverter,
274+
AccountId,
275+
CheckingAccount,
276+
>::check_out(dest, what, context)
277+
}
278+
279+
fn deposit_asset(
280+
what: &MultiAsset,
281+
who: &MultiLocation,
282+
context: Option<&XcmContext>,
283+
) -> XcmResult {
284+
FungibleMutateAdapter::<
285+
Fungible,
286+
Matcher,
287+
AccountIdConverter,
288+
AccountId,
289+
CheckingAccount,
290+
>::deposit_asset(what, who, context)
291+
}
292+
293+
fn withdraw_asset(
294+
what: &MultiAsset,
295+
who: &MultiLocation,
296+
maybe_context: Option<&XcmContext>,
297+
) -> result::Result<xcm_executor::Assets, XcmError> {
298+
FungibleMutateAdapter::<
299+
Fungible,
300+
Matcher,
301+
AccountIdConverter,
302+
AccountId,
303+
CheckingAccount,
304+
>::withdraw_asset(what, who, maybe_context)
305+
}
306+
307+
fn internal_transfer_asset(
308+
what: &MultiAsset,
309+
from: &MultiLocation,
310+
to: &MultiLocation,
311+
context: &XcmContext,
312+
) -> result::Result<xcm_executor::Assets, XcmError> {
313+
FungibleTransferAdapter::<Fungible, Matcher, AccountIdConverter, AccountId>::internal_transfer_asset(
314+
what, from, to, context
315+
)
316+
}
317+
}

xcm-builder/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,17 @@ mod process_xcm_message;
6565
pub use process_xcm_message::ProcessXcmMessage;
6666

6767
mod currency_adapter;
68+
#[allow(deprecated)]
6869
pub use currency_adapter::CurrencyAdapter;
6970

7071
mod fee_handling;
7172
pub use fee_handling::{
7273
deposit_or_burn_fee, HandleFee, XcmFeeManagerFromComponents, XcmFeeToAccount,
7374
};
7475

76+
mod fungible_adapter;
77+
pub use fungible_adapter::{FungibleAdapter, FungibleMutateAdapter, FungibleTransferAdapter};
78+
7579
mod fungibles_adapter;
7680
pub use fungibles_adapter::{
7781
AssetChecking, DualMint, FungiblesAdapter, FungiblesMutateAdapter, FungiblesTransferAdapter,

0 commit comments

Comments
 (0)