Implement transfer_asset on CurrencyAdapter (#4549)

* Implement transfer_asset on CurrencyAdapter

* Use Currency::transfer in transfer_asset

* Add a test to assert that Currency::transfer was indeed used

* Remove superfluous balance conversion to u128

* Rename transfer_asset and beam_asset

Co-authored-by: Giles Cope <gilescope@gmail.com>
This commit is contained in:
Keith Yeung
2022-03-29 12:23:18 +02:00
committed by GitHub
parent 9fdc059363
commit 0e3f36224a
6 changed files with 80 additions and 34 deletions
@@ -17,8 +17,8 @@
//! Adapters to work with `frame_support::traits::Currency` through XCM.
use frame_support::traits::{ExistenceRequirement::AllowDeath, Get, WithdrawReasons};
use sp_runtime::traits::{CheckedSub, SaturatedConversion};
use sp_std::{convert::TryInto, marker::PhantomData, result};
use sp_runtime::traits::CheckedSub;
use sp_std::{marker::PhantomData, result};
use xcm::latest::{Error as XcmError, MultiAsset, MultiLocation, Result};
use xcm_executor::{
traits::{Convert, MatchesFungible, TransactAsset},
@@ -31,8 +31,6 @@ enum Error {
AssetNotFound,
/// `MultiLocation` to `AccountId` conversion failed.
AccountIdConversionFailed,
/// `u128` amount to currency `Balance` conversion failed.
AmountToBalanceConversionFailed,
}
impl From<Error> for XcmError {
@@ -41,8 +39,6 @@ impl From<Error> for XcmError {
match e {
Error::AssetNotFound => XcmError::AssetNotFound,
Error::AccountIdConversionFailed => FailedToTransactAsset("AccountIdConversionFailed"),
Error::AmountToBalanceConversionFailed =>
FailedToTransactAsset("AmountToBalanceConversionFailed"),
}
}
}
@@ -149,27 +145,37 @@ impl<
fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> Result {
log::trace!(target: "xcm::currency_adapter", "deposit_asset what: {:?}, who: {:?}", what, who);
// Check we handle this asset.
let amount: u128 =
Matcher::matches_fungible(&what).ok_or(Error::AssetNotFound)?.saturated_into();
let amount = Matcher::matches_fungible(&what).ok_or(Error::AssetNotFound)?;
let who =
AccountIdConverter::convert_ref(who).map_err(|()| Error::AccountIdConversionFailed)?;
let balance_amount =
amount.try_into().map_err(|_| Error::AmountToBalanceConversionFailed)?;
let _imbalance = Currency::deposit_creating(&who, balance_amount);
let _imbalance = Currency::deposit_creating(&who, amount);
Ok(())
}
fn withdraw_asset(what: &MultiAsset, who: &MultiLocation) -> result::Result<Assets, XcmError> {
log::trace!(target: "xcm::currency_adapter", "withdraw_asset what: {:?}, who: {:?}", what, who);
// Check we handle this asset.
let amount: u128 =
Matcher::matches_fungible(what).ok_or(Error::AssetNotFound)?.saturated_into();
let amount = Matcher::matches_fungible(what).ok_or(Error::AssetNotFound)?;
let who =
AccountIdConverter::convert_ref(who).map_err(|()| Error::AccountIdConversionFailed)?;
let balance_amount =
amount.try_into().map_err(|_| Error::AmountToBalanceConversionFailed)?;
Currency::withdraw(&who, balance_amount, WithdrawReasons::TRANSFER, AllowDeath)
Currency::withdraw(&who, amount, WithdrawReasons::TRANSFER, AllowDeath)
.map_err(|e| XcmError::FailedToTransactAsset(e.into()))?;
Ok(what.clone().into())
}
fn internal_transfer_asset(
asset: &MultiAsset,
from: &MultiLocation,
to: &MultiLocation,
) -> result::Result<Assets, XcmError> {
log::trace!(target: "xcm::currency_adapter", "internal_transfer_asset asset: {:?}, from: {:?}, to: {:?}", asset, from, to);
let amount = Matcher::matches_fungible(asset).ok_or(Error::AssetNotFound)?;
let from =
AccountIdConverter::convert_ref(from).map_err(|()| Error::AccountIdConversionFailed)?;
let to =
AccountIdConverter::convert_ref(to).map_err(|()| Error::AccountIdConversionFailed)?;
Currency::transfer(&from, &to, amount, AllowDeath)
.map_err(|e| XcmError::FailedToTransactAsset(e.into()))?;
Ok(asset.clone().into())
}
}
@@ -118,14 +118,14 @@ impl<
AccountId: Clone, // can't get away without it since Currency is generic over it.
> TransactAsset for FungiblesTransferAdapter<Assets, Matcher, AccountIdConverter, AccountId>
{
fn transfer_asset(
fn internal_transfer_asset(
what: &MultiAsset,
from: &MultiLocation,
to: &MultiLocation,
) -> result::Result<xcm_executor::Assets, XcmError> {
log::trace!(
target: "xcm::fungibles_adapter",
"transfer_asset what: {:?}, from: {:?}, to: {:?}",
"internal_transfer_asset what: {:?}, from: {:?}, to: {:?}",
what, from, to
);
// Check we handle this asset.
@@ -325,12 +325,12 @@ impl<
>::withdraw_asset(what, who)
}
fn transfer_asset(
fn internal_transfer_asset(
what: &MultiAsset,
from: &MultiLocation,
to: &MultiLocation,
) -> result::Result<xcm_executor::Assets, XcmError> {
FungiblesTransferAdapter::<Assets, Matcher, AccountIdConverter, AccountId>::transfer_asset(
FungiblesTransferAdapter::<Assets, Matcher, AccountIdConverter, AccountId>::internal_transfer_asset(
what, from, to,
)
}
+3 -1
View File
@@ -123,7 +123,7 @@ parameter_types! {
pub type SovereignAccountOf =
(ChildParachainConvertsVia<ParaId, AccountId>, AccountId32Aliases<KusamaNetwork, AccountId>);
pub type LocalAssetTransactor = XcmCurrencyAdapter<
pub type LocalCurrencyAdapter = XcmCurrencyAdapter<
Balances,
IsConcrete<KsmLocation>,
SovereignAccountOf,
@@ -131,6 +131,8 @@ pub type LocalAssetTransactor = XcmCurrencyAdapter<
CheckAccount,
>;
pub type LocalAssetTransactor = (LocalCurrencyAdapter,);
type LocalOriginConverter = (
SovereignSignedViaLocation<SovereignAccountOf, Origin>,
ChildParachainAsNative<origin::Origin, Origin>,
+32 -1
View File
@@ -17,7 +17,8 @@
mod mock;
use mock::{
kusama_like_with_balances, AccountId, Balance, Balances, BaseXcmWeight, XcmConfig, CENTS,
kusama_like_with_balances, AccountId, Balance, Balances, BaseXcmWeight, System, XcmConfig,
CENTS,
};
use polkadot_parachain::primitives::Id as ParaId;
use sp_runtime::traits::AccountIdConversion;
@@ -66,6 +67,36 @@ fn withdraw_and_deposit_works() {
});
}
/// Scenario:
/// Alice simply wants to transfer funds to Bob's account via XCM.
///
/// Asserts that the balances are updated correctly and the correct events are fired.
#[test]
fn transfer_asset_works() {
let bob = AccountId::new([1u8; 32]);
let balances = vec![(ALICE, INITIAL_BALANCE), (bob.clone(), INITIAL_BALANCE)];
kusama_like_with_balances(balances).execute_with(|| {
let amount = REGISTER_AMOUNT;
let weight = BaseXcmWeight::get();
// Use `execute_xcm_in_credit` here to pass through the barrier
let r = XcmExecutor::<XcmConfig>::execute_xcm_in_credit(
AccountId32 { network: NetworkId::Any, id: ALICE.into() },
Xcm(vec![TransferAsset {
assets: (Here, amount).into(),
beneficiary: AccountId32 { network: NetworkId::Any, id: bob.clone().into() }.into(),
}]),
weight,
weight,
);
System::assert_last_event(
pallet_balances::Event::Transfer { from: ALICE, to: bob.clone(), amount }.into(),
);
assert_eq!(r, Outcome::Complete(weight));
assert_eq!(Balances::free_balance(ALICE), INITIAL_BALANCE - amount);
assert_eq!(Balances::free_balance(bob), INITIAL_BALANCE + amount);
});
}
/// Scenario:
/// A parachain wants to be notified that a transfer worked correctly.
/// It includes a `QueryHolding` order after the deposit to get notified on success.