Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Statemint Reserve Asset Transfer (#552)
Browse files Browse the repository at this point in the history
* add AssetId type alias in statemint-common

* add FungiblesAdapter to allow for asset transactions via XCM

* use custom Polkadot

* fix FungiblesAdapter usage and add CheckingAccount

* update deps

* remove polkadot overrides

* update deps

* pull NonZeroIssuance struct into common + add FungiblesTransactor to Statemine and Westmint

* remove unnecessary tuple wrapping + adjust asset transactor comments

* accept statemint as reserve in rococo test parachain

* adjust parachain config (add Statemint as reserve)

* add test and docs for AssetsFrom

* cargo fmt

Co-authored-by: Ricardo Rius <ricardo@parity.io>
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
  • Loading branch information
3 people committed Oct 5, 2021
1 parent edd029b commit caa8b81
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 51 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions polkadot-parachains/parachains-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ node-primitives = { git = 'https://github.com/paritytech/substrate', branch = "m
# Polkadot dependencies
polkadot-runtime-common = { git = 'https://github.com/paritytech/polkadot', branch = "master", default-features = false }
polkadot-primitives = { git = 'https://github.com/paritytech/polkadot', branch = "master", default-features = false }
xcm = { git = 'https://github.com/paritytech/polkadot', branch = "master", default-features = false }
xcm-executor = { git = 'https://github.com/paritytech/polkadot', branch = "master", default-features = false }

# Local dependencies
pallet-collator-selection = { path = '../../pallets/collator-selection', default-features = false }
Expand Down
51 changes: 50 additions & 1 deletion polkadot-parachains/parachains-common/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
//! Auxillary struct/enums for parachain runtimes.
//! Taken from polkadot/runtime/common (at a21cd64) and adapted for parachains.

use frame_support::traits::{Currency, Imbalance, OnUnbalanced};
use frame_support::traits::{fungibles, Contains, Currency, Get, Imbalance, OnUnbalanced};
use sp_runtime::traits::Zero;
use sp_std::marker::PhantomData;
use xcm::latest::{AssetId, Fungibility::Fungible, MultiAsset, MultiLocation};
use xcm_executor::traits::FilterAssetLocation;

pub type NegativeImbalance<T> = <pallet_balances::Pallet<T> as Currency<
<T as frame_system::Config>::AccountId,
Expand All @@ -42,6 +46,7 @@ where
}
}

/// Merge the fees into one item and pass them on to the staking pot.
pub struct DealWithFees<R>(sp_std::marker::PhantomData<R>);
impl<R> OnUnbalanced<NegativeImbalance<R>> for DealWithFees<R>
where
Expand All @@ -60,6 +65,29 @@ where
}
}

/// Allow checking in assets that have issuance > 0.
pub struct NonZeroIssuance<AccountId, Assets>(PhantomData<(AccountId, Assets)>);
impl<AccountId, Assets> Contains<<Assets as fungibles::Inspect<AccountId>>::AssetId>
for NonZeroIssuance<AccountId, Assets>
where
Assets: fungibles::Inspect<AccountId>,
{
fn contains(id: &<Assets as fungibles::Inspect<AccountId>>::AssetId) -> bool {
!Assets::total_issuance(*id).is_zero()
}
}

/// Asset filter that allows all assets from a certain location.
pub struct AssetsFrom<T>(PhantomData<T>);
impl<T: Get<MultiLocation>> FilterAssetLocation for AssetsFrom<T> {
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
let loc = T::get();
&loc == origin &&
matches!(asset, MultiAsset { id: AssetId::Concrete(asset_loc), fun: Fungible(_a) }
if asset_loc.match_and_split(&loc).is_some())
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -77,6 +105,7 @@ mod tests {
traits::{BlakeTwo256, IdentityLookup},
Perbill,
};
use xcm::prelude::*;

type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;
Expand Down Expand Up @@ -207,4 +236,24 @@ mod tests {
assert_eq!(Balances::free_balance(CollatorSelection::account_id()), 30);
});
}

#[test]
fn assets_from_filters_correctly() {
parameter_types! {
pub SomeSiblingParachain: MultiLocation = MultiLocation::new(1, X1(Parachain(1234)));
}

let asset_location = SomeSiblingParachain::get()
.clone()
.pushed_with_interior(GeneralIndex(42))
.expect("multilocation will only have 2 junctions; qed");
let asset = MultiAsset { id: Concrete(asset_location), fun: 1_000_000.into() };
assert!(
AssetsFrom::<SomeSiblingParachain>::filter_asset_location(
&asset,
&SomeSiblingParachain::get()
),
"AssetsFrom should allow assets from any of its interior locations"
);
}
}
3 changes: 3 additions & 0 deletions polkadot-parachains/parachains-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ mod types {

// Aura consensus authority.
pub type AuraId = sp_consensus_aura::sr25519::AuthorityId;

// Id used for identifying assets.
pub type AssetId = u32;
}

