mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 21:37:56 +00:00
Asset Conversion: Pool Account ID derivation with additional Pallet ID seed (#3250)
Introduce `PalletId` as an additional seed parameter for pool's account id derivation. The PR also introduces the `pallet_asset_conversion_ops` pallet with a call to migrate a given pool to thew new account. Additionally `fungibles::lifetime::ResetTeam` and `fungible::lifetime::Refund` traits, to facilitate the migration of pools. --------- Co-authored-by: command-bot <>
This commit is contained in:
Generated
+25
@@ -871,6 +871,7 @@ dependencies = [
|
||||
"hex-literal",
|
||||
"log",
|
||||
"pallet-asset-conversion",
|
||||
"pallet-asset-conversion-ops",
|
||||
"pallet-asset-conversion-tx-payment",
|
||||
"pallet-assets",
|
||||
"pallet-aura",
|
||||
@@ -995,6 +996,7 @@ dependencies = [
|
||||
"hex-literal",
|
||||
"log",
|
||||
"pallet-asset-conversion",
|
||||
"pallet-asset-conversion-ops",
|
||||
"pallet-asset-conversion-tx-payment",
|
||||
"pallet-assets",
|
||||
"pallet-aura",
|
||||
@@ -7383,6 +7385,7 @@ dependencies = [
|
||||
"node-primitives",
|
||||
"pallet-alliance",
|
||||
"pallet-asset-conversion",
|
||||
"pallet-asset-conversion-ops",
|
||||
"pallet-asset-conversion-tx-payment",
|
||||
"pallet-asset-rate",
|
||||
"pallet-asset-tx-payment",
|
||||
@@ -9534,6 +9537,7 @@ dependencies = [
|
||||
"frame-benchmarking",
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"log",
|
||||
"pallet-assets",
|
||||
"pallet-balances",
|
||||
"parity-scale-codec",
|
||||
@@ -9547,6 +9551,27 @@ dependencies = [
|
||||
"sp-std 14.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-asset-conversion-ops"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"frame-benchmarking",
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"log",
|
||||
"pallet-asset-conversion",
|
||||
"pallet-assets",
|
||||
"pallet-balances",
|
||||
"parity-scale-codec",
|
||||
"primitive-types",
|
||||
"scale-info",
|
||||
"sp-arithmetic",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-runtime",
|
||||
"sp-std 14.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-asset-conversion-tx-payment"
|
||||
version = "10.0.0"
|
||||
|
||||
@@ -301,6 +301,7 @@ members = [
|
||||
"substrate/frame",
|
||||
"substrate/frame/alliance",
|
||||
"substrate/frame/asset-conversion",
|
||||
"substrate/frame/asset-conversion/ops",
|
||||
"substrate/frame/asset-rate",
|
||||
"substrate/frame/assets",
|
||||
"substrate/frame/atomic-swap",
|
||||
|
||||
@@ -25,6 +25,7 @@ frame-system-rpc-runtime-api = { path = "../../../../../substrate/frame/system/r
|
||||
frame-try-runtime = { path = "../../../../../substrate/frame/try-runtime", default-features = false, optional = true }
|
||||
pallet-asset-conversion-tx-payment = { path = "../../../../../substrate/frame/transaction-payment/asset-conversion-tx-payment", default-features = false }
|
||||
pallet-assets = { path = "../../../../../substrate/frame/assets", default-features = false }
|
||||
pallet-asset-conversion-ops = { path = "../../../../../substrate/frame/asset-conversion/ops", default-features = false }
|
||||
pallet-asset-conversion = { path = "../../../../../substrate/frame/asset-conversion", default-features = false }
|
||||
pallet-aura = { path = "../../../../../substrate/frame/aura", default-features = false }
|
||||
pallet-authorship = { path = "../../../../../substrate/frame/authorship", default-features = false }
|
||||
@@ -120,6 +121,7 @@ runtime-benchmarks = [
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system-benchmarking/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
"pallet-asset-conversion-ops/runtime-benchmarks",
|
||||
"pallet-asset-conversion/runtime-benchmarks",
|
||||
"pallet-assets/runtime-benchmarks",
|
||||
"pallet-balances/runtime-benchmarks",
|
||||
@@ -153,6 +155,7 @@ try-runtime = [
|
||||
"frame-support/try-runtime",
|
||||
"frame-system/try-runtime",
|
||||
"frame-try-runtime/try-runtime",
|
||||
"pallet-asset-conversion-ops/try-runtime",
|
||||
"pallet-asset-conversion-tx-payment/try-runtime",
|
||||
"pallet-asset-conversion/try-runtime",
|
||||
"pallet-assets/try-runtime",
|
||||
@@ -201,6 +204,7 @@ std = [
|
||||
"frame-system/std",
|
||||
"frame-try-runtime?/std",
|
||||
"log/std",
|
||||
"pallet-asset-conversion-ops/std",
|
||||
"pallet-asset-conversion-tx-payment/std",
|
||||
"pallet-asset-conversion/std",
|
||||
"pallet-assets/std",
|
||||
|
||||
@@ -333,6 +333,11 @@ pub type NativeAndAssets = fungible::UnionOf<
|
||||
AccountId,
|
||||
>;
|
||||
|
||||
pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter<
|
||||
AssetConversionPalletId,
|
||||
(xcm::v3::Location, xcm::v3::Location),
|
||||
>;
|
||||
|
||||
impl pallet_asset_conversion::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Balance = Balance;
|
||||
@@ -340,8 +345,12 @@ impl pallet_asset_conversion::Config for Runtime {
|
||||
type AssetKind = xcm::v3::Location;
|
||||
type Assets = NativeAndAssets;
|
||||
type PoolId = (Self::AssetKind, Self::AssetKind);
|
||||
type PoolLocator =
|
||||
pallet_asset_conversion::WithFirstAsset<TokenLocationV3, AccountId, Self::AssetKind>;
|
||||
type PoolLocator = pallet_asset_conversion::WithFirstAsset<
|
||||
TokenLocationV3,
|
||||
AccountId,
|
||||
Self::AssetKind,
|
||||
PoolIdToAccountId,
|
||||
>;
|
||||
type PoolAssetId = u32;
|
||||
type PoolAssets = PoolAssets;
|
||||
type PoolSetupFee = ConstU128<0>; // Asset class deposit fees are sufficient to prevent spam
|
||||
@@ -362,6 +371,18 @@ impl pallet_asset_conversion::Config for Runtime {
|
||||
>;
|
||||
}
|
||||
|
||||
impl pallet_asset_conversion_ops::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type PriorAccountIdConverter = pallet_asset_conversion::AccountIdConverterNoSeed<
|
||||
<Runtime as pallet_asset_conversion::Config>::PoolId,
|
||||
>;
|
||||
type AssetsRefund = <Runtime as pallet_asset_conversion::Config>::Assets;
|
||||
type PoolAssetsRefund = <Runtime as pallet_asset_conversion::Config>::PoolAssets;
|
||||
type PoolAssetsTeam = <Runtime as pallet_asset_conversion::Config>::PoolAssets;
|
||||
type DepositAsset = Balances;
|
||||
type WeightInfo = weights::pallet_asset_conversion_ops::WeightInfo<Runtime>;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
// we just reuse the same deposits
|
||||
pub const ForeignAssetsAssetDeposit: Balance = AssetDeposit::get();
|
||||
@@ -934,6 +955,10 @@ construct_runtime!(
|
||||
|
||||
#[cfg(feature = "state-trie-version-1")]
|
||||
StateTrieMigration: pallet_state_trie_migration = 70,
|
||||
|
||||
// TODO: the pallet instance should be removed once all pools have migrated
|
||||
// to the new account IDs.
|
||||
AssetConversionMigration: pallet_asset_conversion_ops = 200,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -961,6 +986,7 @@ pub type SignedExtra = (
|
||||
pub type UncheckedExtrinsic =
|
||||
generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>;
|
||||
/// Migrations to apply on runtime upgrade.
|
||||
#[allow(deprecated)]
|
||||
pub type Migrations = (
|
||||
pallet_collator_selection::migration::v1::MigrateToV1<Runtime>,
|
||||
InitStorageVersions,
|
||||
@@ -1054,6 +1080,7 @@ mod benches {
|
||||
[cumulus_pallet_parachain_system, ParachainSystem]
|
||||
[cumulus_pallet_xcmp_queue, XcmpQueue]
|
||||
[pallet_xcm_bridge_hub_router, ToWestend]
|
||||
[pallet_asset_conversion_ops, AssetConversionMigration]
|
||||
// XCM
|
||||
[pallet_xcm, PalletXcmExtrinsicsBenchmark::<Runtime>]
|
||||
// NOTE: Make sure you point to the individual modules below.
|
||||
|
||||
@@ -20,6 +20,7 @@ pub mod cumulus_pallet_xcmp_queue;
|
||||
pub mod extrinsic_weights;
|
||||
pub mod frame_system;
|
||||
pub mod pallet_asset_conversion;
|
||||
pub mod pallet_asset_conversion_ops;
|
||||
pub mod pallet_assets_foreign;
|
||||
pub mod pallet_assets_local;
|
||||
pub mod pallet_assets_pool;
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Cumulus.
|
||||
|
||||
// Cumulus 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.
|
||||
|
||||
// Cumulus 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Autogenerated weights for `pallet_asset_conversion_ops`
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
|
||||
//! DATE: 2024-02-15, STEPS: `10`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! WORST CASE MAP SIZE: `1000000`
|
||||
//! HOSTNAME: `cob`, CPU: `<UNKNOWN>`
|
||||
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024
|
||||
|
||||
// Executed Command:
|
||||
// ./target/debug/polkadot-parachain
|
||||
// benchmark
|
||||
// pallet
|
||||
// --chain=asset-hub-rococo-dev
|
||||
// --steps=10
|
||||
// --repeat=2
|
||||
// --pallet=pallet-asset-conversion-ops
|
||||
// --extrinsic=*
|
||||
// --wasm-execution=compiled
|
||||
// --heap-pages=4096
|
||||
// --output=./cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use frame_support::{traits::Get, weights::Weight};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Weight functions for `pallet_asset_conversion_ops`.
|
||||
pub struct WeightInfo<T>(PhantomData<T>);
|
||||
impl<T: frame_system::Config> pallet_asset_conversion_ops::WeightInfo for WeightInfo<T> {
|
||||
/// Storage: `AssetConversion::Pools` (r:1 w:0)
|
||||
/// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:2 w:2)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
/// Storage: `ForeignAssets::Account` (r:2 w:2)
|
||||
/// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`)
|
||||
/// Storage: `PoolAssets::Account` (r:2 w:2)
|
||||
/// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
|
||||
/// Storage: `PoolAssets::Asset` (r:1 w:1)
|
||||
/// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
|
||||
/// Storage: `ForeignAssets::Asset` (r:1 w:1)
|
||||
/// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`)
|
||||
fn migrate_to_new_account() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `1105`
|
||||
// Estimated: `7404`
|
||||
// Minimum execution time: 2_323_000_000 picoseconds.
|
||||
Weight::from_parts(2_404_000_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 7404))
|
||||
.saturating_add(T::DbWeight::get().reads(9))
|
||||
.saturating_add(T::DbWeight::get().writes(8))
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ frame-system = { path = "../../../../../substrate/frame/system", default-feature
|
||||
frame-system-benchmarking = { path = "../../../../../substrate/frame/system/benchmarking", default-features = false, optional = true }
|
||||
frame-system-rpc-runtime-api = { path = "../../../../../substrate/frame/system/rpc/runtime-api", default-features = false }
|
||||
frame-try-runtime = { path = "../../../../../substrate/frame/try-runtime", default-features = false, optional = true }
|
||||
pallet-asset-conversion-ops = { path = "../../../../../substrate/frame/asset-conversion/ops", default-features = false }
|
||||
pallet-asset-conversion-tx-payment = { path = "../../../../../substrate/frame/transaction-payment/asset-conversion-tx-payment", default-features = false }
|
||||
pallet-assets = { path = "../../../../../substrate/frame/assets", default-features = false }
|
||||
pallet-asset-conversion = { path = "../../../../../substrate/frame/asset-conversion", default-features = false }
|
||||
@@ -111,6 +112,7 @@ runtime-benchmarks = [
|
||||
"frame-system-benchmarking/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
"hex-literal",
|
||||
"pallet-asset-conversion-ops/runtime-benchmarks",
|
||||
"pallet-asset-conversion/runtime-benchmarks",
|
||||
"pallet-assets/runtime-benchmarks",
|
||||
"pallet-balances/runtime-benchmarks",
|
||||
@@ -142,6 +144,7 @@ try-runtime = [
|
||||
"frame-support/try-runtime",
|
||||
"frame-system/try-runtime",
|
||||
"frame-try-runtime/try-runtime",
|
||||
"pallet-asset-conversion-ops/try-runtime",
|
||||
"pallet-asset-conversion-tx-payment/try-runtime",
|
||||
"pallet-asset-conversion/try-runtime",
|
||||
"pallet-assets/try-runtime",
|
||||
@@ -189,6 +192,7 @@ std = [
|
||||
"frame-system/std",
|
||||
"frame-try-runtime?/std",
|
||||
"log/std",
|
||||
"pallet-asset-conversion-ops/std",
|
||||
"pallet-asset-conversion-tx-payment/std",
|
||||
"pallet-asset-conversion/std",
|
||||
"pallet-assets/std",
|
||||
|
||||
@@ -315,6 +315,11 @@ pub type NativeAndAssets = fungible::UnionOf<
|
||||
AccountId,
|
||||
>;
|
||||
|
||||
pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter<
|
||||
AssetConversionPalletId,
|
||||
(xcm::v3::Location, xcm::v3::Location),
|
||||
>;
|
||||
|
||||
impl pallet_asset_conversion::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Balance = Balance;
|
||||
@@ -322,8 +327,12 @@ impl pallet_asset_conversion::Config for Runtime {
|
||||
type AssetKind = xcm::v3::Location;
|
||||
type Assets = NativeAndAssets;
|
||||
type PoolId = (Self::AssetKind, Self::AssetKind);
|
||||
type PoolLocator =
|
||||
pallet_asset_conversion::WithFirstAsset<WestendLocationV3, AccountId, Self::AssetKind>;
|
||||
type PoolLocator = pallet_asset_conversion::WithFirstAsset<
|
||||
WestendLocationV3,
|
||||
AccountId,
|
||||
Self::AssetKind,
|
||||
PoolIdToAccountId,
|
||||
>;
|
||||
type PoolAssetId = u32;
|
||||
type PoolAssets = PoolAssets;
|
||||
type PoolSetupFee = ConstU128<0>; // Asset class deposit fees are sufficient to prevent spam
|
||||
@@ -344,6 +353,18 @@ impl pallet_asset_conversion::Config for Runtime {
|
||||
>;
|
||||
}
|
||||
|
||||
impl pallet_asset_conversion_ops::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type PriorAccountIdConverter = pallet_asset_conversion::AccountIdConverterNoSeed<
|
||||
<Runtime as pallet_asset_conversion::Config>::PoolId,
|
||||
>;
|
||||
type AssetsRefund = <Runtime as pallet_asset_conversion::Config>::Assets;
|
||||
type PoolAssetsRefund = <Runtime as pallet_asset_conversion::Config>::PoolAssets;
|
||||
type PoolAssetsTeam = <Runtime as pallet_asset_conversion::Config>::PoolAssets;
|
||||
type DepositAsset = Balances;
|
||||
type WeightInfo = weights::pallet_asset_conversion_ops::WeightInfo<Runtime>;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
// we just reuse the same deposits
|
||||
pub const ForeignAssetsAssetDeposit: Balance = AssetDeposit::get();
|
||||
@@ -906,6 +927,10 @@ construct_runtime!(
|
||||
NftFractionalization: pallet_nft_fractionalization = 54,
|
||||
PoolAssets: pallet_assets::<Instance3> = 55,
|
||||
AssetConversion: pallet_asset_conversion = 56,
|
||||
|
||||
// TODO: the pallet instance should be removed once all pools have migrated
|
||||
// to the new account IDs.
|
||||
AssetConversionMigration: pallet_asset_conversion_ops = 200,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1085,6 +1110,7 @@ mod benches {
|
||||
[cumulus_pallet_parachain_system, ParachainSystem]
|
||||
[cumulus_pallet_xcmp_queue, XcmpQueue]
|
||||
[pallet_xcm_bridge_hub_router, ToRococo]
|
||||
[pallet_asset_conversion_ops, AssetConversionMigration]
|
||||
// XCM
|
||||
[pallet_xcm, PalletXcmExtrinsicsBenchmark::<Runtime>]
|
||||
// NOTE: Make sure you point to the individual modules below.
|
||||
|
||||
@@ -19,6 +19,7 @@ pub mod cumulus_pallet_xcmp_queue;
|
||||
pub mod extrinsic_weights;
|
||||
pub mod frame_system;
|
||||
pub mod pallet_asset_conversion;
|
||||
pub mod pallet_asset_conversion_ops;
|
||||
pub mod pallet_assets_foreign;
|
||||
pub mod pallet_assets_local;
|
||||
pub mod pallet_assets_pool;
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Cumulus.
|
||||
|
||||
// Cumulus 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.
|
||||
|
||||
// Cumulus 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Autogenerated weights for `pallet_asset_conversion_ops`
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
|
||||
//! DATE: 2024-02-15, STEPS: `10`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! WORST CASE MAP SIZE: `1000000`
|
||||
//! HOSTNAME: `cob`, CPU: `<UNKNOWN>`
|
||||
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024
|
||||
|
||||
// Executed Command:
|
||||
// ./target/debug/polkadot-parachain
|
||||
// benchmark
|
||||
// pallet
|
||||
// --chain=asset-hub-westend-dev
|
||||
// --steps=10
|
||||
// --repeat=2
|
||||
// --pallet=pallet-asset-conversion-ops
|
||||
// --extrinsic=*
|
||||
// --wasm-execution=compiled
|
||||
// --heap-pages=4096
|
||||
// --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use frame_support::{traits::Get, weights::Weight};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Weight functions for `pallet_asset_conversion_ops`.
|
||||
pub struct WeightInfo<T>(PhantomData<T>);
|
||||
impl<T: frame_system::Config> pallet_asset_conversion_ops::WeightInfo for WeightInfo<T> {
|
||||
/// Storage: `AssetConversion::Pools` (r:1 w:0)
|
||||
/// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:2 w:2)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
/// Storage: `ForeignAssets::Account` (r:2 w:2)
|
||||
/// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`)
|
||||
/// Storage: `PoolAssets::Account` (r:2 w:2)
|
||||
/// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
|
||||
/// Storage: `PoolAssets::Asset` (r:1 w:1)
|
||||
/// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
|
||||
/// Storage: `ForeignAssets::Asset` (r:1 w:1)
|
||||
/// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`)
|
||||
fn migrate_to_new_account() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `1105`
|
||||
// Estimated: `7404`
|
||||
// Minimum execution time: 2_216_000_000 picoseconds.
|
||||
Weight::from_parts(2_379_000_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 7404))
|
||||
.saturating_add(T::DbWeight::get().reads(9))
|
||||
.saturating_add(T::DbWeight::get().writes(8))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
|
||||
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
|
||||
|
||||
title: "Asset Conversion: Pool Account ID derivation with additional Pallet ID seed"
|
||||
|
||||
doc:
|
||||
- audience: Runtime Dev
|
||||
description: |
|
||||
Introduce PalletId as an additional seed parameter for pool's account id derivation.
|
||||
The PR also introduces the `pallet_asset_conversion_ops` pallet with a call to migrate
|
||||
a pool to the new account. Additionally `fungibles::roles::ResetTeam` and
|
||||
`fungible::lifetime::Refund` traits, to facilitate the migration functionality.
|
||||
|
||||
crates:
|
||||
- name: pallet-asset-conversion
|
||||
bump: minor
|
||||
@@ -66,6 +66,7 @@ frame-system-rpc-runtime-api = { path = "../../../frame/system/rpc/runtime-api",
|
||||
frame-try-runtime = { path = "../../../frame/try-runtime", default-features = false, optional = true }
|
||||
pallet-alliance = { path = "../../../frame/alliance", default-features = false }
|
||||
pallet-asset-conversion = { path = "../../../frame/asset-conversion", default-features = false }
|
||||
pallet-asset-conversion-ops = { path = "../../../frame/asset-conversion/ops", default-features = false }
|
||||
pallet-asset-rate = { path = "../../../frame/asset-rate", default-features = false }
|
||||
pallet-assets = { path = "../../../frame/assets", default-features = false }
|
||||
pallet-authority-discovery = { path = "../../../frame/authority-discovery", default-features = false }
|
||||
@@ -166,6 +167,7 @@ std = [
|
||||
"log/std",
|
||||
"node-primitives/std",
|
||||
"pallet-alliance/std",
|
||||
"pallet-asset-conversion-ops/std",
|
||||
"pallet-asset-conversion-tx-payment/std",
|
||||
"pallet-asset-conversion/std",
|
||||
"pallet-asset-rate/std",
|
||||
@@ -278,6 +280,7 @@ runtime-benchmarks = [
|
||||
"frame-system-benchmarking/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
"pallet-alliance/runtime-benchmarks",
|
||||
"pallet-asset-conversion-ops/runtime-benchmarks",
|
||||
"pallet-asset-conversion/runtime-benchmarks",
|
||||
"pallet-asset-rate/runtime-benchmarks",
|
||||
"pallet-asset-tx-payment/runtime-benchmarks",
|
||||
@@ -354,6 +357,7 @@ try-runtime = [
|
||||
"frame-system/try-runtime",
|
||||
"frame-try-runtime/try-runtime",
|
||||
"pallet-alliance/try-runtime",
|
||||
"pallet-asset-conversion-ops/try-runtime",
|
||||
"pallet-asset-conversion-tx-payment/try-runtime",
|
||||
"pallet-asset-conversion/try-runtime",
|
||||
"pallet-asset-rate/try-runtime",
|
||||
|
||||
@@ -63,7 +63,7 @@ use frame_system::{
|
||||
};
|
||||
pub use node_primitives::{AccountId, Signature};
|
||||
use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Moment, Nonce};
|
||||
use pallet_asset_conversion::{Ascending, Chain, WithFirstAsset};
|
||||
use pallet_asset_conversion::{AccountIdConverter, Ascending, Chain, WithFirstAsset};
|
||||
use pallet_broker::{CoreAssignment, CoreIndex, CoretimeInterface, PartsOf57600};
|
||||
use pallet_election_provider_multi_phase::{GeometricDepositBase, SolutionAccuracyOf};
|
||||
use pallet_identity::legacy::IdentityInfo;
|
||||
@@ -1710,8 +1710,17 @@ impl pallet_asset_conversion::Config for Runtime {
|
||||
type Assets = UnionOf<Balances, Assets, NativeFromLeft, NativeOrWithId<u32>, AccountId>;
|
||||
type PoolId = (Self::AssetKind, Self::AssetKind);
|
||||
type PoolLocator = Chain<
|
||||
WithFirstAsset<Native, AccountId, NativeOrWithId<u32>>,
|
||||
Ascending<AccountId, NativeOrWithId<u32>>,
|
||||
WithFirstAsset<
|
||||
Native,
|
||||
AccountId,
|
||||
NativeOrWithId<u32>,
|
||||
AccountIdConverter<AssetConversionPalletId, Self::PoolId>,
|
||||
>,
|
||||
Ascending<
|
||||
AccountId,
|
||||
NativeOrWithId<u32>,
|
||||
AccountIdConverter<AssetConversionPalletId, Self::PoolId>,
|
||||
>,
|
||||
>;
|
||||
type PoolAssetId = <Self as pallet_assets::Config<Instance2>>::AssetId;
|
||||
type PoolAssets = PoolAssets;
|
||||
@@ -1728,6 +1737,19 @@ impl pallet_asset_conversion::Config for Runtime {
|
||||
type BenchmarkHelper = ();
|
||||
}
|
||||
|
||||
impl pallet_asset_conversion_ops::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type PriorAccountIdConverter = pallet_asset_conversion::AccountIdConverterNoSeed<(
|
||||
NativeOrWithId<u32>,
|
||||
NativeOrWithId<u32>,
|
||||
)>;
|
||||
type AssetsRefund = <Runtime as pallet_asset_conversion::Config>::Assets;
|
||||
type PoolAssetsRefund = <Runtime as pallet_asset_conversion::Config>::PoolAssets;
|
||||
type PoolAssetsTeam = <Runtime as pallet_asset_conversion::Config>::PoolAssets;
|
||||
type DepositAsset = Balances;
|
||||
type WeightInfo = pallet_asset_conversion_ops::weights::SubstrateWeight<Runtime>;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const QueueCount: u32 = 300;
|
||||
pub const MaxQueueLen: u32 = 1000;
|
||||
@@ -2474,6 +2496,9 @@ mod runtime {
|
||||
|
||||
#[runtime::pallet_index(78)]
|
||||
pub type PalletExampleMbms = pallet_example_mbm;
|
||||
|
||||
#[runtime::pallet_index(79)]
|
||||
pub type AssetConversionMigration = pallet_asset_conversion_ops;
|
||||
}
|
||||
|
||||
/// The address format for describing accounts.
|
||||
@@ -2633,6 +2658,7 @@ mod benches {
|
||||
[pallet_tx_pause, TxPause]
|
||||
[pallet_safe_mode, SafeMode]
|
||||
[pallet_example_mbm, PalletExampleMbms]
|
||||
[pallet_asset_conversion_ops, AssetConversionMigration]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false }
|
||||
log = { version = "0.4.20", default-features = false }
|
||||
frame-support = { path = "../support", default-features = false }
|
||||
frame-system = { path = "../system", default-features = false }
|
||||
frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true }
|
||||
@@ -40,6 +41,7 @@ std = [
|
||||
"frame-benchmarking?/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"log/std",
|
||||
"pallet-assets/std",
|
||||
"pallet-balances/std",
|
||||
"primitive-types/std",
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
[package]
|
||||
name = "pallet-asset-conversion-ops"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://substrate.io"
|
||||
repository.workspace = true
|
||||
description = "FRAME asset conversion pallet's operations suite"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false }
|
||||
log = { version = "0.4.20", default-features = false }
|
||||
frame-support = { path = "../../support", default-features = false }
|
||||
frame-system = { path = "../../system", default-features = false }
|
||||
frame-benchmarking = { path = "../../benchmarking", default-features = false, optional = true }
|
||||
pallet-asset-conversion = { path = "..", default-features = false }
|
||||
scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }
|
||||
sp-core = { path = "../../../primitives/core", default-features = false }
|
||||
sp-io = { path = "../../../primitives/io", default-features = false }
|
||||
sp-std = { path = "../../../primitives/std", default-features = false }
|
||||
sp-runtime = { path = "../../../primitives/runtime", default-features = false }
|
||||
sp-arithmetic = { path = "../../../primitives/arithmetic", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
pallet-balances = { path = "../../balances" }
|
||||
pallet-assets = { path = "../../assets" }
|
||||
primitive-types = { version = "0.12.0", default-features = false, features = ["codec", "num-traits", "scale-info"] }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"frame-benchmarking?/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"log/std",
|
||||
"pallet-asset-conversion/std",
|
||||
"pallet-assets/std",
|
||||
"pallet-balances/std",
|
||||
"primitive-types/std",
|
||||
"scale-info/std",
|
||||
"sp-arithmetic/std",
|
||||
"sp-core/std",
|
||||
"sp-io/std",
|
||||
"sp-runtime/std",
|
||||
"sp-std/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"frame-benchmarking/runtime-benchmarks",
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
"pallet-asset-conversion/runtime-benchmarks",
|
||||
"pallet-assets/runtime-benchmarks",
|
||||
"pallet-balances/runtime-benchmarks",
|
||||
"sp-runtime/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"frame-support/try-runtime",
|
||||
"frame-system/try-runtime",
|
||||
"pallet-asset-conversion/try-runtime",
|
||||
"pallet-assets/try-runtime",
|
||||
"pallet-balances/try-runtime",
|
||||
"sp-runtime/try-runtime",
|
||||
]
|
||||
@@ -0,0 +1,167 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Asset Conversion Ops pallet benchmarking.
|
||||
|
||||
use super::*;
|
||||
use crate::Pallet as AssetConversionOps;
|
||||
use frame_benchmarking::{v2::*, whitelisted_caller};
|
||||
use frame_support::{
|
||||
assert_ok,
|
||||
traits::fungibles::{Create, Inspect, Mutate},
|
||||
};
|
||||
use frame_system::RawOrigin as SystemOrigin;
|
||||
use pallet_asset_conversion::{BenchmarkHelper, Pallet as AssetConversion};
|
||||
use sp_core::Get;
|
||||
use sp_runtime::traits::One;
|
||||
use sp_std::prelude::*;
|
||||
|
||||
/// Provides a pair of amounts expected to serve as sufficient initial liquidity for a pool.
|
||||
fn valid_liquidity_amount<T: Config>(ed1: T::Balance, ed2: T::Balance) -> (T::Balance, T::Balance)
|
||||
where
|
||||
T::Assets: Inspect<T::AccountId>,
|
||||
{
|
||||
let l =
|
||||
ed1.max(ed2) + T::MintMinLiquidity::get() + T::MintMinLiquidity::get() + T::Balance::one();
|
||||
(l, l)
|
||||
}
|
||||
|
||||
/// Create the `asset` and mint the `amount` for the `caller`.
|
||||
fn create_asset<T: Config>(caller: &T::AccountId, asset: &T::AssetKind, amount: T::Balance)
|
||||
where
|
||||
T::Assets: Create<T::AccountId> + Mutate<T::AccountId>,
|
||||
{
|
||||
if !T::Assets::asset_exists(asset.clone()) {
|
||||
assert_ok!(T::Assets::create(asset.clone(), caller.clone(), true, T::Balance::one()));
|
||||
}
|
||||
assert_ok!(T::Assets::mint_into(
|
||||
asset.clone(),
|
||||
&caller,
|
||||
amount + T::Assets::minimum_balance(asset.clone())
|
||||
));
|
||||
}
|
||||
|
||||
/// Create the designated fee asset for pool creation.
|
||||
fn create_fee_asset<T: Config>(caller: &T::AccountId)
|
||||
where
|
||||
T::Assets: Create<T::AccountId> + Mutate<T::AccountId>,
|
||||
{
|
||||
let fee_asset = T::PoolSetupFeeAsset::get();
|
||||
if !T::Assets::asset_exists(fee_asset.clone()) {
|
||||
assert_ok!(T::Assets::create(fee_asset.clone(), caller.clone(), true, T::Balance::one()));
|
||||
}
|
||||
assert_ok!(T::Assets::mint_into(
|
||||
fee_asset.clone(),
|
||||
&caller,
|
||||
T::Assets::minimum_balance(fee_asset)
|
||||
));
|
||||
}
|
||||
|
||||
/// Mint the fee asset for the `caller` sufficient to cover the fee for creating a new pool.
|
||||
fn mint_setup_fee_asset<T: Config>(
|
||||
caller: &T::AccountId,
|
||||
asset1: &T::AssetKind,
|
||||
asset2: &T::AssetKind,
|
||||
lp_token: &T::PoolAssetId,
|
||||
) where
|
||||
T::Assets: Create<T::AccountId> + Mutate<T::AccountId>,
|
||||
{
|
||||
assert_ok!(T::Assets::mint_into(
|
||||
T::PoolSetupFeeAsset::get(),
|
||||
&caller,
|
||||
T::PoolSetupFee::get() +
|
||||
T::Assets::deposit_required(asset1.clone()) +
|
||||
T::Assets::deposit_required(asset2.clone()) +
|
||||
T::PoolAssets::deposit_required(lp_token.clone())
|
||||
));
|
||||
}
|
||||
|
||||
/// Creates a pool for a given asset pair.
|
||||
///
|
||||
/// This action mints the necessary amounts of the given assets for the `caller` to provide initial
|
||||
/// liquidity. It returns the LP token ID along with a pair of amounts sufficient for the pool's
|
||||
/// initial liquidity.
|
||||
fn create_asset_and_pool<T: Config>(
|
||||
caller: &T::AccountId,
|
||||
asset1: &T::AssetKind,
|
||||
asset2: &T::AssetKind,
|
||||
) -> (T::PoolAssetId, T::Balance, T::Balance)
|
||||
where
|
||||
T::Assets: Create<T::AccountId> + Mutate<T::AccountId>,
|
||||
{
|
||||
let (liquidity1, liquidity2) = valid_liquidity_amount::<T>(
|
||||
T::Assets::minimum_balance(asset1.clone()),
|
||||
T::Assets::minimum_balance(asset2.clone()),
|
||||
);
|
||||
create_asset::<T>(caller, asset1, liquidity1);
|
||||
create_asset::<T>(caller, asset2, liquidity2);
|
||||
let lp_token = AssetConversion::<T>::get_next_pool_asset_id();
|
||||
|
||||
mint_setup_fee_asset::<T>(caller, asset1, asset2, &lp_token);
|
||||
|
||||
assert_ok!(AssetConversion::<T>::create_pool(
|
||||
SystemOrigin::Signed(caller.clone()).into(),
|
||||
Box::new(asset1.clone()),
|
||||
Box::new(asset2.clone())
|
||||
));
|
||||
|
||||
(lp_token, liquidity1, liquidity2)
|
||||
}
|
||||
|
||||
fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
|
||||
let events = frame_system::Pallet::<T>::events();
|
||||
let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
|
||||
// compare to the last event record
|
||||
let frame_system::EventRecord { event, .. } = &events[events.len() - 1];
|
||||
assert_eq!(event, &system_event);
|
||||
}
|
||||
|
||||
#[benchmarks(where T::Assets: Create<T::AccountId> + Mutate<T::AccountId>, T::PoolAssetId: Into<u32>,)]
|
||||
mod benchmarks {
|
||||
use super::*;
|
||||
|
||||
#[benchmark]
|
||||
fn migrate_to_new_account() {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
let (asset1, asset2) = T::BenchmarkHelper::create_pair(0, 1);
|
||||
|
||||
create_fee_asset::<T>(&caller);
|
||||
let (_, liquidity1, liquidity2) = create_asset_and_pool::<T>(&caller, &asset1, &asset2);
|
||||
|
||||
assert_ok!(AssetConversion::<T>::add_liquidity(
|
||||
SystemOrigin::Signed(caller.clone()).into(),
|
||||
Box::new(asset1.clone()),
|
||||
Box::new(asset2.clone()),
|
||||
liquidity1,
|
||||
liquidity2,
|
||||
T::Balance::one(),
|
||||
T::Balance::zero(),
|
||||
caller.clone(),
|
||||
));
|
||||
|
||||
#[extrinsic_call]
|
||||
_(SystemOrigin::Signed(caller.clone()), Box::new(asset1.clone()), Box::new(asset2.clone()));
|
||||
|
||||
let pool_id = T::PoolLocator::pool_id(&asset1, &asset2).unwrap();
|
||||
let (prior_account, new_account) = AssetConversionOps::<T>::addresses(&pool_id).unwrap();
|
||||
assert_last_event::<T>(
|
||||
Event::MigratedToNewAccount { pool_id, new_account, prior_account }.into(),
|
||||
);
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(AssetConversionOps, crate::mock::new_test_ext(), crate::mock::Test);
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! # Asset Conversion Operations Suite.
|
||||
//!
|
||||
//! This pallet provides operational functionalities for the Asset Conversion pallet,
|
||||
//! allowing you to perform various migration and one-time-use operations. These operations
|
||||
//! are designed to facilitate updates and changes to the Asset Conversion pallet without
|
||||
//! breaking its API.
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! This suite allows you to perform the following operations:
|
||||
//! - Perform migration to update account ID derivation methods for existing pools. The migration
|
||||
//! operation ensures that the required accounts are created, existing account deposits are
|
||||
//! transferred, and liquidity is moved to the new accounts.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking;
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub mod weights;
|
||||
pub use pallet::*;
|
||||
pub use weights::WeightInfo;
|
||||
|
||||
use frame_support::traits::{
|
||||
fungible::{Inspect as FungibleInspect, Mutate as FungibleMutate},
|
||||
fungibles::{roles::ResetTeam, Inspect, Mutate, Refund},
|
||||
tokens::{Fortitude, Precision, Preservation},
|
||||
AccountTouch,
|
||||
};
|
||||
use pallet_asset_conversion::{PoolLocator, Pools};
|
||||
use sp_runtime::traits::{TryConvert, Zero};
|
||||
use sp_std::boxed::Box;
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use super::*;
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config:
|
||||
pallet_asset_conversion::Config<
|
||||
PoolId = (
|
||||
<Self as pallet_asset_conversion::Config>::AssetKind,
|
||||
<Self as pallet_asset_conversion::Config>::AssetKind,
|
||||
),
|
||||
> + frame_system::Config
|
||||
{
|
||||
/// Overarching event type.
|
||||
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
|
||||
/// Type previously used to derive the account ID for a pool. Indicates that the pool's
|
||||
/// liquidity assets are located at this account before the migration.
|
||||
type PriorAccountIdConverter: for<'a> TryConvert<
|
||||
&'a (Self::AssetKind, Self::AssetKind),
|
||||
Self::AccountId,
|
||||
>;
|
||||
|
||||
/// Retrieves information about an existing deposit for a given account ID and asset from
|
||||
/// the [`pallet_asset_conversion::Config::Assets`] registry and can initiate the refund.
|
||||
type AssetsRefund: Refund<
|
||||
Self::AccountId,
|
||||
AssetId = Self::AssetKind,
|
||||
Balance = <Self::DepositAsset as FungibleInspect<Self::AccountId>>::Balance,
|
||||
>;
|
||||
|
||||
/// Retrieves information about an existing deposit for a given account ID and asset from
|
||||
/// the [`pallet_asset_conversion::Config::PoolAssets`] registry and can initiate the
|
||||
/// refund.
|
||||
type PoolAssetsRefund: Refund<
|
||||
Self::AccountId,
|
||||
AssetId = Self::PoolAssetId,
|
||||
Balance = <Self::DepositAsset as FungibleInspect<Self::AccountId>>::Balance,
|
||||
>;
|
||||
|
||||
/// Means to reset the team for assets from the
|
||||
/// [`pallet_asset_conversion::Config::PoolAssets`] registry.
|
||||
type PoolAssetsTeam: ResetTeam<Self::AccountId, AssetId = Self::PoolAssetId>;
|
||||
|
||||
/// Registry of an asset used as an account deposit for the
|
||||
/// [`pallet_asset_conversion::Config::Assets`] and
|
||||
/// [`pallet_asset_conversion::Config::PoolAssets`] registries.
|
||||
type DepositAsset: FungibleMutate<Self::AccountId>;
|
||||
|
||||
/// Weight information for extrinsics in this pallet.
|
||||
type WeightInfo: WeightInfo;
|
||||
}
|
||||
|
||||
// Pallet's events.
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
/// Indicates that a pool has been migrated to the new account ID.
|
||||
MigratedToNewAccount {
|
||||
/// Pool's ID.
|
||||
pool_id: T::PoolId,
|
||||
/// Pool's prior account ID.
|
||||
prior_account: T::AccountId,
|
||||
/// Pool's new account ID.
|
||||
new_account: T::AccountId,
|
||||
},
|
||||
}
|
||||
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
/// Provided asset pair is not supported for pool.
|
||||
InvalidAssetPair,
|
||||
/// The pool doesn't exist.
|
||||
PoolNotFound,
|
||||
/// Pool's balance cannot be zero.
|
||||
ZeroBalance,
|
||||
/// Indicates a partial transfer of balance to the new account during a migration.
|
||||
PartialTransfer,
|
||||
}
|
||||
|
||||
/// Pallet's callable functions.
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Migrates an existing pool to a new account ID derivation method for a given asset pair.
|
||||
/// If the migration is successful, transaction fees are refunded to the caller.
|
||||
///
|
||||
/// Must be signed.
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight(<T as Config>::WeightInfo::migrate_to_new_account())]
|
||||
pub fn migrate_to_new_account(
|
||||
origin: OriginFor<T>,
|
||||
asset1: Box<T::AssetKind>,
|
||||
asset2: Box<T::AssetKind>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let _ = ensure_signed(origin)?;
|
||||
|
||||
let pool_id = T::PoolLocator::pool_id(&asset1, &asset2)
|
||||
.map_err(|_| Error::<T>::InvalidAssetPair)?;
|
||||
let info = Pools::<T>::get(&pool_id).ok_or(Error::<T>::PoolNotFound)?;
|
||||
|
||||
let (prior_account, new_account) =
|
||||
Self::addresses(&pool_id).ok_or(Error::<T>::InvalidAssetPair)?;
|
||||
|
||||
let (asset1, asset2) = pool_id.clone();
|
||||
|
||||
// Assets that must be transferred to the new account id.
|
||||
let balance1 = T::Assets::total_balance(asset1.clone(), &prior_account);
|
||||
let balance2 = T::Assets::total_balance(asset2.clone(), &prior_account);
|
||||
let lp_balance = T::PoolAssets::total_balance(info.lp_token.clone(), &prior_account);
|
||||
|
||||
ensure!(!balance1.is_zero(), Error::<T>::ZeroBalance);
|
||||
ensure!(!balance2.is_zero(), Error::<T>::ZeroBalance);
|
||||
ensure!(!lp_balance.is_zero(), Error::<T>::ZeroBalance);
|
||||
|
||||
// Check if a deposit needs to be placed for the new account. If so, mint the
|
||||
// required deposit amount to the depositor's account to ensure the deposit can be
|
||||
// provided. Once the deposit from the prior account is returned, the minted assets will
|
||||
// be burned. Touching the new account is necessary because it's not possible to
|
||||
// transfer assets to the new account if it's required. Additionally, the deposit cannot
|
||||
// be refunded from the prior account until its balance is zero.
|
||||
|
||||
let deposit_asset_ed = T::DepositAsset::minimum_balance();
|
||||
|
||||
if let Some((depositor, deposit)) =
|
||||
T::AssetsRefund::deposit_held(asset1.clone(), prior_account.clone())
|
||||
{
|
||||
T::DepositAsset::mint_into(&depositor, deposit + deposit_asset_ed)?;
|
||||
T::Assets::touch(asset1.clone(), &new_account, &depositor)?;
|
||||
}
|
||||
|
||||
if let Some((depositor, deposit)) =
|
||||
T::AssetsRefund::deposit_held(asset2.clone(), prior_account.clone())
|
||||
{
|
||||
T::DepositAsset::mint_into(&depositor, deposit + deposit_asset_ed)?;
|
||||
T::Assets::touch(asset2.clone(), &new_account, &depositor)?;
|
||||
}
|
||||
|
||||
if let Some((depositor, deposit)) =
|
||||
T::PoolAssetsRefund::deposit_held(info.lp_token.clone(), prior_account.clone())
|
||||
{
|
||||
T::DepositAsset::mint_into(&depositor, deposit + deposit_asset_ed)?;
|
||||
T::PoolAssets::touch(info.lp_token.clone(), &new_account, &depositor)?;
|
||||
}
|
||||
|
||||
// Transfer all pool related assets to the new account.
|
||||
|
||||
ensure!(
|
||||
balance1 ==
|
||||
T::Assets::transfer(
|
||||
asset1.clone(),
|
||||
&prior_account,
|
||||
&new_account,
|
||||
balance1,
|
||||
Preservation::Expendable,
|
||||
)?,
|
||||
Error::<T>::PartialTransfer
|
||||
);
|
||||
|
||||
ensure!(
|
||||
balance2 ==
|
||||
T::Assets::transfer(
|
||||
asset2.clone(),
|
||||
&prior_account,
|
||||
&new_account,
|
||||
balance2,
|
||||
Preservation::Expendable,
|
||||
)?,
|
||||
Error::<T>::PartialTransfer
|
||||
);
|
||||
|
||||
ensure!(
|
||||
lp_balance ==
|
||||
T::PoolAssets::transfer(
|
||||
info.lp_token.clone(),
|
||||
&prior_account,
|
||||
&new_account,
|
||||
lp_balance,
|
||||
Preservation::Expendable,
|
||||
)?,
|
||||
Error::<T>::PartialTransfer
|
||||
);
|
||||
|
||||
// Refund deposits from prior accounts and burn previously minted assets.
|
||||
|
||||
if let Some((depositor, deposit)) =
|
||||
T::AssetsRefund::deposit_held(asset1.clone(), prior_account.clone())
|
||||
{
|
||||
T::AssetsRefund::refund(asset1.clone(), prior_account.clone())?;
|
||||
T::DepositAsset::burn_from(
|
||||
&depositor,
|
||||
deposit + deposit_asset_ed,
|
||||
Precision::Exact,
|
||||
Fortitude::Force,
|
||||
)?;
|
||||
}
|
||||
|
||||
if let Some((depositor, deposit)) =
|
||||
T::AssetsRefund::deposit_held(asset2.clone(), prior_account.clone())
|
||||
{
|
||||
T::AssetsRefund::refund(asset2.clone(), prior_account.clone())?;
|
||||
T::DepositAsset::burn_from(
|
||||
&depositor,
|
||||
deposit + deposit_asset_ed,
|
||||
Precision::Exact,
|
||||
Fortitude::Force,
|
||||
)?;
|
||||
}
|
||||
|
||||
if let Some((depositor, deposit)) =
|
||||
T::PoolAssetsRefund::deposit_held(info.lp_token.clone(), prior_account.clone())
|
||||
{
|
||||
T::PoolAssetsRefund::refund(info.lp_token.clone(), prior_account.clone())?;
|
||||
T::DepositAsset::burn_from(
|
||||
&depositor,
|
||||
deposit + deposit_asset_ed,
|
||||
Precision::Exact,
|
||||
Fortitude::Force,
|
||||
)?;
|
||||
}
|
||||
|
||||
T::PoolAssetsTeam::reset_team(
|
||||
info.lp_token,
|
||||
new_account.clone(),
|
||||
new_account.clone(),
|
||||
new_account.clone(),
|
||||
new_account.clone(),
|
||||
)?;
|
||||
|
||||
Self::deposit_event(Event::MigratedToNewAccount {
|
||||
pool_id,
|
||||
prior_account,
|
||||
new_account,
|
||||
});
|
||||
|
||||
Ok(Pays::No.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Returns the prior and new account IDs for a given pool ID. The prior account ID comes
|
||||
/// first in the tuple.
|
||||
#[cfg(not(any(test, feature = "runtime-benchmarks")))]
|
||||
fn addresses(pool_id: &T::PoolId) -> Option<(T::AccountId, T::AccountId)> {
|
||||
match (
|
||||
T::PriorAccountIdConverter::try_convert(pool_id),
|
||||
T::PoolLocator::address(pool_id),
|
||||
) {
|
||||
(Ok(a), Ok(b)) if a != b => Some((a, b)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the prior and new account IDs for a given pool ID. The prior account ID comes
|
||||
/// first in the tuple.
|
||||
///
|
||||
/// This function is intended for use only in test and benchmark environments. The prior
|
||||
/// account ID represents the new account ID from [`Config::PoolLocator`], allowing the use
|
||||
/// of the main pallet's calls to set up a pool with liquidity placed in that account and
|
||||
/// migrate it to another account, which in this case is the result of
|
||||
/// [`Config::PriorAccountIdConverter`].
|
||||
#[cfg(any(test, feature = "runtime-benchmarks"))]
|
||||
pub(crate) fn addresses(pool_id: &T::PoolId) -> Option<(T::AccountId, T::AccountId)> {
|
||||
match (
|
||||
T::PoolLocator::address(pool_id),
|
||||
T::PriorAccountIdConverter::try_convert(pool_id),
|
||||
) {
|
||||
(Ok(a), Ok(b)) if a != b => Some((a, b)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Test environment for Asset Conversion Ops pallet.
|
||||
|
||||
use super::*;
|
||||
use crate as pallet_asset_conversion_ops;
|
||||
use core::default::Default;
|
||||
use frame_support::{
|
||||
construct_runtime, derive_impl,
|
||||
instances::{Instance1, Instance2},
|
||||
ord_parameter_types, parameter_types,
|
||||
traits::{
|
||||
tokens::{
|
||||
fungible::{NativeFromLeft, NativeOrWithId, UnionOf},
|
||||
imbalance::ResolveAssetTo,
|
||||
},
|
||||
AsEnsureOriginWithArg, ConstU32, ConstU64,
|
||||
},
|
||||
PalletId,
|
||||
};
|
||||
use frame_system::{EnsureSigned, EnsureSignedBy};
|
||||
use pallet_asset_conversion::{self, AccountIdConverter, AccountIdConverterNoSeed, Ascending};
|
||||
use sp_arithmetic::Permill;
|
||||
use sp_runtime::{traits::AccountIdConversion, BuildStorage};
|
||||
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
|
||||
construct_runtime!(
|
||||
pub enum Test
|
||||
{
|
||||
System: frame_system,
|
||||
Balances: pallet_balances,
|
||||
Assets: pallet_assets::<Instance1>,
|
||||
PoolAssets: pallet_assets::<Instance2>,
|
||||
AssetConversion: pallet_asset_conversion,
|
||||
AssetConversionOps: pallet_asset_conversion_ops,
|
||||
}
|
||||
);
|
||||
|
||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
|
||||
impl frame_system::Config for Test {
|
||||
type Block = Block;
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
}
|
||||
|
||||
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
|
||||
impl pallet_balances::Config for Test {
|
||||
type ReserveIdentifier = [u8; 8];
|
||||
type AccountStore = System;
|
||||
}
|
||||
|
||||
#[derive_impl(pallet_assets::config_preludes::TestDefaultConfig)]
|
||||
impl pallet_assets::Config<Instance1> for Test {
|
||||
type Currency = Balances;
|
||||
type CreateOrigin = AsEnsureOriginWithArg<EnsureSigned<Self::AccountId>>;
|
||||
type ForceOrigin = frame_system::EnsureRoot<Self::AccountId>;
|
||||
type Freezer = ();
|
||||
}
|
||||
|
||||
#[derive_impl(pallet_assets::config_preludes::TestDefaultConfig)]
|
||||
impl pallet_assets::Config<Instance2> for Test {
|
||||
type Currency = Balances;
|
||||
type CreateOrigin =
|
||||
AsEnsureOriginWithArg<EnsureSignedBy<AssetConversionOrigin, Self::AccountId>>;
|
||||
type ForceOrigin = frame_system::EnsureRoot<Self::AccountId>;
|
||||
type Freezer = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const AssetConversionPalletId: PalletId = PalletId(*b"py/ascon");
|
||||
pub const Native: NativeOrWithId<u32> = NativeOrWithId::Native;
|
||||
pub storage LiquidityWithdrawalFee: Permill = Permill::from_percent(0);
|
||||
}
|
||||
|
||||
ord_parameter_types! {
|
||||
pub const AssetConversionOrigin: u64 = AccountIdConversion::<u64>::into_account_truncating(&AssetConversionPalletId::get());
|
||||
}
|
||||
|
||||
pub type NativeAndAssets = UnionOf<Balances, Assets, NativeFromLeft, NativeOrWithId<u32>, u64>;
|
||||
pub type PoolIdToAccountId =
|
||||
AccountIdConverter<AssetConversionPalletId, (NativeOrWithId<u32>, NativeOrWithId<u32>)>;
|
||||
pub type AscendingLocator = Ascending<u64, NativeOrWithId<u32>, PoolIdToAccountId>;
|
||||
|
||||
impl pallet_asset_conversion::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Balance = <Self as pallet_balances::Config>::Balance;
|
||||
type HigherPrecisionBalance = sp_core::U256;
|
||||
type AssetKind = NativeOrWithId<u32>;
|
||||
type Assets = NativeAndAssets;
|
||||
type PoolId = (Self::AssetKind, Self::AssetKind);
|
||||
type PoolLocator = AscendingLocator;
|
||||
type PoolAssetId = u32;
|
||||
type PoolAssets = PoolAssets;
|
||||
type PoolSetupFee = ConstU64<100>;
|
||||
type PoolSetupFeeAsset = Native;
|
||||
type PoolSetupFeeTarget = ResolveAssetTo<AssetConversionOrigin, Self::Assets>;
|
||||
type PalletId = AssetConversionPalletId;
|
||||
type WeightInfo = ();
|
||||
type LPFee = ConstU32<3>;
|
||||
type LiquidityWithdrawalFee = LiquidityWithdrawalFee;
|
||||
type MaxSwapPathLength = ConstU32<4>;
|
||||
type MintMinLiquidity = ConstU64<100>;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type BenchmarkHelper = ();
|
||||
}
|
||||
|
||||
pub type OldPoolIdToAccountId =
|
||||
AccountIdConverterNoSeed<(NativeOrWithId<u32>, NativeOrWithId<u32>)>;
|
||||
|
||||
impl pallet_asset_conversion_ops::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type PriorAccountIdConverter = OldPoolIdToAccountId;
|
||||
type AssetsRefund = NativeAndAssets;
|
||||
type PoolAssetsRefund = PoolAssets;
|
||||
type PoolAssetsTeam = PoolAssets;
|
||||
type DepositAsset = Balances;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
|
||||
let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
|
||||
pallet_balances::GenesisConfig::<Test> {
|
||||
balances: vec![(1, 10000), (2, 20000), (3, 30000), (4, 40000)],
|
||||
}
|
||||
.assimilate_storage(&mut t)
|
||||
.unwrap();
|
||||
|
||||
let mut ext = sp_io::TestExternalities::new(t);
|
||||
ext.execute_with(|| System::set_block_number(1));
|
||||
ext
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Asset Conversion Ops pallet tests.
|
||||
|
||||
use crate::{mock::*, *};
|
||||
use frame_support::{
|
||||
assert_noop, assert_ok,
|
||||
traits::{
|
||||
fungible::{Inspect as FungibleInspect, NativeOrWithId},
|
||||
fungibles::{Create, Inspect},
|
||||
Incrementable,
|
||||
},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn migrate_pool_account_id_with_native() {
|
||||
new_test_ext().execute_with(|| {
|
||||
type PoolLocator = <Test as pallet_asset_conversion::Config>::PoolLocator;
|
||||
let user = 1;
|
||||
let token_1 = NativeOrWithId::Native;
|
||||
let token_2 = NativeOrWithId::WithId(2);
|
||||
let pool_id = PoolLocator::pool_id(&token_1, &token_2).unwrap();
|
||||
let lp_token =
|
||||
<Test as pallet_asset_conversion::Config>::PoolAssetId::initial_value().unwrap();
|
||||
|
||||
// setup pool and provide some liquidity.
|
||||
assert_ok!(NativeAndAssets::create(token_2.clone(), user, false, 1));
|
||||
|
||||
assert_ok!(AssetConversion::create_pool(
|
||||
RuntimeOrigin::signed(user),
|
||||
Box::new(token_1.clone()),
|
||||
Box::new(token_2.clone())
|
||||
));
|
||||
|
||||
let ed = Balances::minimum_balance();
|
||||
assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 10000 * 2 + ed));
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000));
|
||||
|
||||
assert_ok!(AssetConversion::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
Box::new(token_1.clone()),
|
||||
Box::new(token_2.clone()),
|
||||
10000,
|
||||
10,
|
||||
10000,
|
||||
10,
|
||||
user,
|
||||
));
|
||||
|
||||
// assert user's balance.
|
||||
assert_eq!(NativeAndAssets::balance(token_1.clone(), &user), 10000 + ed);
|
||||
assert_eq!(NativeAndAssets::balance(token_2.clone(), &user), 1000 - 10);
|
||||
assert_eq!(PoolAssets::balance(lp_token, &user), 216);
|
||||
|
||||
// record total issuances before migration.
|
||||
let total_issuance_token1 = NativeAndAssets::total_issuance(token_1.clone());
|
||||
let total_issuance_token2 = NativeAndAssets::total_issuance(token_2.clone());
|
||||
let total_issuance_lp_token = PoolAssets::total_issuance(lp_token);
|
||||
|
||||
let pool_account = PoolLocator::address(&pool_id).unwrap();
|
||||
let (prior_pool_account, new_pool_account) =
|
||||
AssetConversionOps::addresses(&pool_id).unwrap();
|
||||
assert_eq!(pool_account, prior_pool_account);
|
||||
|
||||
// assert pool's balances before migration.
|
||||
assert_eq!(NativeAndAssets::balance(token_1.clone(), &prior_pool_account), 10000);
|
||||
assert_eq!(NativeAndAssets::balance(token_2.clone(), &prior_pool_account), 10);
|
||||
assert_eq!(PoolAssets::balance(lp_token, &prior_pool_account), 100);
|
||||
|
||||
// migrate.
|
||||
assert_ok!(AssetConversionOps::migrate_to_new_account(
|
||||
RuntimeOrigin::signed(user),
|
||||
Box::new(token_1.clone()),
|
||||
Box::new(token_2.clone()),
|
||||
));
|
||||
|
||||
// assert user's balance has not changed.
|
||||
assert_eq!(NativeAndAssets::balance(token_1.clone(), &user), 10000 + ed);
|
||||
assert_eq!(NativeAndAssets::balance(token_2.clone(), &user), 1000 - 10);
|
||||
assert_eq!(PoolAssets::balance(lp_token, &user), 216);
|
||||
|
||||
// assert pool's balance on new account id is same as on prior account id.
|
||||
assert_eq!(NativeAndAssets::balance(token_1.clone(), &new_pool_account), 10000);
|
||||
assert_eq!(NativeAndAssets::balance(token_2.clone(), &new_pool_account), 10);
|
||||
assert_eq!(PoolAssets::balance(lp_token, &new_pool_account), 100);
|
||||
|
||||
// assert pool's balance on prior account id is zero.
|
||||
assert_eq!(NativeAndAssets::balance(token_1.clone(), &prior_pool_account), 0);
|
||||
assert_eq!(NativeAndAssets::balance(token_2.clone(), &prior_pool_account), 0);
|
||||
assert_eq!(PoolAssets::balance(lp_token, &prior_pool_account), 0);
|
||||
|
||||
// assert total issuance has not changed.
|
||||
assert_eq!(total_issuance_token1, NativeAndAssets::total_issuance(token_1));
|
||||
assert_eq!(total_issuance_token2, NativeAndAssets::total_issuance(token_2));
|
||||
assert_eq!(total_issuance_lp_token, PoolAssets::total_issuance(lp_token));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn migrate_pool_account_id_with_insufficient_assets() {
|
||||
new_test_ext().execute_with(|| {
|
||||
type PoolLocator = <Test as pallet_asset_conversion::Config>::PoolLocator;
|
||||
let user = 1;
|
||||
let token_1 = NativeOrWithId::WithId(1);
|
||||
let token_2 = NativeOrWithId::WithId(2);
|
||||
let pool_id = PoolLocator::pool_id(&token_1, &token_2).unwrap();
|
||||
let lp_token =
|
||||
<Test as pallet_asset_conversion::Config>::PoolAssetId::initial_value().unwrap();
|
||||
|
||||
// setup pool and provide some liquidity.
|
||||
assert_ok!(NativeAndAssets::create(token_1.clone(), user, false, 1));
|
||||
assert_ok!(NativeAndAssets::create(token_2.clone(), user, false, 1));
|
||||
|
||||
assert_ok!(AssetConversion::create_pool(
|
||||
RuntimeOrigin::signed(user),
|
||||
Box::new(token_1.clone()),
|
||||
Box::new(token_2.clone())
|
||||
));
|
||||
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 1, user, 20000));
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000));
|
||||
|
||||
assert_ok!(AssetConversion::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
Box::new(token_1.clone()),
|
||||
Box::new(token_2.clone()),
|
||||
10000,
|
||||
10,
|
||||
10000,
|
||||
10,
|
||||
user,
|
||||
));
|
||||
|
||||
// assert user's balance.
|
||||
assert_eq!(NativeAndAssets::balance(token_1.clone(), &user), 10000);
|
||||
assert_eq!(NativeAndAssets::balance(token_2.clone(), &user), 1000 - 10);
|
||||
assert_eq!(PoolAssets::balance(lp_token, &user), 216);
|
||||
|
||||
// record total issuances before migration.
|
||||
let total_issuance_token1 = NativeAndAssets::total_issuance(token_1.clone());
|
||||
let total_issuance_token2 = NativeAndAssets::total_issuance(token_2.clone());
|
||||
let total_issuance_lp_token = PoolAssets::total_issuance(lp_token);
|
||||
|
||||
let pool_account = PoolLocator::address(&pool_id).unwrap();
|
||||
let (prior_pool_account, new_pool_account) =
|
||||
AssetConversionOps::addresses(&pool_id).unwrap();
|
||||
assert_eq!(pool_account, prior_pool_account);
|
||||
|
||||
// assert pool's balances before migration.
|
||||
assert_eq!(NativeAndAssets::balance(token_1.clone(), &prior_pool_account), 10000);
|
||||
assert_eq!(NativeAndAssets::balance(token_2.clone(), &prior_pool_account), 10);
|
||||
assert_eq!(PoolAssets::balance(lp_token, &prior_pool_account), 100);
|
||||
|
||||
// migrate.
|
||||
assert_ok!(AssetConversionOps::migrate_to_new_account(
|
||||
RuntimeOrigin::signed(user),
|
||||
Box::new(token_1.clone()),
|
||||
Box::new(token_2.clone()),
|
||||
));
|
||||
|
||||
// assert user's balance has not changed.
|
||||
assert_eq!(NativeAndAssets::balance(token_1.clone(), &user), 10000);
|
||||
assert_eq!(NativeAndAssets::balance(token_2.clone(), &user), 1000 - 10);
|
||||
assert_eq!(PoolAssets::balance(lp_token, &user), 216);
|
||||
|
||||
// assert pool's balance on new account id is same as on prior account id.
|
||||
assert_eq!(NativeAndAssets::balance(token_1.clone(), &new_pool_account), 10000);
|
||||
assert_eq!(NativeAndAssets::balance(token_2.clone(), &new_pool_account), 10);
|
||||
assert_eq!(PoolAssets::balance(lp_token, &new_pool_account), 100);
|
||||
|
||||
// assert pool's balance on prior account id is zero.
|
||||
assert_eq!(NativeAndAssets::balance(token_1.clone(), &prior_pool_account), 0);
|
||||
assert_eq!(NativeAndAssets::balance(token_2.clone(), &prior_pool_account), 0);
|
||||
assert_eq!(PoolAssets::balance(lp_token, &prior_pool_account), 0);
|
||||
|
||||
// assert total issuance has not changed.
|
||||
assert_eq!(total_issuance_token1, NativeAndAssets::total_issuance(token_1));
|
||||
assert_eq!(total_issuance_token2, NativeAndAssets::total_issuance(token_2));
|
||||
assert_eq!(total_issuance_lp_token, PoolAssets::total_issuance(lp_token));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn migrate_pool_account_id_with_sufficient_assets() {
|
||||
new_test_ext().execute_with(|| {
|
||||
type PoolLocator = <Test as pallet_asset_conversion::Config>::PoolLocator;
|
||||
let user = 1;
|
||||
let token_1 = NativeOrWithId::WithId(1);
|
||||
let token_2 = NativeOrWithId::WithId(2);
|
||||
let pool_id = PoolLocator::pool_id(&token_1, &token_2).unwrap();
|
||||
let lp_token =
|
||||
<Test as pallet_asset_conversion::Config>::PoolAssetId::initial_value().unwrap();
|
||||
|
||||
// setup pool and provide some liquidity.
|
||||
assert_ok!(NativeAndAssets::create(token_1.clone(), user, true, 1));
|
||||
assert_ok!(NativeAndAssets::create(token_2.clone(), user, true, 1));
|
||||
|
||||
assert_ok!(AssetConversion::create_pool(
|
||||
RuntimeOrigin::signed(user),
|
||||
Box::new(token_1.clone()),
|
||||
Box::new(token_2.clone())
|
||||
));
|
||||
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 1, user, 20000));
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000));
|
||||
|
||||
assert_ok!(AssetConversion::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
Box::new(token_1.clone()),
|
||||
Box::new(token_2.clone()),
|
||||
10000,
|
||||
10,
|
||||
10000,
|
||||
10,
|
||||
user,
|
||||
));
|
||||
|
||||
// assert user's balance.
|
||||
assert_eq!(NativeAndAssets::balance(token_1.clone(), &user), 10000);
|
||||
assert_eq!(NativeAndAssets::balance(token_2.clone(), &user), 1000 - 10);
|
||||
assert_eq!(PoolAssets::balance(lp_token, &user), 216);
|
||||
|
||||
// record total issuances before migration.
|
||||
let total_issuance_token1 = NativeAndAssets::total_issuance(token_1.clone());
|
||||
let total_issuance_token2 = NativeAndAssets::total_issuance(token_2.clone());
|
||||
let total_issuance_lp_token = PoolAssets::total_issuance(lp_token);
|
||||
|
||||
let pool_account = PoolLocator::address(&pool_id).unwrap();
|
||||
let (prior_pool_account, new_pool_account) =
|
||||
AssetConversionOps::addresses(&pool_id).unwrap();
|
||||
assert_eq!(pool_account, prior_pool_account);
|
||||
|
||||
// assert pool's balances before migration.
|
||||
assert_eq!(NativeAndAssets::balance(token_1.clone(), &prior_pool_account), 10000);
|
||||
assert_eq!(NativeAndAssets::balance(token_2.clone(), &prior_pool_account), 10);
|
||||
assert_eq!(PoolAssets::balance(lp_token, &prior_pool_account), 100);
|
||||
|
||||
// migrate.
|
||||
assert_ok!(AssetConversionOps::migrate_to_new_account(
|
||||
RuntimeOrigin::signed(user),
|
||||
Box::new(token_1.clone()),
|
||||
Box::new(token_2.clone()),
|
||||
));
|
||||
|
||||
// assert user's balance has not changed.
|
||||
assert_eq!(NativeAndAssets::balance(token_1.clone(), &user), 10000);
|
||||
assert_eq!(NativeAndAssets::balance(token_2.clone(), &user), 1000 - 10);
|
||||
assert_eq!(PoolAssets::balance(lp_token, &user), 216);
|
||||
|
||||
// assert pool's balance on new account id is same as on prior account id.
|
||||
assert_eq!(NativeAndAssets::balance(token_1.clone(), &new_pool_account), 10000);
|
||||
assert_eq!(NativeAndAssets::balance(token_2.clone(), &new_pool_account), 10);
|
||||
assert_eq!(PoolAssets::balance(lp_token, &new_pool_account), 100);
|
||||
|
||||
// assert pool's balance on prior account id is zero.
|
||||
assert_eq!(NativeAndAssets::balance(token_1.clone(), &prior_pool_account), 0);
|
||||
assert_eq!(NativeAndAssets::balance(token_2.clone(), &prior_pool_account), 0);
|
||||
assert_eq!(PoolAssets::balance(lp_token, &prior_pool_account), 0);
|
||||
|
||||
// assert total issuance has not changed.
|
||||
assert_eq!(total_issuance_token1, NativeAndAssets::total_issuance(token_1));
|
||||
assert_eq!(total_issuance_token2, NativeAndAssets::total_issuance(token_2));
|
||||
assert_eq!(total_issuance_lp_token, PoolAssets::total_issuance(lp_token));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn migrate_empty_pool_account_id() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let user = 1;
|
||||
let token_1 = NativeOrWithId::Native;
|
||||
let token_2 = NativeOrWithId::WithId(2);
|
||||
|
||||
// setup pool and provide some liquidity.
|
||||
assert_ok!(NativeAndAssets::create(token_2.clone(), user, false, 1));
|
||||
|
||||
assert_ok!(AssetConversion::create_pool(
|
||||
RuntimeOrigin::signed(user),
|
||||
Box::new(token_1.clone()),
|
||||
Box::new(token_2.clone())
|
||||
));
|
||||
|
||||
// migrate.
|
||||
assert_noop!(
|
||||
AssetConversionOps::migrate_to_new_account(
|
||||
RuntimeOrigin::signed(user),
|
||||
Box::new(token_1.clone()),
|
||||
Box::new(token_2.clone()),
|
||||
),
|
||||
Error::<Test>::ZeroBalance
|
||||
);
|
||||
});
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Autogenerated weights for `pallet_asset_conversion_ops`
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
|
||||
//! DATE: 2024-04-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! WORST CASE MAP SIZE: `1000000`
|
||||
//! HOSTNAME: `runner-anb7yjbi-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
|
||||
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024`
|
||||
|
||||
// Executed Command:
|
||||
// target/production/substrate-node
|
||||
// benchmark
|
||||
// pallet
|
||||
// --steps=50
|
||||
// --repeat=20
|
||||
// --extrinsic=*
|
||||
// --wasm-execution=compiled
|
||||
// --heap-pages=4096
|
||||
// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json
|
||||
// --pallet=pallet_asset_conversion_ops
|
||||
// --chain=dev
|
||||
// --header=./substrate/HEADER-APACHE2
|
||||
// --output=./substrate/frame/asset-conversion-ops/src/weights.rs
|
||||
// --template=./substrate/.maintain/frame-weight-template.hbs
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Weight functions needed for `pallet_asset_conversion_ops`.
|
||||
pub trait WeightInfo {
|
||||
fn migrate_to_new_account() -> Weight;
|
||||
}
|
||||
|
||||
/// Weights for `pallet_asset_conversion_ops` using the Substrate node and recommended hardware.
|
||||
pub struct SubstrateWeight<T>(PhantomData<T>);
|
||||
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
/// Storage: `AssetConversion::Pools` (r:1 w:0)
|
||||
/// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Assets::Account` (r:4 w:4)
|
||||
/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
|
||||
/// Storage: `PoolAssets::Account` (r:2 w:2)
|
||||
/// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
|
||||
/// Storage: `PoolAssets::Asset` (r:1 w:1)
|
||||
/// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Assets::Asset` (r:2 w:2)
|
||||
/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:2 w:2)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
fn migrate_to_new_account() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `1762`
|
||||
// Estimated: `11426`
|
||||
// Minimum execution time: 223_850_000 picoseconds.
|
||||
Weight::from_parts(231_676_000, 11426)
|
||||
.saturating_add(T::DbWeight::get().reads(12_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(11_u64))
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests.
|
||||
impl WeightInfo for () {
|
||||
/// Storage: `AssetConversion::Pools` (r:1 w:0)
|
||||
/// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Assets::Account` (r:4 w:4)
|
||||
/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
|
||||
/// Storage: `PoolAssets::Account` (r:2 w:2)
|
||||
/// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
|
||||
/// Storage: `PoolAssets::Asset` (r:1 w:1)
|
||||
/// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Assets::Asset` (r:2 w:2)
|
||||
/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:2 w:2)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
fn migrate_to_new_account() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `1762`
|
||||
// Estimated: `11426`
|
||||
// Minimum execution time: 223_850_000 picoseconds.
|
||||
Weight::from_parts(231_676_000, 11426)
|
||||
.saturating_add(RocksDbWeight::get().reads(12_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(11_u64))
|
||||
}
|
||||
}
|
||||
@@ -137,8 +137,11 @@ ord_parameter_types! {
|
||||
}
|
||||
|
||||
pub type NativeAndAssets = UnionOf<Balances, Assets, NativeFromLeft, NativeOrWithId<u32>, u128>;
|
||||
pub type AscendingLocator = Ascending<u128, NativeOrWithId<u32>>;
|
||||
pub type WithFirstAssetLocator = WithFirstAsset<Native, u128, NativeOrWithId<u32>>;
|
||||
pub type PoolIdToAccountId =
|
||||
AccountIdConverter<AssetConversionPalletId, (NativeOrWithId<u32>, NativeOrWithId<u32>)>;
|
||||
pub type AscendingLocator = Ascending<u128, NativeOrWithId<u32>, PoolIdToAccountId>;
|
||||
pub type WithFirstAssetLocator =
|
||||
WithFirstAsset<Native, u128, NativeOrWithId<u32>, PoolIdToAccountId>;
|
||||
|
||||
impl Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
|
||||
@@ -19,6 +19,7 @@ use super::*;
|
||||
use codec::{Decode, Encode, MaxEncodedLen};
|
||||
use core::marker::PhantomData;
|
||||
use scale_info::TypeInfo;
|
||||
use sp_runtime::traits::TryConvert;
|
||||
|
||||
/// Represents a swap path with associated asset amounts indicating how much of the asset needs to
|
||||
/// be deposited to get the following asset's amount withdrawn (this is inclusive of fees).
|
||||
@@ -68,49 +69,59 @@ pub trait PoolLocator<AccountId, AssetKind, PoolId> {
|
||||
///
|
||||
/// The `PoolId` is represented as a tuple of `AssetKind`s with `FirstAsset` always positioned as
|
||||
/// the first element.
|
||||
pub struct WithFirstAsset<FirstAsset, AccountId, AssetKind>(
|
||||
PhantomData<(FirstAsset, AccountId, AssetKind)>,
|
||||
pub struct WithFirstAsset<FirstAsset, AccountId, AssetKind, AccountIdConverter>(
|
||||
PhantomData<(FirstAsset, AccountId, AssetKind, AccountIdConverter)>,
|
||||
);
|
||||
impl<FirstAsset, AccountId, AssetKind> PoolLocator<AccountId, AssetKind, (AssetKind, AssetKind)>
|
||||
for WithFirstAsset<FirstAsset, AccountId, AssetKind>
|
||||
impl<FirstAsset, AccountId, AssetKind, AccountIdConverter>
|
||||
PoolLocator<AccountId, AssetKind, (AssetKind, AssetKind)>
|
||||
for WithFirstAsset<FirstAsset, AccountId, AssetKind, AccountIdConverter>
|
||||
where
|
||||
AssetKind: Eq + Clone + Encode,
|
||||
AccountId: Decode,
|
||||
FirstAsset: Get<AssetKind>,
|
||||
AccountIdConverter: for<'a> TryConvert<&'a (AssetKind, AssetKind), AccountId>,
|
||||
{
|
||||
fn pool_id(asset1: &AssetKind, asset2: &AssetKind) -> Result<(AssetKind, AssetKind), ()> {
|
||||
if asset1 == asset2 {
|
||||
return Err(());
|
||||
}
|
||||
let first = FirstAsset::get();
|
||||
match true {
|
||||
_ if asset1 == asset2 => Err(()),
|
||||
_ if first == *asset1 => Ok((first, asset2.clone())),
|
||||
_ if first == *asset2 => Ok((first, asset1.clone())),
|
||||
_ => Err(()),
|
||||
if first == *asset1 {
|
||||
Ok((first, asset2.clone()))
|
||||
} else if first == *asset2 {
|
||||
Ok((first, asset1.clone()))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
fn address(id: &(AssetKind, AssetKind)) -> Result<AccountId, ()> {
|
||||
let encoded = sp_io::hashing::blake2_256(&Encode::encode(id)[..]);
|
||||
Decode::decode(&mut TrailingZeroInput::new(encoded.as_ref())).map_err(|_| ())
|
||||
AccountIdConverter::try_convert(id).map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Pool locator where the `PoolId` is a tuple of `AssetKind`s arranged in ascending order.
|
||||
pub struct Ascending<AccountId, AssetKind>(PhantomData<(AccountId, AssetKind)>);
|
||||
impl<AccountId, AssetKind> PoolLocator<AccountId, AssetKind, (AssetKind, AssetKind)>
|
||||
for Ascending<AccountId, AssetKind>
|
||||
pub struct Ascending<AccountId, AssetKind, AccountIdConverter>(
|
||||
PhantomData<(AccountId, AssetKind, AccountIdConverter)>,
|
||||
);
|
||||
impl<AccountId, AssetKind, AccountIdConverter>
|
||||
PoolLocator<AccountId, AssetKind, (AssetKind, AssetKind)>
|
||||
for Ascending<AccountId, AssetKind, AccountIdConverter>
|
||||
where
|
||||
AssetKind: Ord + Clone + Encode,
|
||||
AccountId: Decode,
|
||||
AccountIdConverter: for<'a> TryConvert<&'a (AssetKind, AssetKind), AccountId>,
|
||||
{
|
||||
fn pool_id(asset1: &AssetKind, asset2: &AssetKind) -> Result<(AssetKind, AssetKind), ()> {
|
||||
match true {
|
||||
_ if asset1 > asset2 => Ok((asset2.clone(), asset1.clone())),
|
||||
_ if asset1 < asset2 => Ok((asset1.clone(), asset2.clone())),
|
||||
_ => Err(()),
|
||||
if asset1 > asset2 {
|
||||
Ok((asset2.clone(), asset1.clone()))
|
||||
} else if asset1 < asset2 {
|
||||
Ok((asset1.clone(), asset2.clone()))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
fn address(id: &(AssetKind, AssetKind)) -> Result<AccountId, ()> {
|
||||
let encoded = sp_io::hashing::blake2_256(&Encode::encode(id)[..]);
|
||||
Decode::decode(&mut TrailingZeroInput::new(encoded.as_ref())).map_err(|_| ())
|
||||
AccountIdConverter::try_convert(id).map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,3 +142,30 @@ where
|
||||
First::address(id).or(Second::address(id))
|
||||
}
|
||||
}
|
||||
|
||||
/// `PoolId` to `AccountId` conversion.
|
||||
pub struct AccountIdConverter<Seed, PoolId>(PhantomData<(Seed, PoolId)>);
|
||||
impl<Seed, PoolId, AccountId> TryConvert<&PoolId, AccountId> for AccountIdConverter<Seed, PoolId>
|
||||
where
|
||||
PoolId: Encode,
|
||||
AccountId: Decode,
|
||||
Seed: Get<PalletId>,
|
||||
{
|
||||
fn try_convert(id: &PoolId) -> Result<AccountId, &PoolId> {
|
||||
sp_io::hashing::blake2_256(&Encode::encode(&(Seed::get(), id))[..])
|
||||
.using_encoded(|e| Decode::decode(&mut TrailingZeroInput::new(e)).map_err(|_| id))
|
||||
}
|
||||
}
|
||||
|
||||
/// `PoolId` to `AccountId` conversion without an addition arguments to the seed.
|
||||
pub struct AccountIdConverterNoSeed<PoolId>(PhantomData<PoolId>);
|
||||
impl<PoolId, AccountId> TryConvert<&PoolId, AccountId> for AccountIdConverterNoSeed<PoolId>
|
||||
where
|
||||
PoolId: Encode,
|
||||
AccountId: Decode,
|
||||
{
|
||||
fn try_convert(id: &PoolId) -> Result<AccountId, &PoolId> {
|
||||
sp_io::hashing::blake2_256(&Encode::encode(id)[..])
|
||||
.using_encoded(|e| Decode::decode(&mut TrailingZeroInput::new(e)).map_err(|_| id))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,11 +368,14 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a `DepositFrom` of an account only if balance is zero.
|
||||
/// Refunds the `DepositFrom` of an account only if its balance is zero.
|
||||
///
|
||||
/// If the `maybe_check_caller` parameter is specified, it must match the account that provided
|
||||
/// the deposit or must be the admin of the asset.
|
||||
pub(super) fn do_refund_other(
|
||||
id: T::AssetId,
|
||||
who: &T::AccountId,
|
||||
caller: &T::AccountId,
|
||||
maybe_check_caller: Option<T::AccountId>,
|
||||
) -> DispatchResult {
|
||||
let mut account = Account::<T, I>::get(&id, &who).ok_or(Error::<T, I>::NoDeposit)?;
|
||||
let (depositor, deposit) =
|
||||
@@ -380,7 +383,9 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
|
||||
ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
|
||||
ensure!(!account.status.is_frozen(), Error::<T, I>::Frozen);
|
||||
ensure!(caller == &depositor || caller == &details.admin, Error::<T, I>::NoPermission);
|
||||
if let Some(caller) = maybe_check_caller {
|
||||
ensure!(caller == depositor || caller == details.admin, Error::<T, I>::NoPermission);
|
||||
}
|
||||
ensure!(account.balance.is_zero(), Error::<T, I>::WouldBurn);
|
||||
|
||||
T::Currency::unreserve(&depositor, deposit);
|
||||
@@ -1013,4 +1018,28 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// Reset the team for the asset with the given `id`.
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - `id`: The identifier of the asset for which the team is being reset.
|
||||
/// - `owner`: The new `owner` account for the asset.
|
||||
/// - `admin`: The new `admin` account for the asset.
|
||||
/// - `issuer`: The new `issuer` account for the asset.
|
||||
/// - `freezer`: The new `freezer` account for the asset.
|
||||
pub(crate) fn do_reset_team(
|
||||
id: T::AssetId,
|
||||
owner: T::AccountId,
|
||||
admin: T::AccountId,
|
||||
issuer: T::AccountId,
|
||||
freezer: T::AccountId,
|
||||
) -> DispatchResult {
|
||||
let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
|
||||
d.owner = owner;
|
||||
d.admin = admin;
|
||||
d.issuer = issuer;
|
||||
d.freezer = freezer;
|
||||
Asset::<T, I>::insert(&id, d);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,3 +308,35 @@ impl<T: Config<I>, I: 'static> fungibles::InspectEnumerable<T::AccountId> for Pa
|
||||
Asset::<T, I>::iter_keys()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config<I>, I: 'static> fungibles::roles::ResetTeam<T::AccountId> for Pallet<T, I> {
|
||||
fn reset_team(
|
||||
id: T::AssetId,
|
||||
owner: T::AccountId,
|
||||
admin: T::AccountId,
|
||||
issuer: T::AccountId,
|
||||
freezer: T::AccountId,
|
||||
) -> DispatchResult {
|
||||
Self::do_reset_team(id, owner, admin, issuer, freezer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config<I>, I: 'static> fungibles::Refund<T::AccountId> for Pallet<T, I> {
|
||||
type AssetId = T::AssetId;
|
||||
type Balance = DepositBalanceOf<T, I>;
|
||||
fn deposit_held(id: Self::AssetId, who: T::AccountId) -> Option<(T::AccountId, Self::Balance)> {
|
||||
use ExistenceReason::*;
|
||||
match Account::<T, I>::get(&id, &who).ok_or(Error::<T, I>::NoDeposit).ok()?.reason {
|
||||
DepositHeld(b) => Some((who, b)),
|
||||
DepositFrom(d, b) => Some((d, b)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn refund(id: Self::AssetId, who: T::AccountId) -> DispatchResult {
|
||||
match Self::deposit_held(id.clone(), who.clone()) {
|
||||
Some((d, _)) if d == who => Self::do_refund(id, who, false),
|
||||
Some(..) => Self::do_refund_other(id, &who, None),
|
||||
None => Err(Error::<T, I>::NoDeposit.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1644,7 +1644,7 @@ pub mod pallet {
|
||||
let origin = ensure_signed(origin)?;
|
||||
let who = T::Lookup::lookup(who)?;
|
||||
let id: T::AssetId = id.into();
|
||||
Self::do_refund_other(id, &who, &origin)
|
||||
Self::do_refund_other(id, &who, Some(origin))
|
||||
}
|
||||
|
||||
/// Disallow further unprivileged transfers of an asset `id` to and from an account `who`.
|
||||
|
||||
@@ -925,3 +925,28 @@ impl<
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Left: fungible::Inspect<AccountId>,
|
||||
Right: fungibles::Inspect<AccountId> + fungibles::Refund<AccountId>,
|
||||
Criterion: Convert<AssetKind, Either<(), <Right as fungibles::Refund<AccountId>>::AssetId>>,
|
||||
AssetKind: AssetId,
|
||||
AccountId,
|
||||
> fungibles::Refund<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
|
||||
{
|
||||
type AssetId = AssetKind;
|
||||
type Balance = <Right as fungibles::Refund<AccountId>>::Balance;
|
||||
|
||||
fn deposit_held(asset: AssetKind, who: AccountId) -> Option<(AccountId, Self::Balance)> {
|
||||
match Criterion::convert(asset) {
|
||||
Left(()) => None,
|
||||
Right(a) => <Right as fungibles::Refund<AccountId>>::deposit_held(a, who),
|
||||
}
|
||||
}
|
||||
fn refund(asset: AssetKind, who: AccountId) -> DispatchResult {
|
||||
match Criterion::convert(asset) {
|
||||
Left(()) => Err(DispatchError::Unavailable),
|
||||
Right(a) => <Right as fungibles::Refund<AccountId>>::refund(a, who),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Traits for creating and destroying assets.
|
||||
//! Traits for creating, editing and destroying assets.
|
||||
//!
|
||||
//! See the [`crate::traits::fungibles`] doc for more information about fungibles traits.
|
||||
|
||||
use sp_runtime::{DispatchError, DispatchResult};
|
||||
|
||||
use super::Inspect;
|
||||
use crate::traits::tokens::{AssetId, Balance};
|
||||
use sp_runtime::{DispatchError, DispatchResult};
|
||||
|
||||
/// Trait for providing the ability to create new fungible assets.
|
||||
pub trait Create<AccountId>: Inspect<AccountId> {
|
||||
@@ -34,6 +34,22 @@ pub trait Create<AccountId>: Inspect<AccountId> {
|
||||
) -> DispatchResult;
|
||||
}
|
||||
|
||||
/// Trait for refunding the existence deposit of a target asset account.
|
||||
///
|
||||
/// The existence deposit might by necessary and present in cases where the asset held by the
|
||||
/// account is insufficient for the required storage, or when the system cannot provide a consumer
|
||||
/// reference for any reason.
|
||||
pub trait Refund<AccountId> {
|
||||
/// Means of identifying one asset class from another.
|
||||
type AssetId: AssetId;
|
||||
/// Scalar type for representing deposit balance of an account.
|
||||
type Balance: Balance;
|
||||
/// Returns the amount of account deposit and depositor address, if any.
|
||||
fn deposit_held(id: Self::AssetId, who: AccountId) -> Option<(AccountId, Self::Balance)>;
|
||||
/// Return the deposit (if any) of a target asset account.
|
||||
fn refund(id: Self::AssetId, who: AccountId) -> DispatchResult;
|
||||
}
|
||||
|
||||
/// Trait for providing the ability to destroy existing fungible assets.
|
||||
pub trait Destroy<AccountId>: Inspect<AccountId> {
|
||||
/// Start the destruction an existing fungible asset.
|
||||
|
||||
@@ -44,7 +44,7 @@ pub use hold::{
|
||||
Unbalanced as UnbalancedHold,
|
||||
};
|
||||
pub use imbalance::{Credit, Debt, HandleImbalanceDrop, Imbalance};
|
||||
pub use lifetime::{Create, Destroy};
|
||||
pub use lifetime::{Create, Destroy, Refund};
|
||||
pub use regular::{
|
||||
Balanced, DecreaseIssuance, Dust, IncreaseIssuance, Inspect, Mutate, Unbalanced,
|
||||
};
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
//!
|
||||
//! See the [`crate::traits::fungibles`] doc for more information about fungibles traits.
|
||||
|
||||
use sp_runtime::DispatchResult;
|
||||
|
||||
pub trait Inspect<AccountId>: super::Inspect<AccountId> {
|
||||
// Get owner for an AssetId.
|
||||
fn owner(asset: Self::AssetId) -> Option<AccountId>;
|
||||
@@ -29,3 +31,22 @@ pub trait Inspect<AccountId>: super::Inspect<AccountId> {
|
||||
// Get freezer for an AssetId.
|
||||
fn freezer(asset: Self::AssetId) -> Option<AccountId>;
|
||||
}
|
||||
|
||||
/// Trait for resetting the team configuration of an existing fungible asset.
|
||||
pub trait ResetTeam<AccountId>: super::Inspect<AccountId> {
|
||||
/// Reset the team for the asset with the given `id`.
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - `id`: The identifier of the asset for which the team is being reset.
|
||||
/// - `owner`: The new `owner` account for the asset.
|
||||
/// - `admin`: The new `admin` account for the asset.
|
||||
/// - `issuer`: The new `issuer` account for the asset.
|
||||
/// - `freezer`: The new `freezer` account for the asset.
|
||||
fn reset_team(
|
||||
id: Self::AssetId,
|
||||
owner: AccountId,
|
||||
admin: AccountId,
|
||||
issuer: AccountId,
|
||||
freezer: AccountId,
|
||||
) -> DispatchResult;
|
||||
}
|
||||
|
||||
@@ -904,3 +904,35 @@ impl<
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Left: fungibles::Inspect<AccountId> + fungibles::Refund<AccountId>,
|
||||
Right: fungibles::Inspect<AccountId>
|
||||
+ fungibles::Refund<AccountId, Balance = <Left as fungibles::Refund<AccountId>>::Balance>,
|
||||
Criterion: Convert<
|
||||
AssetKind,
|
||||
Either<
|
||||
<Left as fungibles::Refund<AccountId>>::AssetId,
|
||||
<Right as fungibles::Refund<AccountId>>::AssetId,
|
||||
>,
|
||||
>,
|
||||
AssetKind: AssetId,
|
||||
AccountId,
|
||||
> fungibles::Refund<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
|
||||
{
|
||||
type AssetId = AssetKind;
|
||||
type Balance = <Left as fungibles::Refund<AccountId>>::Balance;
|
||||
|
||||
fn deposit_held(asset: AssetKind, who: AccountId) -> Option<(AccountId, Self::Balance)> {
|
||||
match Criterion::convert(asset) {
|
||||
Left(a) => <Left as fungibles::Refund<AccountId>>::deposit_held(a, who),
|
||||
Right(a) => <Right as fungibles::Refund<AccountId>>::deposit_held(a, who),
|
||||
}
|
||||
}
|
||||
fn refund(asset: AssetKind, who: AccountId) -> DispatchResult {
|
||||
match Criterion::convert(asset) {
|
||||
Left(a) => <Left as fungibles::Refund<AccountId>>::refund(a, who),
|
||||
Right(a) => <Right as fungibles::Refund<AccountId>>::refund(a, who),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,6 +244,11 @@ ord_parameter_types! {
|
||||
pub const AssetConversionOrigin: u64 = AccountIdConversion::<u64>::into_account_truncating(&AssetConversionPalletId::get());
|
||||
}
|
||||
|
||||
pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter<
|
||||
AssetConversionPalletId,
|
||||
(NativeOrWithId<u32>, NativeOrWithId<u32>),
|
||||
>;
|
||||
|
||||
impl pallet_asset_conversion::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Balance = Balance;
|
||||
@@ -252,8 +257,8 @@ impl pallet_asset_conversion::Config for Runtime {
|
||||
type Assets = UnionOf<Balances, Assets, NativeFromLeft, NativeOrWithId<u32>, AccountId>;
|
||||
type PoolId = (Self::AssetKind, Self::AssetKind);
|
||||
type PoolLocator = Chain<
|
||||
WithFirstAsset<Native, AccountId, NativeOrWithId<u32>>,
|
||||
Ascending<AccountId, NativeOrWithId<u32>>,
|
||||
WithFirstAsset<Native, AccountId, NativeOrWithId<u32>, PoolIdToAccountId>,
|
||||
Ascending<AccountId, NativeOrWithId<u32>, PoolIdToAccountId>,
|
||||
>;
|
||||
type PoolAssetId = u32;
|
||||
type PoolAssets = PoolAssets;
|
||||
|
||||
Reference in New Issue
Block a user