/// Common constants of parachains.
Expand Down
2 changes: 2 additions & 0 deletions polkadot-parachains/rococo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ pallet-transaction-payment = { git = "https://github.com/paritytech/substrate",
pallet-aura = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }

parachains-common = { path = "../parachains-common", default-features = false }

# Cumulus dependencies
cumulus-pallet-aura-ext = { path = "../../pallets/aura-ext", default-features = false }
cumulus-pallet-parachain-system = { path = "../../pallets/parachain-system", default-features = false }
Expand Down
63 changes: 57 additions & 6 deletions polkadot-parachains/rococo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,24 @@ pub use frame_support::{
},
StorageValue,
};
use frame_system::limits::{BlockLength, BlockWeights};
use frame_system::{
limits::{BlockLength, BlockWeights},
EnsureOneOf, EnsureRoot,
};
pub use pallet_balances::Call as BalancesCall;
pub use pallet_timestamp::Call as TimestampCall;
pub use sp_consensus_aura::sr25519::AuthorityId as AuraId;
#[cfg(any(feature = "std", test))]
pub use sp_runtime::BuildStorage;
pub use sp_runtime::{Perbill, Permill};

use parachains_common::{
impls::{AssetsFrom, NonZeroIssuance},
AssetId,
};
use xcm_builder::{AsPrefixedGeneralIndex, ConvertedConcreteAssetId, FungiblesAdapter};
use xcm_executor::traits::JustTry;

// XCM imports
use pallet_xcm::{EnsureXcm, IsMajorityOfBody, XcmPassthrough};
use polkadot_parachain::primitives::Sibling;
Expand Down Expand Up @@ -258,6 +268,7 @@ parameter_types! {
pub const RococoNetwork: NetworkId = NetworkId::Polkadot;
pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into();
pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into();
pub CheckingAccount: AccountId = PolkadotXcm::check_account();
}

/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used
Expand All @@ -273,7 +284,7 @@ pub type LocationToAccountId = (
);

/// Means for transacting assets on this chain.
pub type LocalAssetTransactor = CurrencyAdapter<
pub type CurrencyTransactor = CurrencyAdapter<
// Use this currency:
Balances,
// Use this currency when it is a fungible asset matching the given location or name:
Expand All @@ -286,6 +297,30 @@ pub type LocalAssetTransactor = CurrencyAdapter<
(),
>;

/// Means for transacting assets besides the native currency on this chain.
pub type FungiblesTransactor = FungiblesAdapter<
// Use this fungibles implementation:
Assets,
// Use this currency when it is a fungible asset matching the given location or name:
ConvertedConcreteAssetId<
AssetId,
u64,
AsPrefixedGeneralIndex<StatemintLocation, AssetId, JustTry>,
JustTry,
>,
// Convert an XCM MultiLocation into a local account id:
LocationToAccountId,
// Our chain's account ID type (we can't get away without mentioning it explicitly):
AccountId,
// We only want to allow teleports of known assets. We use non-zero issuance as an indication
// that this asset is known.
NonZeroIssuance<AccountId, Assets>,
// The account to use for tracking teleports.
CheckingAccount,
>;
/// Means for transacting assets on this chain.
pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor);

/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance,
/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can
/// biases the kind of local `Origin` it will become.
Expand Down Expand Up @@ -324,22 +359,34 @@ match_type! {
MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Unit, .. }) }
};
}
match_type! {
pub type Statemint: impl Contains<MultiLocation> = {
MultiLocation { parents: 1, interior: X1(Parachain(1000)) }
};
}

pub type Barrier = (
TakeWeightCredit,
AllowTopLevelPaidExecutionFrom<Everything>,
AllowUnpaidExecutionFrom<ParentOrParentsUnitPlurality>,
// ^^^ Parent & its unit plurality gets free execution
AllowUnpaidExecutionFrom<Statemint>,
);

parameter_types! {
pub StatemintLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(1000)));
}

pub type Reserves = (NativeAsset, AssetsFrom<StatemintLocation>);

pub struct XcmConfig;
impl Config for XcmConfig {
type Call = Call;
type XcmSender = XcmRouter;
// How to withdraw and deposit an asset.
type AssetTransactor = LocalAssetTransactor;
type AssetTransactor = AssetTransactors;
type OriginConverter = XcmOriginToTransactDispatchOrigin;
type IsReserve = NativeAsset;
type IsReserve = Reserves;
type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of ROC
type LocationInverter = LocationInverter<Ancestry>;
type Barrier = Barrier;
Expand Down Expand Up @@ -416,12 +463,16 @@ parameter_types! {
}

/// A majority of the Unit body from Rococo over XCM is our required administration origin.
pub type AdminOrigin = EnsureXcm<IsMajorityOfBody<RocLocation, UnitBody>>;
pub type AdminOrigin = EnsureOneOf<
AccountId,
EnsureRoot<AccountId>,
EnsureXcm<IsMajorityOfBody<RocLocation, UnitBody>>,
>;

impl pallet_assets::Config for Runtime {
type Event = Event;
type Balance = u64;
type AssetId = u32;
type AssetId = AssetId;
type Currency = Balances;
type ForceOrigin = AdminOrigin;
type AssetDeposit = AssetDeposit;
Expand Down
54 changes: 40 additions & 14 deletions polkadot-parachains/statemine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use sp_runtime::{
create_runtime_str, generic, impl_opaque_keys,
traits::{AccountIdLookup, BlakeTwo256, Block as BlockT},
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult,
ApplyExtrinsicResult, Perbill,
};

use sp_std::prelude::*;
Expand All @@ -56,11 +56,10 @@ use frame_system::{
};
pub use parachains_common as common;
use parachains_common::{
impls::DealWithFees, opaque, AccountId, AuraId, Balance, BlockNumber, Hash, Header, Index,
Signature, AVERAGE_ON_INITIALIZE_RATIO, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO,
SLOT_DURATION,
impls::{DealWithFees, NonZeroIssuance},
opaque, AccountId, AssetId, AuraId, Balance, BlockNumber, Hash, Header, Index, Signature,
AVERAGE_ON_INITIALIZE_RATIO, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION,
};
use sp_runtime::Perbill;

#[cfg(any(feature = "std", test))]
pub use sp_runtime::BuildStorage;
Expand All @@ -71,13 +70,14 @@ use polkadot_parachain::primitives::Sibling;
use polkadot_runtime_common::{BlockHashCount, RocksDbWeight, SlowAdjustingFeeUpdate};
use xcm::latest::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter,
EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset,
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom,
AsPrefixedGeneralIndex, ConvertedConcreteAssetId, CurrencyAdapter, EnsureXcmOrigin,
FixedWeightBounds, FungiblesAdapter, IsConcrete, LocationInverter, NativeAsset,
ParentAsSuperuser, ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative,
SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, TakeWeightCredit, UsingComponents,
};
use xcm_executor::{Config, XcmExecutor};
use xcm_executor::{traits::JustTry, Config, XcmExecutor};

impl_opaque_keys! {
pub struct SessionKeys {
Expand Down Expand Up @@ -238,7 +238,7 @@ pub type AssetsForceOrigin = EnsureOneOf<
impl pallet_assets::Config for Runtime {
type Event = Event;
type Balance = Balance;
type AssetId = u32;
type AssetId = AssetId;
type Currency = Balances;
type ForceOrigin = AssetsForceOrigin;
type AssetDeposit = AssetDeposit;
Expand Down Expand Up @@ -465,6 +465,8 @@ parameter_types! {
pub const RelayNetwork: NetworkId = NetworkId::Kusama;
pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into();
pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into();
pub const Local: MultiLocation = Here.into();
pub CheckingAccount: AccountId = PolkadotXcm::check_account();
}

/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used
Expand All @@ -479,20 +481,44 @@ pub type LocationToAccountId = (
AccountId32Aliases<RelayNetwork, AccountId>,
);

/// Means for transacting assets on this chain.
pub type LocalAssetTransactor = CurrencyAdapter<
/// Means for transacting the native currency on this chain.
pub type CurrencyTransactor = CurrencyAdapter<
// Use this currency:
Balances,
// Use this currency when it is a fungible asset matching the given location or name:
IsConcrete<KsmLocation>,
// Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID:
// Convert an XCM MultiLocation into a local account id:
LocationToAccountId,
// Our chain's account ID type (we can't get away without mentioning it explicitly):
AccountId,
// We don't track any teleports.
// We don't track any teleports of `Balances`.
(),
>;

/// Means for transacting assets besides the native currency on this chain.
pub type FungiblesTransactor = FungiblesAdapter<
// Use this fungibles implementation:
Assets,
// Use this currency when it is a fungible asset matching the given location or name:
ConvertedConcreteAssetId<
AssetId,
Balance,
AsPrefixedGeneralIndex<Local, AssetId, JustTry>,
JustTry,
>,
// Convert an XCM MultiLocation into a local account id:
LocationToAccountId,
// Our chain's account ID type (we can't get away without mentioning it explicitly):
AccountId,
// We only want to allow teleports of known assets. We use non-zero issuance as an indication
// that this asset is known.
NonZeroIssuance<AccountId, Assets>,
// The account to use for tracking teleports.
CheckingAccount,
>;
/// Means for transacting assets on this chain.
pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor);

/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance,
/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can
/// biases the kind of local `Origin` it will become.
Expand Down Expand Up @@ -542,7 +568,7 @@ impl Config for XcmConfig {
type Call = Call;
type XcmSender = XcmRouter;
// How to withdraw and deposit an asset.
type AssetTransactor = LocalAssetTransactor;
type AssetTransactor = AssetTransactors;
type OriginConverter = XcmOriginToTransactDispatchOrigin;
type IsReserve = NativeAsset;
type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of KSM
Expand Down

0 comments on commit caa8b81

Please sign in to comment.