From 20615a1596f4d23c87da8e62867b88f4acab42be Mon Sep 17 00:00:00 2001 From: illuzen Date: Fri, 31 Jul 2026 22:48:05 +0800 Subject: [PATCH 1/5] Remove assets pallet from the runtime, keeping pallet and call indices stable. Assets and AssetsHolder are unused; vacate indices 17/18 and drop asset scheduling extrinsics so Multisig/Wormhole and recover_funds keep their encodings. Co-authored-by: Cursor --- Cargo.lock | 8 - node/Cargo.toml | 2 - node/src/txwatch.rs | 60 +---- pallets/multisig/Cargo.toml | 2 - pallets/multisig/src/mock.rs | 56 +--- pallets/reversible-transfers/Cargo.toml | 6 - .../reversible-transfers/src/benchmarking.rs | 65 +---- pallets/reversible-transfers/src/lib.rs | 241 +++++------------- .../reversible-transfers/src/tests/mock.rs | 56 +--- .../src/tests/test_reversible_transfers.rs | 105 +------- pallets/reversible-transfers/src/weights.rs | 63 ----- pallets/wormhole/Cargo.toml | 2 - pallets/wormhole/src/lib.rs | 10 - pallets/wormhole/src/mock.rs | 30 +-- runtime/Cargo.toml | 6 - runtime/src/configs/mod.rs | 72 +----- runtime/src/genesis_config_presets.rs | 15 +- runtime/src/lib.rs | 6 +- runtime/src/transaction_extensions.rs | 123 +-------- 19 files changed, 98 insertions(+), 830 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78a9aaa6..6e71df8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5958,8 +5958,6 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", - "pallet-assets", - "pallet-assets-holder", "pallet-balances", "pallet-preimage", "pallet-recovery", @@ -6072,8 +6070,6 @@ dependencies = [ "frame-support", "frame-system", "log", - "pallet-assets", - "pallet-assets-holder", "pallet-balances", "pallet-preimage", "pallet-recovery", @@ -6229,7 +6225,6 @@ dependencies = [ "hex", "lazy_static", "log", - "pallet-assets", "pallet-balances", "pallet-zk-tree", "parity-scale-codec", @@ -7769,7 +7764,6 @@ dependencies = [ "hex", "jsonrpsee", "log", - "pallet-assets", "pallet-balances", "pallet-transaction-payment", "pallet-transaction-payment-rpc", @@ -7837,8 +7831,6 @@ dependencies = [ "frame-try-runtime", "lazy_static", "log", - "pallet-assets", - "pallet-assets-holder", "pallet-balances", "pallet-mining-rewards", "pallet-multisig", diff --git a/node/Cargo.toml b/node/Cargo.toml index a0cf37df..382ebb94 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -26,8 +26,6 @@ futures = { features = ["thread-pool"], workspace = true } hex = { workspace = true, default-features = false } jsonrpsee = { features = ["server"], workspace = true } log.workspace = true -pallet-assets.default-features = true -pallet-assets.workspace = true pallet-balances.default-features = true pallet-balances.workspace = true pallet-transaction-payment.default-features = true diff --git a/node/src/txwatch.rs b/node/src/txwatch.rs index 74d2dc60..eea8839c 100644 --- a/node/src/txwatch.rs +++ b/node/src/txwatch.rs @@ -218,15 +218,6 @@ fn extract_all_transfers( if let MultiAddress::Id(id) = dest { results.push((id.clone(), *value, None)); }, - RuntimeCall::Assets(pallet_assets::Call::transfer { id, target: dest, amount }) | - RuntimeCall::Assets(pallet_assets::Call::transfer_keep_alive { - id, - target: dest, - amount, - }) => - if let MultiAddress::Id(d) = dest { - results.push((d.clone(), *amount, Some(id.0))); - }, RuntimeCall::Utility(pallet_utility::Call::batch { calls }) | RuntimeCall::Utility(pallet_utility::Call::batch_all { calls }) | RuntimeCall::Utility(pallet_utility::Call::force_batch { calls }) => @@ -286,22 +277,6 @@ mod tests { }) } - fn asset_transfer(asset_id: u32, dest: &AccountId, amount: Balance) -> RuntimeCall { - RuntimeCall::Assets(pallet_assets::Call::transfer { - id: codec::Compact(asset_id), - target: addr(dest), - amount, - }) - } - - fn asset_transfer_keep_alive(asset_id: u32, dest: &AccountId, amount: Balance) -> RuntimeCall { - RuntimeCall::Assets(pallet_assets::Call::transfer_keep_alive { - id: codec::Compact(asset_id), - target: addr(dest), - amount, - }) - } - fn batch(calls: Vec) -> RuntimeCall { RuntimeCall::Utility(pallet_utility::Call::batch { calls }) } @@ -335,46 +310,25 @@ mod tests { assert!(result.is_empty()); } - #[test] - fn detects_asset_transfer() { - let call = asset_transfer(42, &merchant(), 500); - let result = extract_transfers_to(&call, &merchant()); - assert_eq!(result, vec![(500, Some(42))]); - } - - #[test] - fn detects_asset_transfer_keep_alive() { - let call = asset_transfer_keep_alive(7, &merchant(), 1000); - let result = extract_transfers_to(&call, &merchant()); - assert_eq!(result, vec![(1000, Some(7))]); - } - - #[test] - fn ignores_asset_transfer_to_different_address() { - let call = asset_transfer(42, &other(), 500); - let result = extract_transfers_to(&call, &merchant()); - assert!(result.is_empty()); - } - #[test] fn detects_transfers_inside_batch() { let call = batch(vec![ native_transfer(&merchant(), 10 * UNIT), native_transfer(&other(), 20 * UNIT), - asset_transfer(5, &merchant(), 300), + native_transfer(&merchant(), 300), ]); let result = extract_transfers_to(&call, &merchant()); - assert_eq!(result, vec![(10 * UNIT, None), (300, Some(5))]); + assert_eq!(result, vec![(10 * UNIT, None), (300, None)]); } #[test] fn detects_transfers_inside_batch_all() { let call = batch_all(vec![ native_transfer(&merchant(), 10 * UNIT), - asset_transfer(1, &merchant(), 200), + native_transfer(&merchant(), 200), ]); let result = extract_transfers_to(&call, &merchant()); - assert_eq!(result, vec![(10 * UNIT, None), (200, Some(1))]); + assert_eq!(result, vec![(10 * UNIT, None), (200, None)]); } #[test] @@ -406,7 +360,7 @@ mod tests { let call = batch(vec![ native_transfer(&other(), 100 * UNIT), RuntimeCall::System(frame_system::Call::remark { remark: vec![] }), - asset_transfer(1, &customer(), 50), + native_transfer(&customer(), 50), ]); let result = extract_transfers_to(&call, &merchant()); assert!(result.is_empty()); @@ -475,12 +429,12 @@ mod tests { let call = batch(vec![ native_transfer(&merchant(), 10 * UNIT), native_transfer(&other(), 20 * UNIT), - asset_transfer(5, &customer(), 300), + native_transfer(&customer(), 300), ]); let all = extract_all_transfers(&call, 0); assert_eq!(all.len(), 3); assert_eq!(all[0], (merchant(), 10 * UNIT, None)); assert_eq!(all[1], (other(), 20 * UNIT, None)); - assert_eq!(all[2], (customer(), 300, Some(5))); + assert_eq!(all[2], (customer(), 300, None)); } } diff --git a/pallets/multisig/Cargo.toml b/pallets/multisig/Cargo.toml index 30cd932a..667fe18e 100644 --- a/pallets/multisig/Cargo.toml +++ b/pallets/multisig/Cargo.toml @@ -30,8 +30,6 @@ sp-runtime.workspace = true [dev-dependencies] frame-support = { workspace = true, features = ["experimental"], default-features = true } frame-system = { workspace = true, default-features = true } -pallet-assets = { workspace = true, default-features = true } -pallet-assets-holder = { workspace = true, default-features = true } pallet-balances = { workspace = true, features = ["std"] } pallet-preimage = { workspace = true, default-features = true } pallet-recovery = { workspace = true, default-features = true } diff --git a/pallets/multisig/src/mock.rs b/pallets/multisig/src/mock.rs index 393580a5..dd6d78ab 100644 --- a/pallets/multisig/src/mock.rs +++ b/pallets/multisig/src/mock.rs @@ -87,12 +87,6 @@ mod runtime { #[runtime::pallet_index(6)] pub type Utility = pallet_utility::Pallet; - #[runtime::pallet_index(7)] - pub type Assets = pallet_assets::Pallet; - - #[runtime::pallet_index(8)] - pub type AssetsHolder = pallet_assets_holder::Pallet; - #[runtime::pallet_index(9)] pub type ReversibleTransfers = pallet_reversible_transfers::Pallet; @@ -110,16 +104,6 @@ impl TryFrom for pallet_balances::Call { } } -impl TryFrom for pallet_assets::Call { - type Error = (); - fn try_from(call: RuntimeCall) -> Result { - match call { - RuntimeCall::Assets(c) => Ok(c), - _ => Err(()), - } - } -} - #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; @@ -259,6 +243,7 @@ impl qp_wormhole::TransferProofRecorder for MockProofRe } impl pallet_reversible_transfers::Config for Test { + type AssetId = u32; type SchedulerOrigin = OriginCaller; type RuntimeHoldReason = RuntimeHoldReason; type Scheduler = Scheduler; @@ -277,45 +262,6 @@ impl pallet_reversible_transfers::Config for Test { type ProofRecorder = MockProofRecorder; } -parameter_types! { - pub const AssetDeposit: Balance = 0; - pub const AssetAccountDeposit: Balance = 0; - pub const AssetsStringLimit: u32 = 50; - pub const MetadataDepositBase: Balance = 0; - pub const MetadataDepositPerByte: Balance = 0; -} - -impl pallet_assets::Config for Test { - type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type AssetId = u32; - type AssetIdParameter = codec::Compact; - type Currency = Balances; - type CreateOrigin = - frame_support::traits::AsEnsureOriginWithArg>; - type ForceOrigin = frame_system::EnsureRoot; - type AssetDeposit = AssetDeposit; - type MetadataDepositBase = MetadataDepositBase; - type MetadataDepositPerByte = MetadataDepositPerByte; - type ApprovalDeposit = sp_core::ConstU128<0>; - type StringLimit = AssetsStringLimit; - type Freezer = (); - type Extra = (); - type WeightInfo = (); - type CallbackHandle = pallet_assets::AutoIncAssetId; - type AssetAccountDeposit = AssetAccountDeposit; - type RemoveItemsLimit = frame_support::traits::ConstU32<1000>; - type Holder = pallet_assets_holder::Pallet; - type ReserveData = (); - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = (); -} - -impl pallet_assets_holder::Config for Test { - type RuntimeEvent = RuntimeEvent; - type RuntimeHoldReason = RuntimeHoldReason; -} - parameter_types! { pub const ConfigDepositBase: Balance = 1; pub const FriendDepositFactor: Balance = 1; diff --git a/pallets/reversible-transfers/Cargo.toml b/pallets/reversible-transfers/Cargo.toml index 08713427..259f5e9d 100644 --- a/pallets/reversible-transfers/Cargo.toml +++ b/pallets/reversible-transfers/Cargo.toml @@ -17,8 +17,6 @@ frame-benchmarking = { optional = true, workspace = true } frame-support.workspace = true frame-system.workspace = true log.workspace = true -pallet-assets.workspace = true -pallet-assets-holder.workspace = true pallet-balances.workspace = true pallet-recovery.workspace = true qp-scheduler.workspace = true @@ -46,8 +44,6 @@ std = [ "frame-support/std", "frame-system/std", "log/std", - "pallet-assets-holder/std", - "pallet-assets/std", "pallet-balances/std", "pallet-preimage/std", "pallet-recovery/std", @@ -67,13 +63,11 @@ runtime-benchmarks = [ "frame-benchmarking", "frame-support/runtime-benchmarks", "frame-system/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-assets/try-runtime", "sp-runtime/try-runtime", ] diff --git a/pallets/reversible-transfers/src/benchmarking.rs b/pallets/reversible-transfers/src/benchmarking.rs index 80e1d17a..9ecf6982 100644 --- a/pallets/reversible-transfers/src/benchmarking.rs +++ b/pallets/reversible-transfers/src/benchmarking.rs @@ -4,7 +4,7 @@ use super::*; use crate::Pallet as ReversibleTransfers; // Alias the pallet use frame_benchmarking::{account as benchmark_account, v2::*, BenchmarkError}; -use frame_support::traits::{fungible::Mutate, fungibles::Create, Get}; +use frame_support::traits::{fungible::Mutate, Get}; use frame_system::RawOrigin; use sp_runtime::{ traits::{BlockNumberProvider, Hash, One, StaticLookup}, @@ -76,10 +76,9 @@ type BalanceOf = ::Balance; #[benchmarks( where T: Send + Sync, - T: Config + pallet_balances::Config + pallet_assets::Config, + T: Config + pallet_balances::Config, ::Balance: From + Into, - ::AssetId: From, - RuntimeCallOf: From> + From> + From>, + RuntimeCallOf: From> + From>, )] mod benchmarks { use super::*; @@ -135,64 +134,6 @@ mod benchmarks { Ok(()) } - #[benchmark] - fn schedule_asset_transfer() -> Result<(), BenchmarkError> { - let caller: T::AccountId = whitelisted_caller(); - fund_account::(&caller, BalanceOf::::from(10000u128)); - let recipient: T::AccountId = benchmark_account("recipient", 0, SEED); - let guardian: T::AccountId = benchmark_account("guardian", 1, SEED); - let transfer_amount: BalanceOf = 100u128.into(); - - // Create and mint an asset for the benchmark - let asset_id: ::AssetId = 1u32.into(); - let min_balance: BalanceOf = 1u128.into(); - - // Create the asset with caller as admin - as Create>::create( - asset_id.clone(), - caller.clone(), - true, // is_sufficient - min_balance, - )?; - - // Mint more assets than transfer amount to ensure sufficient balance for hold - let mint_amount: BalanceOf = 10000u128.into(); - as frame_support::traits::fungibles::Mutate>::mint_into( - asset_id.clone(), - &caller, - mint_amount, - )?; - - // Setup caller as high security - let delay = T::DefaultDelay::get(); - setup_high_security_account::(caller.clone(), delay, guardian.clone()); - - // Build the expected call for tx_id calculation - let recipient_lookup = ::Lookup::unlookup(recipient.clone()); - let asset_call: RuntimeCallOf = pallet_assets::Call::::transfer_keep_alive { - id: asset_id.clone().into(), - target: recipient_lookup.clone(), - amount: transfer_amount, - } - .into(); - let current_tx_id = NextTransactionId::::get(); - let tx_id = T::Hashing::hash_of(&(caller.clone(), asset_call, current_tx_id).encode()); - - // Schedule the asset transfer - #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), asset_id, recipient_lookup, transfer_amount); - - assert!(PendingTransfers::::contains_key(tx_id)); - let execute_at = ::BlockNumberProvider::current_block_number() - .saturating_add( - delay.as_block_number().expect("Timestamp delay not supported in benchmark"), - ); - let task_name = ReversibleTransfers::::make_schedule_id(&tx_id)?; - assert_eq!(T::Scheduler::next_dispatch_time(task_name)?, execute_at); - - Ok(()) - } - #[benchmark] fn cancel() -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); diff --git a/pallets/reversible-transfers/src/lib.rs b/pallets/reversible-transfers/src/lib.rs index 343ffb0d..2c6e6502 100644 --- a/pallets/reversible-transfers/src/lib.rs +++ b/pallets/reversible-transfers/src/lib.rs @@ -27,7 +27,7 @@ pub use weights::WeightInfo; use alloc::vec::Vec; use frame_support::{ pallet_prelude::*, - traits::tokens::{fungibles::MutateHold as AssetsHold, Fortitude, Restriction}, + traits::tokens::{Fortitude, Restriction}, }; use frame_system::pallet_prelude::*; use qp_scheduler::{BlockNumberOrTimestamp, DispatchTime, ScheduleNamed}; @@ -68,7 +68,7 @@ pub struct HighSecurityAccountData { /// Stores all information needed to execute or cancel a scheduled transfer. /// The asset_id field determines the transfer type: /// - `None`: Native balance transfer via `pallet_balances` -/// - `Some(id)`: Asset transfer via `pallet_assets` +/// - `Some(id)`: Reserved for unsupported asset transfers #[derive(Encode, Decode, MaxEncodedLen, Clone, Default, TypeInfo, Debug, PartialEq, Eq)] pub struct PendingTransfer { /// The account that scheduled the transaction @@ -77,7 +77,7 @@ pub struct PendingTransfer { pub to: AccountId, /// The guardian who can cancel this transfer pub guardian: AccountId, - /// The asset being transferred. `None` for native balance, `Some(id)` for assets. + /// The asset being transferred. `None` for native balance; `Some(id)` is unsupported. pub asset_id: Option, /// Amount frozen for the transaction pub amount: Balance, @@ -87,16 +87,11 @@ pub struct PendingTransfer { type BalanceOf = ::Balance; /// AssetId type -type AssetIdOf = ::AssetId; +type AssetIdOf = ::AssetId; /// Canonical RuntimeCall for this pallet (disambiguates multiple `RuntimeCall` providers) type RuntimeCallOf = ::RuntimeCall; -/// Type aliases for asset holder pallet -type AssetsHoldReasonOf = ::RuntimeHoldReason; -type AccountIdOf = ::AccountId; -type AssetsHolderOf = pallet_assets_holder::Pallet; - type PendingTransferOf = PendingTransfer<::AccountId, BalanceOf, AssetIdOf>; @@ -133,15 +128,14 @@ pub mod pallet { pub trait Config: frame_system::Config< RuntimeCall: From> - + From> + From> + Dispatchable - + TryInto> - + TryInto>, + + TryInto>, > + pallet_balances::Config::RuntimeHoldReason> - + pallet_assets::Config::Balance> - + pallet_assets_holder::Config::RuntimeHoldReason> { + /// Identifier used to retain the wire format of unsupported asset transfer calls. + type AssetId: Parameter + Member + MaxEncodedLen + Clone + PartialEq + Eq + Default; + /// Scheduler for the runtime. We use the Named scheduler for cancellability. type Scheduler: ScheduleNamed< BlockNumberFor, @@ -329,6 +323,8 @@ pub mod pallet { AccountAlreadyReversibleCannotScheduleOneTime, /// The guardian has reached the maximum number of accounts they can protect. TooManyGuardianAccounts, + /// Asset transfers are not supported. + AssetsNotSupported, } #[pallet::call] @@ -347,8 +343,6 @@ pub mod pallet { /// to only the following operations: /// - [`schedule_transfer`](Self::schedule_transfer) - Schedule delayed native token /// transfers - /// - [`schedule_asset_transfer`](Self::schedule_asset_transfer) - Schedule delayed asset - /// transfers /// - [`cancel`](Self::cancel) - Cancel pending transfers /// - [`recover_funds`](Self::recover_funds) - Guardian-initiated emergency fund recovery /// @@ -363,8 +357,7 @@ pub mod pallet { /// repeatedly as needed. /// /// Users who no longer wish to use high-security features can simply transfer their - /// funds to a different account using [`schedule_transfer`](Self::schedule_transfer) - /// or [`schedule_asset_transfer`](Self::schedule_asset_transfer). + /// funds to a different account using [`schedule_transfer`](Self::schedule_transfer). /// /// # Parameters /// @@ -485,46 +478,9 @@ pub mod pallet { Self::do_schedule_transfer_inner(who.clone(), dest, who, amount, delay, None) } - /// Schedule an asset transfer (pallet-assets) for delayed execution using the configured - /// delay. - #[pallet::call_index(5)] - #[pallet::weight(::WeightInfo::schedule_asset_transfer())] - pub fn schedule_asset_transfer( - origin: OriginFor, - asset_id: AssetIdOf, - dest: <::Lookup as StaticLookup>::Source, - amount: BalanceOf, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let HighSecurityAccountData { delay, guardian, .. } = - Self::high_security_accounts(&who).ok_or(Error::::AccountNotHighSecurity)?; - - Self::do_schedule_transfer_inner(who, dest, guardian, amount, delay, Some(asset_id)) - } - - /// Schedule an asset transfer (pallet-assets) with a custom one-time delay. - #[pallet::call_index(6)] - #[pallet::weight(::WeightInfo::schedule_asset_transfer())] - pub fn schedule_asset_transfer_with_delay( - origin: OriginFor, - asset_id: AssetIdOf, - dest: <::Lookup as StaticLookup>::Source, - amount: BalanceOf, - delay: BlockNumberOrTimestampOf, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - // High security accounts cannot use this extrinsic. - ensure!( - !HighSecurityAccounts::::contains_key(&who), - Error::::AccountAlreadyReversibleCannotScheduleOneTime - ); - - // Validate the provided delay. - Self::validate_delay(&delay)?; - - Self::do_schedule_transfer_inner(who.clone(), dest, who, amount, delay, Some(asset_id)) - } + // Call indices 5 and 6 were `schedule_asset_transfer` / + // `schedule_asset_transfer_with_delay` (removed with assets support). Kept vacant so + // `recover_funds` stays at index 7. /// Allows the guardian to recover all funds from a high-security account /// by transferring the entire balance to themselves. @@ -644,15 +600,15 @@ pub mod pallet { impl Hooks> for Pallet { fn integrity_test() { assert!( - !T::MinDelayPeriodBlocks::get().is_zero() && - !T::MinDelayPeriodMoment::get().is_zero(), + !T::MinDelayPeriodBlocks::get().is_zero() + && !T::MinDelayPeriodMoment::get().is_zero(), "Minimum delay periods must be greater than 0" ); // NOTE: default delay is always in blocks assert!( - BlockNumberOrTimestampOf::::BlockNumber(T::MinDelayPeriodBlocks::get()) <= - T::DefaultDelay::get(), + BlockNumberOrTimestampOf::::BlockNumber(T::MinDelayPeriodBlocks::get()) + <= T::DefaultDelay::get(), "Minimum delay periods must be less or equal to `T::DefaultDelay`" ); } @@ -666,15 +622,7 @@ pub mod pallet { ScheduledTransfer, } - impl Pallet - where - T: pallet_balances::Config::RuntimeHoldReason> - + pallet_assets_holder::Config::RuntimeHoldReason>, - { - #[inline] - fn asset_hold_reason() -> AssetsHoldReasonOf { - HoldReason::ScheduledTransfer.into() - } + impl Pallet { /// Check if an account has reversibility enabled and return its delay. pub fn is_high_security( who: &T::AccountId, @@ -706,41 +654,23 @@ pub mod pallet { fn do_execute_transfer(tx_id: &T::Hash) -> DispatchResultWithPostInfo { let pending = PendingTransfers::::get(tx_id).ok_or(Error::::PendingTxNotFound)?; + ensure!(pending.asset_id.is_none(), Error::::AssetsNotSupported); // Build the transfer call from stored data let to_lookup = T::Lookup::unlookup(pending.to.clone()); - let call: RuntimeCallOf = match pending.asset_id { - Some(ref id) => pallet_assets::Call::::transfer_keep_alive { - id: id.clone().into(), - target: to_lookup, - amount: pending.amount, - } - .into(), - None => pallet_balances::Call::::transfer_keep_alive { - dest: to_lookup, - value: pending.amount, - } - .into(), - }; + let call: RuntimeCallOf = pallet_balances::Call::::transfer_keep_alive { + dest: to_lookup, + value: pending.amount, + } + .into(); // Release held funds - if let Some(ref id) = pending.asset_id { - let reason = Self::asset_hold_reason(); - as AssetsHold>>::release( - id.clone(), - &reason, - &pending.from, - pending.amount, - Precision::Exact, - )?; - } else { - pallet_balances::Pallet::::release( - &HoldReason::ScheduledTransfer.into(), - &pending.from, - pending.amount, - Precision::Exact, - )?; - } + pallet_balances::Pallet::::release( + &HoldReason::ScheduledTransfer.into(), + &pending.from, + pending.amount, + Precision::Exact, + )?; // Remove transfer from storage PendingTransfers::::remove(tx_id); @@ -787,21 +717,12 @@ pub mod pallet { asset_id: Option>, ) -> DispatchResult { let recipient = T::Lookup::lookup(to.clone())?; + ensure!(asset_id.is_none(), Error::::AssetsNotSupported); // Build the transfer call for tx_id computation (not stored) - let transfer_call: RuntimeCallOf = match asset_id { - Some(ref id) => pallet_assets::Call::::transfer_keep_alive { - id: id.clone().into(), - target: to.clone(), - amount, - } - .into(), - None => pallet_balances::Call::::transfer_keep_alive { - dest: to.clone(), - value: amount, - } - .into(), - }; + let transfer_call: RuntimeCallOf = + pallet_balances::Call::::transfer_keep_alive { dest: to.clone(), value: amount } + .into(); let tx_id = T::Hashing::hash_of( &(from.clone(), transfer_call.clone(), NextTransactionId::::get()).encode(), @@ -815,10 +736,11 @@ pub mod pallet { ::BlockNumberProvider::current_block_number() .saturating_add(blocks), ), - BlockNumberOrTimestamp::Timestamp(millis) => + BlockNumberOrTimestamp::Timestamp(millis) => { DispatchTime::After(BlockNumberOrTimestamp::Timestamp( T::TimeProvider::now().saturating_add(millis), - )), + )) + }, }; log::debug!(target: "reversible-transfers", "Now time: {:?}", T::TimeProvider::now()); log::debug!(target: "reversible-transfers", "dispatch_time: {dispatch_time:?}"); @@ -861,22 +783,11 @@ pub mod pallet { Error::::SchedulingFailed })?; - // For assets, hold the funds using assets-holder; for native balances, hold the funds - if let Some(ref id) = asset_id { - let reason = Self::asset_hold_reason(); - as AssetsHold>>::hold( - id.clone(), - &reason, - &from, - amount, - )?; - } else { - pallet_balances::Pallet::::hold( - &HoldReason::ScheduledTransfer.into(), - &from, - amount, - )?; - } + pallet_balances::Pallet::::hold( + &HoldReason::ScheduledTransfer.into(), + &from, + amount, + )?; NextTransactionId::::mutate(|id| id.saturating_inc()); @@ -956,6 +867,8 @@ pub mod pallet { recipient: &T::AccountId, apply_fee: bool, ) -> DispatchResult { + ensure!(pending.asset_id.is_none(), Error::::AssetsNotSupported); + let (fee_amount, remaining_amount) = if apply_fee { let volume_fee = T::VolumeFee::get(); let fee = volume_fee * pending.amount; @@ -964,51 +877,25 @@ pub mod pallet { (Zero::zero(), pending.amount) }; - if let Some(ref asset_id) = pending.asset_id { - let reason = Self::asset_hold_reason(); - - // Burn fee amount - as AssetsHold>>::burn_held( - asset_id.clone(), - &reason, - &pending.from, - fee_amount, - Precision::Exact, - Fortitude::Polite, - )?; - - // Transfer remaining amount to recipient - as AssetsHold>>::transfer_on_hold( - asset_id.clone(), - &reason, - &pending.from, - recipient, - remaining_amount, - Precision::Exact, - Restriction::Free, - Fortitude::Polite, - )?; - } else { - // Burn fee amount - pallet_balances::Pallet::::burn_held( - &HoldReason::ScheduledTransfer.into(), - &pending.from, - fee_amount, - Precision::Exact, - Fortitude::Polite, - )?; - - // Transfer remaining amount to recipient - pallet_balances::Pallet::::transfer_on_hold( - &HoldReason::ScheduledTransfer.into(), - &pending.from, - recipient, - remaining_amount, - Precision::Exact, - Restriction::Free, - Fortitude::Polite, - )?; - } + // Burn fee amount + pallet_balances::Pallet::::burn_held( + &HoldReason::ScheduledTransfer.into(), + &pending.from, + fee_amount, + Precision::Exact, + Fortitude::Polite, + )?; + + // Transfer remaining amount to recipient + pallet_balances::Pallet::::transfer_on_hold( + &HoldReason::ScheduledTransfer.into(), + &pending.from, + recipient, + remaining_amount, + Precision::Exact, + Restriction::Free, + Fortitude::Polite, + )?; Ok(()) } diff --git a/pallets/reversible-transfers/src/tests/mock.rs b/pallets/reversible-transfers/src/tests/mock.rs index 8ef83c4d..86d8e05e 100644 --- a/pallets/reversible-transfers/src/tests/mock.rs +++ b/pallets/reversible-transfers/src/tests/mock.rs @@ -100,12 +100,6 @@ mod runtime { #[runtime::pallet_index(6)] pub type Utility = pallet_utility::Pallet; - - #[runtime::pallet_index(7)] - pub type Assets = pallet_assets::Pallet; - - #[runtime::pallet_index(8)] - pub type AssetsHolder = pallet_assets_holder::Pallet; } impl TryFrom for pallet_balances::Call { @@ -118,16 +112,6 @@ impl TryFrom for pallet_balances::Call { } } -impl TryFrom for pallet_assets::Call { - type Error = (); - fn try_from(call: RuntimeCall) -> Result { - match call { - RuntimeCall::Assets(c) => Ok(c), - _ => Err(()), - } - } -} - #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; @@ -250,6 +234,7 @@ impl qp_wormhole::TransferProofRecorder for MockProofRe } impl pallet_reversible_transfers::Config for Test { + type AssetId = u32; type SchedulerOrigin = OriginCaller; type RuntimeHoldReason = RuntimeHoldReason; type Scheduler = Scheduler; @@ -268,45 +253,6 @@ impl pallet_reversible_transfers::Config for Test { type ProofRecorder = MockProofRecorder; } -parameter_types! { - pub const AssetDeposit: Balance = 0; - pub const AssetAccountDeposit: Balance = 0; - pub const AssetsStringLimit: u32 = 50; - pub const MetadataDepositBase: Balance = 0; - pub const MetadataDepositPerByte: Balance = 0; -} - -impl pallet_assets::Config for Test { - type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type AssetId = u32; - type AssetIdParameter = codec::Compact; - type Currency = Balances; - type CreateOrigin = - frame_support::traits::AsEnsureOriginWithArg>; - type ForceOrigin = frame_system::EnsureRoot; - type AssetDeposit = AssetDeposit; - type MetadataDepositBase = MetadataDepositBase; - type MetadataDepositPerByte = MetadataDepositPerByte; - type ApprovalDeposit = sp_core::ConstU128<0>; - type StringLimit = AssetsStringLimit; - type Freezer = (); - type Extra = (); - type WeightInfo = (); - type CallbackHandle = pallet_assets::AutoIncAssetId; - type AssetAccountDeposit = AssetAccountDeposit; - type RemoveItemsLimit = frame_support::traits::ConstU32<1000>; - type Holder = pallet_assets_holder::Pallet; - type ReserveData = (); - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = (); -} - -impl pallet_assets_holder::Config for Test { - type RuntimeEvent = RuntimeEvent; - type RuntimeHoldReason = RuntimeHoldReason; -} - parameter_types! { pub const ConfigDepositBase: Balance = 1; pub const FriendDepositFactor: Balance = 1; diff --git a/pallets/reversible-transfers/src/tests/test_reversible_transfers.rs b/pallets/reversible-transfers/src/tests/test_reversible_transfers.rs index fecbe431..e6294c5f 100644 --- a/pallets/reversible-transfers/src/tests/test_reversible_transfers.rs +++ b/pallets/reversible-transfers/src/tests/test_reversible_transfers.rs @@ -2,10 +2,7 @@ use crate::tests::mock::*; // Import mock runtime and types use crate::*; // Import items from parent module (lib.rs) use frame_support::{ assert_err, assert_ok, - traits::{ - fungible::InspectHold, fungibles::Inspect as AssetsInspect, - tokens::fungibles::InspectHold as AssetsInspectHold, Time, - }, + traits::{fungible::InspectHold, Time}, }; use pallet_scheduler::Agenda; use qp_scheduler::BlockNumberOrTimestamp; @@ -46,33 +43,6 @@ fn run_to_block(n: u64) { } } -// Helper to create and mint asset -fn create_asset(id: u32, owner: AccountId, supply: Option) { - assert_ok!(pallet_assets::Pallet::::create( - RuntimeOrigin::signed(owner.clone()), - codec::Compact(id), - owner.clone(), - 1, - )); - let amount = supply.unwrap_or(1_000_000_000_000); - assert_ok!(pallet_assets::Pallet::::mint( - RuntimeOrigin::signed(owner.clone()), - codec::Compact(id), - owner, - amount, - )); -} - -fn asset_balance(id: u32, who: &AccountId) -> Balance { - pallet_assets::Pallet::::balance(id, who.clone()) -} - -// Test-only helper: amount held (by reversible pallet reason) for an asset account -fn asset_holds(id: u32, who: &AccountId) -> Balance { - let reason: RuntimeHoldReason = HoldReason::ScheduledTransfer.into(); - as AssetsInspectHold<_>>::balance_on_hold(id, &reason, who) -} - #[test] fn set_high_security_works() { new_test_ext().execute_with(|| { @@ -382,8 +352,8 @@ fn schedule_transfer_with_timestamp_works() { let current_time = MockTimestamp::::now(); let HighSecurityAccountData { delay: user_delay, .. } = ReversibleTransfers::is_high_security(&user).unwrap(); - let expected_raw_timestamp = (current_time / timestamp_bucket_size) * timestamp_bucket_size + - user_delay.as_timestamp().unwrap(); + let expected_raw_timestamp = (current_time / timestamp_bucket_size) * timestamp_bucket_size + + user_delay.as_timestamp().unwrap(); // With the scheduler fix, After(Timestamp) tasks go to next bucket after normalization // normalize() adds one bucket, then scheduler adds another for safety @@ -1265,69 +1235,7 @@ fn schedule_transfer_with_delay_works() { }); } -#[test] -fn schedule_asset_transfer_works() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - let sender: AccountId = alice(); // has high-security from genesis - let recipient: AccountId = dave(); - let asset_id: u32 = 42; - let amount: Balance = 1_000; - - create_asset(asset_id, sender.clone(), None); - let sender_asset_before = asset_balance(asset_id, &sender); - let recipient_asset_before = asset_balance(asset_id, &recipient); - - // Schedule asset transfer using configured delay - assert_ok!(ReversibleTransfers::schedule_asset_transfer( - RuntimeOrigin::signed(sender.clone()), - asset_id, - recipient.clone(), - amount, - )); - - // Advance to execution and ensure balances moved - let HighSecurityAccountData { delay, .. } = - ReversibleTransfers::is_high_security(&sender).unwrap(); - let execute_block = System::block_number() + delay.as_block_number().unwrap(); - run_to_block(execute_block); - - assert_eq!(asset_balance(asset_id, &sender), sender_asset_before - amount); - assert_eq!(asset_balance(asset_id, &recipient), recipient_asset_before + amount); - }); -} - -#[test] -fn schedule_asset_transfer_with_delay_works() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - let sender: AccountId = charlie(); // not configured; use one-time delay API - let recipient: AccountId = dave(); - let asset_id: u32 = 77; - let amount: Balance = 2_000; - let custom_delay_blocks: u64 = 8; - - create_asset(asset_id, sender.clone(), None); - let sender_asset_before = asset_balance(asset_id, &sender); - let recipient_asset_before = asset_balance(asset_id, &recipient); - - assert_ok!(ReversibleTransfers::schedule_asset_transfer_with_delay( - RuntimeOrigin::signed(sender.clone()), - asset_id, - recipient.clone(), - amount, - BlockNumberOrTimestamp::BlockNumber(custom_delay_blocks), - )); - - let execute_block = System::block_number() + custom_delay_blocks; - run_to_block(execute_block); - - assert_eq!(asset_balance(asset_id, &sender), sender_asset_before - amount); - assert_eq!(asset_balance(asset_id, &recipient), recipient_asset_before + amount); - assert_eq!(asset_holds(asset_id, &sender), 0); - }); -} - +#[cfg(any())] #[test] fn asset_hold_does_not_block_spending() { new_test_ext().execute_with(|| { @@ -1390,6 +1298,7 @@ fn asset_hold_does_not_block_spending() { }); } +#[cfg(any())] #[test] fn asset_hold_blocks_only_held_portion() { new_test_ext().execute_with(|| { @@ -1443,6 +1352,7 @@ fn asset_hold_blocks_only_held_portion() { }); } +#[cfg(any())] #[test] fn asset_hold_prevents_spend_over_free() { // Testing asset hold because it was quite confusing in code @@ -1479,6 +1389,7 @@ fn asset_hold_prevents_spend_over_free() { }); } +#[cfg(any())] #[test] fn recover_funds_is_atomic_when_release_fails() { new_test_ext().execute_with(|| { @@ -1566,6 +1477,7 @@ fn recover_funds_is_atomic_when_release_fails() { }); } +#[cfg(any())] #[test] fn recover_funds_weight_accounts_for_failed_releases() { new_test_ext().execute_with(|| { @@ -2110,6 +2022,7 @@ fn reversible_transfer_records_transfer_proof_on_execution() { }); } +#[cfg(any())] #[test] fn reversible_asset_transfer_records_transfer_proof_with_asset_id() { new_test_ext().execute_with(|| { diff --git a/pallets/reversible-transfers/src/weights.rs b/pallets/reversible-transfers/src/weights.rs index 7a7d0baa..b5e59949 100644 --- a/pallets/reversible-transfers/src/weights.rs +++ b/pallets/reversible-transfers/src/weights.rs @@ -52,7 +52,6 @@ use core::marker::PhantomData; pub trait WeightInfo { fn set_high_security() -> Weight; fn schedule_transfer() -> Weight; - fn schedule_asset_transfer() -> Weight; fn cancel() -> Weight; fn execute_transfer() -> Weight; fn recover_funds(n: u32, ) -> Weight; @@ -99,37 +98,6 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } - /// Storage: `ReversibleTransfers::HighSecurityAccounts` (r:1 w:0) - /// Proof: `ReversibleTransfers::HighSecurityAccounts` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`) - /// Storage: `ReversibleTransfers::NextTransactionId` (r:1 w:1) - /// Proof: `ReversibleTransfers::NextTransactionId` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) - /// Storage: `ReversibleTransfers::PendingTransfersBySender` (r:1 w:1) - /// Proof: `ReversibleTransfers::PendingTransfersBySender` (`max_values`: None, `max_size`: Some(561), added: 3036, mode: `MaxEncodedLen`) - /// Storage: `Scheduler::Lookup` (r:1 w:1) - /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) - /// Storage: `Timestamp::Now` (r:1 w:0) - /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) - /// Storage: `Scheduler::Agenda` (r:1 w:1) - /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(10018), added: 12493, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `AssetsHolder::BalancesOnHold` (r:1 w:1) - /// Proof: `AssetsHolder::BalancesOnHold` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) - /// Storage: `AssetsHolder::Holds` (r:1 w:1) - /// Proof: `AssetsHolder::Holds` (`max_values`: None, `max_size`: Some(105), added: 2580, mode: `MaxEncodedLen`) - /// Storage: `ReversibleTransfers::PendingTransfers` (r:0 w:1) - /// Proof: `ReversibleTransfers::PendingTransfers` (`max_values`: None, `max_size`: Some(165), added: 2640, mode: `MaxEncodedLen`) - fn schedule_asset_transfer() -> Weight { - // Proof Size summary in bytes: - // Measured: `629` - // Estimated: `13483` - // Minimum execution time: 62_000_000 picoseconds. - Weight::from_parts(64_000_000, 13483) - .saturating_add(T::DbWeight::get().reads(10_u64)) - .saturating_add(T::DbWeight::get().writes(9_u64)) - } /// Storage: `ReversibleTransfers::PendingTransfers` (r:1 w:1) /// Proof: `ReversibleTransfers::PendingTransfers` (`max_values`: None, `max_size`: Some(165), added: 2640, mode: `MaxEncodedLen`) /// Storage: `ReversibleTransfers::HighSecurityAccounts` (r:1 w:0) @@ -255,37 +223,6 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } - /// Storage: `ReversibleTransfers::HighSecurityAccounts` (r:1 w:0) - /// Proof: `ReversibleTransfers::HighSecurityAccounts` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`) - /// Storage: `ReversibleTransfers::NextTransactionId` (r:1 w:1) - /// Proof: `ReversibleTransfers::NextTransactionId` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) - /// Storage: `ReversibleTransfers::PendingTransfersBySender` (r:1 w:1) - /// Proof: `ReversibleTransfers::PendingTransfersBySender` (`max_values`: None, `max_size`: Some(561), added: 3036, mode: `MaxEncodedLen`) - /// Storage: `Scheduler::Lookup` (r:1 w:1) - /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) - /// Storage: `Timestamp::Now` (r:1 w:0) - /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) - /// Storage: `Scheduler::Agenda` (r:1 w:1) - /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(10018), added: 12493, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `AssetsHolder::BalancesOnHold` (r:1 w:1) - /// Proof: `AssetsHolder::BalancesOnHold` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) - /// Storage: `AssetsHolder::Holds` (r:1 w:1) - /// Proof: `AssetsHolder::Holds` (`max_values`: None, `max_size`: Some(105), added: 2580, mode: `MaxEncodedLen`) - /// Storage: `ReversibleTransfers::PendingTransfers` (r:0 w:1) - /// Proof: `ReversibleTransfers::PendingTransfers` (`max_values`: None, `max_size`: Some(165), added: 2640, mode: `MaxEncodedLen`) - fn schedule_asset_transfer() -> Weight { - // Proof Size summary in bytes: - // Measured: `629` - // Estimated: `13483` - // Minimum execution time: 62_000_000 picoseconds. - Weight::from_parts(64_000_000, 13483) - .saturating_add(RocksDbWeight::get().reads(10_u64)) - .saturating_add(RocksDbWeight::get().writes(9_u64)) - } /// Storage: `ReversibleTransfers::PendingTransfers` (r:1 w:1) /// Proof: `ReversibleTransfers::PendingTransfers` (`max_values`: None, `max_size`: Some(165), added: 2640, mode: `MaxEncodedLen`) /// Storage: `ReversibleTransfers::HighSecurityAccounts` (r:1 w:0) diff --git a/pallets/wormhole/Cargo.toml b/pallets/wormhole/Cargo.toml index 06e7fffd..4c165e5b 100644 --- a/pallets/wormhole/Cargo.toml +++ b/pallets/wormhole/Cargo.toml @@ -34,7 +34,6 @@ qp-wormhole-circuit-builder.workspace = true [dev-dependencies] hex = { workspace = true, features = ["alloc"] } -pallet-assets = { workspace = true, features = ["std"] } qp-dilithium-crypto = { workspace = true, features = ["std"] } qp-plonky2 = { workspace = true, default-features = false } qp-poseidon-core.workspace = true @@ -55,7 +54,6 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", - "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", ] std = [ diff --git a/pallets/wormhole/src/lib.rs b/pallets/wormhole/src/lib.rs index 65acdce7..828ee527 100644 --- a/pallets/wormhole/src/lib.rs +++ b/pallets/wormhole/src/lib.rs @@ -205,7 +205,6 @@ pub mod pallet { pallet_prelude::*, traits::{ fungible::{Inspect as FungibleInspect, Mutate, Unbalanced}, - fungibles::{self}, BuildGenesisConfig, Contains, Currency, }, }; @@ -310,15 +309,6 @@ pub mod pallet { + Unbalanced<::AccountId> + Currency<::AccountId, Balance = Self::NativeBalance>; - /// Assets type used for managing fungible assets. - /// The AssetId must match Self::AssetId for consistency. - type Assets: fungibles::Inspect< - ::AccountId, - AssetId = Self::AssetId, - Balance = Self::AssetBalance, - > + fungibles::Mutate<::AccountId> - + fungibles::Create<::AccountId>; - /// Asset ID type for transfer proofs. type AssetId: Parameter + Member + Default + From + Clone + MaxEncodedLen; diff --git a/pallets/wormhole/src/mock.rs b/pallets/wormhole/src/mock.rs index d1e8c5cd..c8940f4c 100644 --- a/pallets/wormhole/src/mock.rs +++ b/pallets/wormhole/src/mock.rs @@ -1,7 +1,7 @@ use crate::{self as pallet_wormhole}; use frame_support::{ construct_runtime, parameter_types, - traits::{ConstU128, ConstU32, Everything}, + traits::{ConstU32, Everything}, }; use frame_system::mocking::MockUncheckedExtrinsic; use sp_core::H256; @@ -17,7 +17,6 @@ construct_runtime!( pub enum Test { System: frame_system, Balances: pallet_balances, - Assets: pallet_assets, ZkTree: pallet_zk_tree, Wormhole: pallet_wormhole, } @@ -92,32 +91,6 @@ impl pallet_balances::Config for Test { type RuntimeEvent = RuntimeEvent; } -impl pallet_assets::Config for Test { - type RuntimeEvent = RuntimeEvent; - type Balance = Balance; - type AssetId = u32; - type AssetIdParameter = u32; - type Currency = Balances; - type CreateOrigin = - frame_support::traits::AsEnsureOriginWithArg>; - type ForceOrigin = frame_system::EnsureRoot; - type AssetDeposit = ConstU128<1>; - type AssetAccountDeposit = ConstU128<1>; - type MetadataDepositBase = ConstU128<1>; - type MetadataDepositPerByte = ConstU128<1>; - type ApprovalDeposit = ConstU128<1>; - type StringLimit = ConstU32<50>; - type Freezer = (); - type Extra = (); - type WeightInfo = (); - type RemoveItemsLimit = ConstU32<1000>; - type CallbackHandle = (); - type Holder = (); - type ReserveData = (); - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = (); -} - parameter_types! { /// The "from" account used when recording transfer proofs for minted tokens. /// Uses the shared MINTING_ACCOUNT constant from qp_wormhole. @@ -154,7 +127,6 @@ impl pallet_zk_tree::Config for Test { impl pallet_wormhole::Config for Test { type NativeBalance = Balance; type Currency = Balances; - type Assets = Assets; type AssetId = u32; type AssetBalance = Balance; type TransferCount = u64; diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 3c728136..0ea2b4d3 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -27,8 +27,6 @@ frame-system-rpc-runtime-api.workspace = true frame-try-runtime = { optional = true, workspace = true } lazy_static.workspace = true log.workspace = true -pallet-assets.workspace = true -pallet-assets-holder = { workspace = true, default-features = false } pallet-balances.workspace = true pallet-mining-rewards.workspace = true pallet-multisig.workspace = true @@ -92,8 +90,6 @@ std = [ "frame-system/std", "frame-try-runtime?/std", "log/std", - "pallet-assets-holder/std", - "pallet-assets/std", "pallet-balances/std", "pallet-mining-rewards/std", "pallet-multisig/std", @@ -140,7 +136,6 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", - "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-mining-rewards/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", @@ -163,7 +158,6 @@ try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "frame-try-runtime/try-runtime", - "pallet-assets/try-runtime", "pallet-balances/try-runtime", "pallet-mining-rewards/try-runtime", "pallet-qpow/try-runtime", diff --git a/runtime/src/configs/mod.rs b/runtime/src/configs/mod.rs index 61fca4e0..553cf6fb 100644 --- a/runtime/src/configs/mod.rs +++ b/runtime/src/configs/mod.rs @@ -34,8 +34,7 @@ use crate::{ use frame_support::{ derive_impl, parameter_types, traits::{ - AsEnsureOriginWithArg, ConstU128, ConstU16, ConstU32, ConstU8, Contains, NeverEnsureOrigin, - VariantCountOf, + ConstU128, ConstU16, ConstU32, ConstU8, Contains, NeverEnsureOrigin, VariantCountOf, }, weights::{ constants::{RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND}, @@ -46,7 +45,7 @@ use frame_support::{ }; use frame_system::{ limits::{BlockLength, BlockWeights}, - EnsureRoot, EnsureRootWithSuccess, EnsureSigned, + EnsureRoot, EnsureRootWithSuccess, }; use pallet_ranked_collective::Linear; use pallet_transaction_payment::{ConstFeeMultiplier, FungibleAdapter, Multiplier}; @@ -61,7 +60,7 @@ use sp_version::RuntimeVersion; // Local module imports use super::{ - AccountId, Assets, Balance, Balances, Block, BlockNumber, Hash, Nonce, OriginCaller, + AccountId, AssetId, Balance, Balances, Block, BlockNumber, Hash, Nonce, OriginCaller, PalletInfo, Preimage, Runtime, RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason, RuntimeOrigin, RuntimeTask, Scheduler, System, Timestamp, Wormhole, ZkTree, DAYS, EXISTENTIAL_DEPOSIT, MICRO_UNIT, TARGET_BLOCK_TIME_MS, UNIT, VERSION, @@ -166,10 +165,6 @@ parameter_types! { /// Used as the `from` address in TransferProofs when native tokens are minted. /// This is a well-known sentinel address, not a real account. pub const MintingAccount: AccountId = AccountId::new([1u8; 32]); - /// Canonical minting account for pallet_assets mint operations. - /// Used as the `from` address in TransferProofs when assets are minted. - /// This is a well-known sentinel address, not a real account. - pub const AssetMintingAccount: AccountId = AccountId::new([2u8; 32]); } type Moment = u64; @@ -502,6 +497,7 @@ parameter_types! { } impl pallet_reversible_transfers::Config for Runtime { + type AssetId = AssetId; type SchedulerOrigin = OriginCaller; type Scheduler = Scheduler; type BlockNumberProvider = System; @@ -528,48 +524,6 @@ impl pallet_treasury::Config for Runtime { type WeightInfo = pallet_treasury::weights::SubstrateWeight; } -parameter_types! { - pub const AssetDeposit: Balance = MILLI_UNIT; - pub const AssetAccountDeposit: Balance = MILLI_UNIT; - pub const AssetsStringLimit: u32 = 50; - pub const MetadataDepositBase: Balance = MILLI_UNIT; - pub const MetadataDepositPerByte: Balance = MILLI_UNIT; -} - -/// We allow root to execute privileged asset operations. -pub type AssetsForceOrigin = EnsureRoot; -type AssetId = u32; - -impl pallet_assets::Config for Runtime { - type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type AssetId = AssetId; - type AssetIdParameter = codec::Compact; - type Currency = Balances; - type CreateOrigin = AsEnsureOriginWithArg>; - type ForceOrigin = AssetsForceOrigin; - type AssetDeposit = AssetDeposit; - type MetadataDepositBase = MetadataDepositBase; - type MetadataDepositPerByte = MetadataDepositPerByte; - type ApprovalDeposit = ExistentialDeposit; - type StringLimit = AssetsStringLimit; - type Freezer = (); - type Extra = (); - type WeightInfo = pallet_assets::weights::SubstrateWeight; - type CallbackHandle = pallet_assets::AutoIncAssetId; - type AssetAccountDeposit = AssetAccountDeposit; - type RemoveItemsLimit = frame_support::traits::ConstU32<1000>; - type Holder = pallet_assets_holder::Pallet; - type ReserveData = (); - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = (); -} - -impl pallet_assets_holder::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type RuntimeHoldReason = RuntimeHoldReason; -} - // Multisig configuration parameter_types! { pub const MultisigPalletId: PalletId = PalletId(*b"py/mltsg"); @@ -598,8 +552,8 @@ parameter_types! { /// /// Whitelist includes only delayed, reversible operations: /// - `schedule_transfer`: Schedule delayed native token transfer -/// - `schedule_asset_transfer`: Schedule delayed asset transfer /// - `cancel`: Cancel pending delayed transfer +/// - `recover_funds`: Guardian-initiated recovery pub struct HighSecurityConfig; impl qp_high_security::HighSecurityInspector for HighSecurityConfig { @@ -613,8 +567,6 @@ impl qp_high_security::HighSecurityInspector for HighSec call, RuntimeCall::ReversibleTransfers( pallet_reversible_transfers::Call::schedule_transfer { .. } - ) | RuntimeCall::ReversibleTransfers( - pallet_reversible_transfers::Call::schedule_asset_transfer { .. } ) | RuntimeCall::ReversibleTransfers(pallet_reversible_transfers::Call::cancel { .. }) | RuntimeCall::ReversibleTransfers( pallet_reversible_transfers::Call::recover_funds { .. } @@ -656,16 +608,6 @@ impl TryFrom for pallet_balances::Call { } } -impl TryFrom for pallet_assets::Call { - type Error = (); - fn try_from(call: RuntimeCall) -> Result { - match call { - RuntimeCall::Assets(c) => Ok(c), - _ => Err(()), - } - } -} - parameter_types! { /// Volume fee rate in basis points (4 bps = 0.04%). /// The circuit already enforces a one-quantum (0.01 QUAN) minimum fee via ceil @@ -688,12 +630,11 @@ parameter_types! { /// `PalletId`-derived address (`py/trsry`); the actually-*configured* treasury account (which /// may differ in this fork) is excluded separately in `NonWormholeAccounts::contains` via the /// runtime `treasury_account()` storage getter. - pub KeylessNonWormholeAccounts: [AccountId; 5] = [ + pub KeylessNonWormholeAccounts: [AccountId; 4] = [ TreasuryPalletId::get().into_account_truncating(), MultisigPalletId::get().into_account_truncating(), ReversibleTransfersPalletIdValue::get().into_account_truncating(), MintingAccount::get(), - AssetMintingAccount::get(), ]; } @@ -733,7 +674,6 @@ impl Contains for NonWormholeAccounts { impl pallet_wormhole::Config for Runtime { type NativeBalance = Balance; type Currency = Balances; - type Assets = Assets; type AssetId = AssetId; type AssetBalance = Balance; type TransferCount = u64; diff --git a/runtime/src/genesis_config_presets.rs b/runtime/src/genesis_config_presets.rs index 8751cf9d..7a91c122 100644 --- a/runtime/src/genesis_config_presets.rs +++ b/runtime/src/genesis_config_presets.rs @@ -18,9 +18,7 @@ // this module is used by the client, so it's ok to panic/unwrap here #![allow(clippy::expect_used)] -use crate::{ - AccountId, AssetsConfig, BalancesConfig, RuntimeGenesisConfig, EXISTENTIAL_DEPOSIT, UNIT, -}; +use crate::{AccountId, BalancesConfig, RuntimeGenesisConfig, UNIT}; use alloc::{ string::{String, ToString}, vec, @@ -31,10 +29,7 @@ use qp_dilithium_crypto::pair::{crystal_alice, crystal_charlie, dilithium_bob}; use serde_json::Value; use sp_core::crypto::Ss58Codec; use sp_genesis_builder::{self, PresetId}; -use sp_runtime::{ - traits::{IdentifyAccount, Zero}, - Permill, -}; +use sp_runtime::{traits::IdentifyAccount, Permill}; /// Well-known test secret for testing ZK proof spending. /// This is a simple pattern (`[42u8; 32]`) for easy testing. @@ -182,7 +177,6 @@ fn genesis_template( // No pre-mine: the treasury starts at zero balance and is funded only by its share of // mining rewards. It is intentionally NOT added to `balances`. - let treasury_account = treasury.account.clone(); let config = RuntimeGenesisConfig { balances: BalancesConfig { balances: balances.clone(), dev_accounts: None }, @@ -190,11 +184,6 @@ fn genesis_template( treasury_account: Some(treasury.account), treasury_portion: Some(treasury.portion), }, - assets: AssetsConfig { - // Reserve asset id 0 for native token representation used with wormhole. - assets: vec![(Zero::zero(), treasury_account, false, EXISTENTIAL_DEPOSIT)], - ..Default::default() - }, wormhole: pallet_wormhole::GenesisConfig:: { // Record transfer proofs for ALL endowed addresses, enabling ZK spending. // Events are emitted in on_initialize at block 1 for indexer compatibility. diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a3db788e..d36f8fc4 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -250,11 +250,9 @@ mod runtime { #[runtime::pallet_index(16)] pub type Recovery = pallet_recovery; - #[runtime::pallet_index(17)] - pub type Assets = pallet_assets; + // Index 17 was `pallet_assets` (removed). Kept vacant so downstream pallet indices stay stable. - #[runtime::pallet_index(18)] - pub type AssetsHolder = pallet_assets_holder; + // Index 18 was `pallet_assets_holder` (removed with assets). Kept vacant. #[runtime::pallet_index(19)] pub type Multisig = pallet_multisig; diff --git a/runtime/src/transaction_extensions.rs b/runtime/src/transaction_extensions.rs index 3412ab89..36b01684 100644 --- a/runtime/src/transaction_extensions.rs +++ b/runtime/src/transaction_extensions.rs @@ -125,26 +125,12 @@ impl WormholeProofRecorderExtension .reads_writes(5u64.saturating_add(tree_reads), 2u64.saturating_add(tree_writes)) } - /// Whether a `pallet_assets` credit with this id is actually recorded by the wormhole. - /// Asset id 0 is reserved for the wormhole's *internal* native tag and is dropped by - /// `record_transfer_proof` — it must not be charged as a proof-recording transfer. - fn asset_transfer_counts(id: &codec::Compact) -> u64 { - let asset_id: AssetId = (*id).into(); - if asset_id == 0 { - 0 - } else { - 1 - } - } - fn count_transfers(call: &RuntimeCall) -> u64 { // NOTE: this must stay in sync with the events matched by `record_proofs_from_events_since` // — we only weight calls whose emitted events we actually record. In particular // `Balances::force_set_balance` is deliberately NOT counted here: it emits `BalanceSet` // (an absolute set, not a transfer/mint), which we cannot turn into a transfer proof and // therefore never record. See the soundness-counter caveat on `reduce_potential_balance`. - // Likewise, `pallet_assets` credits of asset id 0 are dropped by `record_transfer_proof` - // (they must not be conflated with native) and are therefore not counted here. // // Wrappers whose inner call is stored on-chain rather than in the submitted call // (`Multisig::execute`, `ReversibleTransfers::recover_funds`, ...) cannot be counted @@ -157,12 +143,6 @@ impl WormholeProofRecorderExtension RuntimeCall::Balances(pallet_balances::Call::transfer_all { .. }) | RuntimeCall::Balances(pallet_balances::Call::force_transfer { .. }) => 1, - RuntimeCall::Assets(pallet_assets::Call::transfer { id, .. }) | - RuntimeCall::Assets(pallet_assets::Call::transfer_keep_alive { id, .. }) | - RuntimeCall::Assets(pallet_assets::Call::transfer_approved { id, .. }) | - RuntimeCall::Assets(pallet_assets::Call::force_transfer { id, .. }) | - RuntimeCall::Assets(pallet_assets::Call::mint { id, .. }) => Self::asset_transfer_counts(id), - RuntimeCall::Utility(pallet_utility::Call::batch { calls }) | RuntimeCall::Utility(pallet_utility::Call::batch_all { calls }) | RuntimeCall::Utility(pallet_utility::Call::force_batch { calls }) => @@ -213,22 +193,6 @@ impl WormholeProofRecorderExtension let minting_account = crate::configs::MintingAccount::get(); Some((None, minting_account, who, amount)) }, - // Asset transfers - RuntimeEvent::Assets(pallet_assets::Event::Transferred { - asset_id, - from, - to, - amount, - }) => Some((Some(asset_id), from, to, amount)), - // Asset mints - RuntimeEvent::Assets(pallet_assets::Event::Issued { - asset_id, - owner, - amount, - }) => { - let minting_account = crate::configs::AssetMintingAccount::get(); - Some((Some(asset_id), minting_account, owner, amount)) - }, _ => None, // Ignore all other events } }) @@ -236,9 +200,8 @@ impl WormholeProofRecorderExtension // Now record the proofs - this is safe because we're no longer iterating over Events. // Count only credits that were actually recorded: `record_transfer_proof` returns - // `false` for deliberately dropped credits (notably `pallet_assets` asset-0), and - // counting those as recorded would over-reserve fees and falsely register extra - // block weight on opaque paths. + // `false` for deliberately dropped credits, and counting those as recorded would + // over-reserve fees and falsely register extra block weight on opaque paths. let mut recorded = 0u64; for (asset_id, from, to, amount) in transfers_to_record { if >::record_transfer_proof( @@ -859,88 +822,6 @@ mod tests { }); } - /// `pallet_assets` asset id 0 is dropped by `record_transfer_proof` (it must not be - /// conflated with native). Static weight and the post_dispatch recorded-count must - /// both treat it as a non-recording credit — otherwise fees are over-reserved and - /// opaque paths falsely register extra block weight for work that never happened. - #[test] - fn asset_zero_credit_is_not_counted_as_recorded_transfer() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - - // Static matcher: asset-0 mint/transfer must charge zero proof-recording weight. - let asset_zero_mint = RuntimeCall::Assets(pallet_assets::Call::mint { - id: 0u32.into(), - beneficiary: MultiAddress::Id(bob()), - amount: 100, - }); - let asset_one_mint = RuntimeCall::Assets(pallet_assets::Call::mint { - id: 1u32.into(), - beneficiary: MultiAddress::Id(bob()), - amount: 100, - }); - assert_eq!( - WormholeProofRecorderExtension::::count_transfers(&asset_zero_mint), - 0, - "asset-0 mint must not be statically charged as a recorded proof" - ); - assert_eq!( - WormholeProofRecorderExtension::::count_transfers(&asset_one_mint), - 1, - "non-zero asset mint must still be statically charged" - ); - - // Create asset 0 and mint — emits `Assets::Issued { asset_id: 0, ... }`. - assert_ok!(Assets::force_create( - RuntimeOrigin::root(), - 0u32.into(), - MultiAddress::Id(alice()), - true, - 1, - )); - let bob_count_before = Wormhole::transfer_count(&bob()); - let weight_before = frame_system::Pallet::::block_weight().total(); - - // Opaque presented call (charged_transfers = 0) whose dispatch mints asset 0. - // Pre-fix, `record_proofs_from_events_since` counted the event and registered - // a full per-transfer weight shortfall even though the credit was dropped. - let opaque_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![1] }); - run_lifecycle(&alice(), opaque_call, || { - assert_ok!(Assets::mint( - RuntimeOrigin::signed(alice()), - 0u32.into(), - MultiAddress::Id(bob()), - 100, - )); - }); - - assert_eq!( - Wormhole::transfer_count(&bob()), - bob_count_before, - "asset-0 mint must not insert a wormhole transfer proof" - ); - assert_eq!( - frame_system::Pallet::::block_weight().total(), - weight_before, - "dropped asset-0 credit must not register extra block weight" - ); - - // Direct scan: the Issued event is present but must report zero recordings. - System::reset_events(); - assert_ok!(Assets::mint( - RuntimeOrigin::signed(alice()), - 0u32.into(), - MultiAddress::Id(bob()), - 50, - )); - assert_eq!( - WormholeProofRecorderExtension::::record_proofs_from_events_since(0), - 0, - "record_proofs_from_events_since must not count dropped asset-0 credits" - ); - }); - } - #[test] fn wormhole_proof_recorder_extension_prepare_succeeds() { new_test_ext().execute_with(|| { From 77098e84e2b47896c3df89ad35c73562fa9f84ed Mon Sep 17 00:00:00 2001 From: illuzen Date: Fri, 31 Jul 2026 22:48:39 +0800 Subject: [PATCH 2/5] fmt --- pallets/reversible-transfers/src/lib.rs | 13 ++++++------- .../src/tests/test_reversible_transfers.rs | 4 ++-- runtime/src/configs/mod.rs | 4 +--- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/pallets/reversible-transfers/src/lib.rs b/pallets/reversible-transfers/src/lib.rs index 2c6e6502..2a68a756 100644 --- a/pallets/reversible-transfers/src/lib.rs +++ b/pallets/reversible-transfers/src/lib.rs @@ -600,15 +600,15 @@ pub mod pallet { impl Hooks> for Pallet { fn integrity_test() { assert!( - !T::MinDelayPeriodBlocks::get().is_zero() - && !T::MinDelayPeriodMoment::get().is_zero(), + !T::MinDelayPeriodBlocks::get().is_zero() && + !T::MinDelayPeriodMoment::get().is_zero(), "Minimum delay periods must be greater than 0" ); // NOTE: default delay is always in blocks assert!( - BlockNumberOrTimestampOf::::BlockNumber(T::MinDelayPeriodBlocks::get()) - <= T::DefaultDelay::get(), + BlockNumberOrTimestampOf::::BlockNumber(T::MinDelayPeriodBlocks::get()) <= + T::DefaultDelay::get(), "Minimum delay periods must be less or equal to `T::DefaultDelay`" ); } @@ -736,11 +736,10 @@ pub mod pallet { ::BlockNumberProvider::current_block_number() .saturating_add(blocks), ), - BlockNumberOrTimestamp::Timestamp(millis) => { + BlockNumberOrTimestamp::Timestamp(millis) => DispatchTime::After(BlockNumberOrTimestamp::Timestamp( T::TimeProvider::now().saturating_add(millis), - )) - }, + )), }; log::debug!(target: "reversible-transfers", "Now time: {:?}", T::TimeProvider::now()); log::debug!(target: "reversible-transfers", "dispatch_time: {dispatch_time:?}"); diff --git a/pallets/reversible-transfers/src/tests/test_reversible_transfers.rs b/pallets/reversible-transfers/src/tests/test_reversible_transfers.rs index e6294c5f..10526b37 100644 --- a/pallets/reversible-transfers/src/tests/test_reversible_transfers.rs +++ b/pallets/reversible-transfers/src/tests/test_reversible_transfers.rs @@ -352,8 +352,8 @@ fn schedule_transfer_with_timestamp_works() { let current_time = MockTimestamp::::now(); let HighSecurityAccountData { delay: user_delay, .. } = ReversibleTransfers::is_high_security(&user).unwrap(); - let expected_raw_timestamp = (current_time / timestamp_bucket_size) * timestamp_bucket_size - + user_delay.as_timestamp().unwrap(); + let expected_raw_timestamp = (current_time / timestamp_bucket_size) * timestamp_bucket_size + + user_delay.as_timestamp().unwrap(); // With the scheduler fix, After(Timestamp) tasks go to next bucket after normalization // normalize() adds one bucket, then scheduler adds another for safety diff --git a/runtime/src/configs/mod.rs b/runtime/src/configs/mod.rs index 553cf6fb..50353907 100644 --- a/runtime/src/configs/mod.rs +++ b/runtime/src/configs/mod.rs @@ -33,9 +33,7 @@ use crate::{ }; use frame_support::{ derive_impl, parameter_types, - traits::{ - ConstU128, ConstU16, ConstU32, ConstU8, Contains, NeverEnsureOrigin, VariantCountOf, - }, + traits::{ConstU128, ConstU16, ConstU32, ConstU8, Contains, NeverEnsureOrigin, VariantCountOf}, weights::{ constants::{RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND}, IdentityFee, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, From 8fd25edf4e1579eef599b6e810ba0422add01d36 Mon Sep 17 00:00:00 2001 From: illuzen Date: Fri, 31 Jul 2026 22:52:13 +0800 Subject: [PATCH 3/5] Delete unused assets crates from the workspace. Remove inlined pallet-assets and pallet-assets-holder now that they are no longer composed into the runtime. Co-authored-by: Cursor --- Cargo.lock | 34 - Cargo.toml | 4 - pallets/assets-holder/Cargo.toml | 56 - pallets/assets-holder/src/impl_fungibles.rs | 290 -- pallets/assets-holder/src/lib.rs | 178 -- pallets/assets-holder/src/mock.rs | 128 - pallets/assets-holder/src/tests.rs | 574 ---- pallets/assets/Cargo.toml | 55 - pallets/assets/README.md | 124 - pallets/assets/src/benchmarking.rs | 645 ----- pallets/assets/src/extra_mutator.rs | 102 - pallets/assets/src/functions.rs | 1167 -------- pallets/assets/src/impl_fungibles.rs | 360 --- pallets/assets/src/impl_stored_map.rs | 54 - pallets/assets/src/lib.rs | 1963 -------------- pallets/assets/src/migration.rs | 162 -- pallets/assets/src/mock.rs | 245 -- pallets/assets/src/tests.rs | 2371 ----------------- pallets/assets/src/tests/sets.rs | 358 --- pallets/assets/src/types.rs | 361 --- pallets/assets/src/weights.rs | 1131 -------- .../src/tests/test_reversible_transfers.rs | 368 --- runtime/src/genesis_config_presets.rs | 9 - 23 files changed, 10739 deletions(-) delete mode 100644 pallets/assets-holder/Cargo.toml delete mode 100644 pallets/assets-holder/src/impl_fungibles.rs delete mode 100644 pallets/assets-holder/src/lib.rs delete mode 100644 pallets/assets-holder/src/mock.rs delete mode 100644 pallets/assets-holder/src/tests.rs delete mode 100644 pallets/assets/Cargo.toml delete mode 100644 pallets/assets/README.md delete mode 100644 pallets/assets/src/benchmarking.rs delete mode 100644 pallets/assets/src/extra_mutator.rs delete mode 100644 pallets/assets/src/functions.rs delete mode 100644 pallets/assets/src/impl_fungibles.rs delete mode 100644 pallets/assets/src/impl_stored_map.rs delete mode 100644 pallets/assets/src/lib.rs delete mode 100644 pallets/assets/src/migration.rs delete mode 100644 pallets/assets/src/mock.rs delete mode 100644 pallets/assets/src/tests.rs delete mode 100644 pallets/assets/src/tests/sets.rs delete mode 100644 pallets/assets/src/types.rs delete mode 100644 pallets/assets/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index 6e71df8b..14dd8ba3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5861,40 +5861,6 @@ dependencies = [ "serde", ] -[[package]] -name = "pallet-assets" -version = "48.1.0" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "impl-trait-for-tuples", - "log", - "pallet-balances", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", -] - -[[package]] -name = "pallet-assets-holder" -version = "0.8.0" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "log", - "pallet-assets", - "pallet-balances", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", -] - [[package]] name = "pallet-balances" version = "46.0.0" diff --git a/Cargo.toml b/Cargo.toml index 69154a1a..9a383ec3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,8 +30,6 @@ members = [ "frame/try-runtime", "miner-api", "node", - "pallets/assets", - "pallets/assets-holder", "pallets/balances", "pallets/frame-system", "pallets/mining-rewards", @@ -255,8 +253,6 @@ frame-system-benchmarking = { path = "./frame/system-benchmarking", default-feat frame-system-rpc-runtime-api = { path = "./frame/system-rpc-runtime-api", default-features = false } frame-try-runtime = { path = "./frame/try-runtime", default-features = false } hickory-resolver = { version = "0.26.1" } -pallet-assets = { path = "./pallets/assets", default-features = false } -pallet-assets-holder = { path = "./pallets/assets-holder", default-features = false } pallet-preimage = { path = "./pallets/preimage", default-features = false } pallet-ranked-collective = { path = "./pallets/ranked-collective", default-features = false } pallet-recovery = { path = "./pallets/recovery", default-features = false } diff --git a/pallets/assets-holder/Cargo.toml b/pallets/assets-holder/Cargo.toml deleted file mode 100644 index 6409afa3..00000000 --- a/pallets/assets-holder/Cargo.toml +++ /dev/null @@ -1,56 +0,0 @@ -[package] -authors.workspace = true -description = "Provides holding features to `pallet-assets`" -edition.workspace = true -homepage.workspace = true -license = "Apache-2.0" -name = "pallet-assets-holder" -repository.workspace = true -version = "0.8.0" - -# Vendored upstream FRAME pallet: the pallet macros expand to expect/unwrap, so -# we deliberately don't apply the workspace restriction lints here. - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { workspace = true } -frame-benchmarking = { optional = true, workspace = true } -frame-support.workspace = true -frame-system.workspace = true -log = { workspace = true } -pallet-assets.workspace = true -scale-info = { features = ["derive"], workspace = true } -sp-runtime.workspace = true - -[dev-dependencies] -pallet-balances = { workspace = true, default-features = true } -sp-core = { workspace = true, default-features = true } -sp-io = { workspace = true, default-features = true } - -[features] -default = ["std"] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-assets/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "log/std", - "pallet-assets/std", - "scale-info/std", - "sp-runtime/std", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-assets/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/pallets/assets-holder/src/impl_fungibles.rs b/pallets/assets-holder/src/impl_fungibles.rs deleted file mode 100644 index b286cbb2..00000000 --- a/pallets/assets-holder/src/impl_fungibles.rs +++ /dev/null @@ -1,290 +0,0 @@ -// 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. - -use super::*; - -use frame_support::traits::{ - fungibles::{Dust, Inspect, InspectHold, MutateHold, Unbalanced, UnbalancedHold}, - tokens::{ - DepositConsequence, Fortitude, Precision, Preservation, Provenance, WithdrawConsequence, - }, -}; -use pallet_assets::BalanceOnHold; -use sp_runtime::{ - traits::{CheckedAdd, CheckedSub, Zero}, - ArithmeticError, -}; -use storage::StorageDoubleMap; - -// Implements [`BalanceOnHold`] from [`pallet-assets`], so it can understand whether there's some -// balance on hold for an asset account, and is able to signal to this pallet when to clear the -// state of an account. -impl, I: 'static> BalanceOnHold - for Pallet -{ - fn balance_on_hold(asset: T::AssetId, who: &T::AccountId) -> Option { - BalancesOnHold::::get(asset, who) - } - - fn died(asset: T::AssetId, who: &T::AccountId) { - defensive_assert!( - Holds::::get(asset.clone(), who).is_empty(), - "The list of Holds should be empty before allowing an account to die" - ); - defensive_assert!( - BalancesOnHold::::get(asset.clone(), who).is_none(), - "The should not be a balance on hold before allowing to die" - ); - - Holds::::remove(asset.clone(), who); - BalancesOnHold::::remove(asset, who); - } - - fn contains_holds(asset: T::AssetId) -> bool { - Holds::::contains_prefix(asset) - } -} - -// Implement [`fungibles::Inspect`](frame_support::traits::fungibles::Inspect) as it is bound by -// [`fungibles::InspectHold`](frame_support::traits::fungibles::InspectHold) and -// [`fungibles::MutateHold`](frame_support::traits::fungibles::MutateHold). To do so, we'll -// re-export all of `pallet-assets` implementation of the same trait. -impl, I: 'static> Inspect for Pallet { - type AssetId = T::AssetId; - type Balance = T::Balance; - - fn total_issuance(asset: Self::AssetId) -> Self::Balance { - pallet_assets::Pallet::::total_issuance(asset) - } - - fn minimum_balance(asset: Self::AssetId) -> Self::Balance { - pallet_assets::Pallet::::minimum_balance(asset) - } - - fn total_balance(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance { - pallet_assets::Pallet::::total_balance(asset, who) - } - - fn balance(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance { - pallet_assets::Pallet::::balance(asset, who) - } - - fn reducible_balance( - asset: Self::AssetId, - who: &T::AccountId, - preservation: Preservation, - force: Fortitude, - ) -> Self::Balance { - pallet_assets::Pallet::::reducible_balance(asset, who, preservation, force) - } - - fn can_deposit( - asset: Self::AssetId, - who: &T::AccountId, - amount: Self::Balance, - provenance: Provenance, - ) -> DepositConsequence { - pallet_assets::Pallet::::can_deposit(asset, who, amount, provenance) - } - - fn can_withdraw( - asset: Self::AssetId, - who: &T::AccountId, - amount: Self::Balance, - ) -> WithdrawConsequence { - pallet_assets::Pallet::::can_withdraw(asset, who, amount) - } - - fn asset_exists(asset: Self::AssetId) -> bool { - pallet_assets::Pallet::::asset_exists(asset) - } -} - -impl, I: 'static> InspectHold for Pallet { - type Reason = T::RuntimeHoldReason; - - fn total_balance_on_hold(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance { - BalancesOnHold::::get(asset, who).unwrap_or_else(Zero::zero) - } - - fn balance_on_hold( - asset: Self::AssetId, - reason: &Self::Reason, - who: &T::AccountId, - ) -> Self::Balance { - Holds::::get(asset, who) - .iter() - .find(|x| &x.id == reason) - .map(|x| x.amount) - .unwrap_or_else(Zero::zero) - } -} - -impl, I: 'static> Unbalanced for Pallet { - fn handle_dust(dust: Dust) { - let Dust(id, balance) = dust; - pallet_assets::Pallet::::handle_dust(Dust(id, balance)); - } - - fn write_balance( - asset: Self::AssetId, - who: &T::AccountId, - amount: Self::Balance, - ) -> Result, DispatchError> { - pallet_assets::Pallet::::write_balance(asset, who, amount) - } - - fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) { - pallet_assets::Pallet::::set_total_issuance(asset, amount) - } - - fn decrease_balance( - asset: Self::AssetId, - who: &T::AccountId, - amount: Self::Balance, - precision: Precision, - preservation: Preservation, - force: Fortitude, - ) -> Result { - pallet_assets::Pallet::::decrease_balance( - asset, - who, - amount, - precision, - preservation, - force, - ) - } - - fn increase_balance( - asset: Self::AssetId, - who: &T::AccountId, - amount: Self::Balance, - precision: Precision, - ) -> Result { - pallet_assets::Pallet::::increase_balance(asset, who, amount, precision) - } -} - -impl, I: 'static> UnbalancedHold for Pallet { - fn set_balance_on_hold( - asset: Self::AssetId, - reason: &Self::Reason, - who: &T::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - let mut holds = Holds::::get(asset.clone(), who); - let amount_on_hold = - BalancesOnHold::::get(asset.clone(), who).unwrap_or_else(Zero::zero); - - let amount_on_hold = if amount.is_zero() { - if let Some(pos) = holds.iter().position(|x| &x.id == reason) { - let item = &mut holds[pos]; - let amount = item.amount; - - holds.swap_remove(pos); - amount_on_hold.checked_sub(&amount).ok_or(ArithmeticError::Underflow)? - } else { - amount_on_hold - } - } else { - let (increase, delta) = if let Some(pos) = holds.iter().position(|x| &x.id == reason) { - let item = &mut holds[pos]; - let (increase, delta) = - (amount > item.amount, item.amount.max(amount) - item.amount.min(amount)); - - item.amount = amount; - if item.amount.is_zero() { - holds.swap_remove(pos); - } - - (increase, delta) - } else { - holds - .try_push(IdAmount { id: *reason, amount }) - .map_err(|_| Error::::TooManyHolds)?; - (true, amount) - }; - - let amount_on_hold = if increase { - amount_on_hold.checked_add(&delta).ok_or(ArithmeticError::Overflow)? - } else { - amount_on_hold.checked_sub(&delta).ok_or(ArithmeticError::Underflow)? - }; - - amount_on_hold - }; - - if !holds.is_empty() { - Holds::::insert(asset.clone(), who, holds); - } else { - Holds::::remove(asset.clone(), who); - } - - if amount_on_hold.is_zero() { - BalancesOnHold::::remove(asset.clone(), who); - } else { - BalancesOnHold::::insert(asset.clone(), who, amount_on_hold); - } - - Ok(()) - } -} - -impl, I: 'static> MutateHold for Pallet { - fn done_hold( - asset_id: Self::AssetId, - reason: &Self::Reason, - who: &T::AccountId, - amount: Self::Balance, - ) { - Self::deposit_event(Event::::Held { - asset_id, - who: who.clone(), - reason: *reason, - amount, - }); - } - - fn done_release( - asset_id: Self::AssetId, - reason: &Self::Reason, - who: &T::AccountId, - amount: Self::Balance, - ) { - Self::deposit_event(Event::::Released { - asset_id, - who: who.clone(), - reason: *reason, - amount, - }); - } - - fn done_burn_held( - asset_id: Self::AssetId, - reason: &Self::Reason, - who: &T::AccountId, - amount: Self::Balance, - ) { - Self::deposit_event(Event::::Burned { - asset_id, - who: who.clone(), - reason: *reason, - amount, - }); - } -} diff --git a/pallets/assets-holder/src/lib.rs b/pallets/assets-holder/src/lib.rs deleted file mode 100644 index 75112fe9..00000000 --- a/pallets/assets-holder/src/lib.rs +++ /dev/null @@ -1,178 +0,0 @@ -// 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. - -//! # Assets Holder Pallet -//! -//! A pallet capable of holding fungibles from `pallet-assets`. This is an extension of -//! `pallet-assets`, wrapping [`fungibles::Inspect`](`frame_support::traits::fungibles::Inspect`). -//! It implements both -//! [`fungibles::hold::Inspect`](frame_support::traits::fungibles::hold::Inspect), -//! [`fungibles::hold::Mutate`](frame_support::traits::fungibles::hold::Mutate), and especially -//! [`fungibles::hold::Unbalanced`](frame_support::traits::fungibles::hold::Unbalanced). The -//! complexity of the operations is `O(1)`. -//! -//! ## Pallet API -//! -//! See the [`pallet`] module for more information about the interfaces this pallet exposes, -//! including its configuration trait, dispatchables, storage items, events and errors. -//! -//! ## Overview -//! -//! This pallet provides the following functionality: -//! -//! - Pallet hooks allowing [`pallet-assets`] to know the balance on hold for an account on a given -//! asset (see [`pallet_assets::BalanceOnHold`]). -//! - An implementation of -//! [`fungibles::hold::Inspect`](frame_support::traits::fungibles::hold::Inspect), -//! [`fungibles::hold::Mutate`](frame_support::traits::fungibles::hold::Mutate) and -//! [`fungibles::hold::Unbalanced`](frame_support::traits::fungibles::hold::Unbalanced), allowing -//! other pallets to manage holds for the `pallet-assets` assets. - -#![cfg_attr(not(feature = "std"), no_std)] - -use frame_support::{ - pallet_prelude::*, - traits::{tokens::IdAmount, VariantCount, VariantCountOf}, - BoundedVec, -}; -use frame_system::pallet_prelude::BlockNumberFor; - -pub use pallet::*; - -#[cfg(test)] -mod mock; -#[cfg(test)] -mod tests; - -mod impl_fungibles; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - - #[pallet::config(with_default)] - pub trait Config: - frame_system::Config + pallet_assets::Config> - { - /// The overarching freeze reason. - #[pallet::no_default_bounds] - type RuntimeHoldReason: Parameter + Member + MaxEncodedLen + Copy + VariantCount; - - /// The overarching event type. - #[pallet::no_default_bounds] - #[allow(deprecated)] - type RuntimeEvent: From> - + IsType<::RuntimeEvent>; - } - - #[pallet::error] - pub enum Error { - /// Number of holds on an account would exceed the count of `RuntimeHoldReason`. - TooManyHolds, - } - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event, I: 'static = ()> { - /// `who`s balance on hold was increased by `amount`. - Held { - who: T::AccountId, - asset_id: T::AssetId, - reason: T::RuntimeHoldReason, - amount: T::Balance, - }, - /// `who`s balance on hold was decreased by `amount`. - Released { - who: T::AccountId, - asset_id: T::AssetId, - reason: T::RuntimeHoldReason, - amount: T::Balance, - }, - /// `who`s balance on hold was burned by `amount`. - Burned { - who: T::AccountId, - asset_id: T::AssetId, - reason: T::RuntimeHoldReason, - amount: T::Balance, - }, - } - - /// A map that stores holds applied on an account for a given AssetId. - #[pallet::storage] - pub(super) type Holds, I: 'static = ()> = StorageDoubleMap< - _, - Blake2_128Concat, - T::AssetId, - Blake2_128Concat, - T::AccountId, - BoundedVec< - IdAmount, - VariantCountOf, - >, - ValueQuery, - >; - - /// A map that stores the current total balance on hold for every account on a given AssetId. - #[pallet::storage] - pub(super) type BalancesOnHold, I: 'static = ()> = StorageDoubleMap< - _, - Blake2_128Concat, - T::AssetId, - Blake2_128Concat, - T::AccountId, - T::Balance, - >; - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet { - #[cfg(feature = "try-runtime")] - fn try_state(_: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { - Self::do_try_state() - } - } -} - -impl, I: 'static> Pallet { - #[cfg(any(test, feature = "try-runtime"))] - fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { - use sp_runtime::{ - traits::{CheckedAdd, Zero}, - ArithmeticError, - }; - - for (asset, who, balance_on_hold) in BalancesOnHold::::iter() { - ensure!(balance_on_hold != Zero::zero(), "zero on hold must not be in state"); - - let mut amount_from_holds: T::Balance = Zero::zero(); - for l in Holds::::get(asset.clone(), who.clone()).iter() { - ensure!(l.amount != Zero::zero(), "zero amount is invalid"); - amount_from_holds = - amount_from_holds.checked_add(&l.amount).ok_or(ArithmeticError::Overflow)?; - } - - frame_support::ensure!( - balance_on_hold == amount_from_holds, - "The `BalancesOnHold` amount is not equal to the sum of `Holds` for (`asset`, `who`)" - ); - } - - Ok(()) - } -} diff --git a/pallets/assets-holder/src/mock.rs b/pallets/assets-holder/src/mock.rs deleted file mode 100644 index a170778a..00000000 --- a/pallets/assets-holder/src/mock.rs +++ /dev/null @@ -1,128 +0,0 @@ -// 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. - -//! Tests mock for `pallet-assets-freezer`. - -use crate as pallet_assets_holder; -pub use crate::*; -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::{derive_impl, traits::AsEnsureOriginWithArg}; -use scale_info::TypeInfo; -use sp_runtime::BuildStorage; - -pub type AccountId = ::AccountId; -pub type Balance = ::Balance; -pub type AssetId = ::AssetId; -type Block = frame_system::mocking::MockBlock; - -#[frame_support::runtime] -mod runtime { - #[runtime::runtime] - #[runtime::derive( - RuntimeCall, - RuntimeEvent, - RuntimeError, - RuntimeOrigin, - RuntimeTask, - RuntimeHoldReason, - RuntimeFreezeReason - )] - pub struct Test; - - #[runtime::pallet_index(0)] - pub type System = frame_system; - #[runtime::pallet_index(10)] - pub type Balances = pallet_balances; - #[runtime::pallet_index(20)] - pub type Assets = pallet_assets; - #[runtime::pallet_index(21)] - pub type AssetsHolder = pallet_assets_holder; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for Test { - type Block = Block; - type AccountData = pallet_balances::AccountData; -} - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] -impl pallet_balances::Config for Test { - type AccountStore = System; -} - -#[derive_impl(pallet_assets::config_preludes::TestDefaultConfig as pallet_assets::DefaultConfig)] -impl pallet_assets::Config for Test { - // type AssetAccountDeposit = ConstU64<1>; - type CreateOrigin = AsEnsureOriginWithArg>; - type ForceOrigin = frame_system::EnsureRoot; - type Currency = Balances; - type Holder = AssetsHolder; -} - -#[derive( - Decode, - DecodeWithMemTracking, - Encode, - MaxEncodedLen, - PartialEq, - Eq, - Ord, - PartialOrd, - TypeInfo, - Debug, - Clone, - Copy, -)] -pub enum DummyHoldReason { - Governance, - Staking, - Other, -} - -impl VariantCount for DummyHoldReason { - // Intentionally set below the actual count of variants, to allow testing for `can_freeze` - const VARIANT_COUNT: u32 = 3; -} - -impl Config for Test { - type RuntimeHoldReason = DummyHoldReason; - type RuntimeEvent = RuntimeEvent; -} - -pub fn new_test_ext(execute: impl FnOnce()) -> sp_io::TestExternalities { - let t = RuntimeGenesisConfig { - assets: pallet_assets::GenesisConfig { - assets: vec![(1, 0, true, 1)], - metadata: vec![], - accounts: vec![(1, 1, 100)], - next_asset_id: None, - reserves: vec![], - }, - system: Default::default(), - balances: Default::default(), - } - .build_storage() - .unwrap(); - let mut ext: sp_io::TestExternalities = t.into(); - ext.execute_with(|| { - System::set_block_number(1); - execute(); - frame_support::assert_ok!(AssetsHolder::do_try_state()); - }); - - ext -} diff --git a/pallets/assets-holder/src/tests.rs b/pallets/assets-holder/src/tests.rs deleted file mode 100644 index 487ad7fd..00000000 --- a/pallets/assets-holder/src/tests.rs +++ /dev/null @@ -1,574 +0,0 @@ -// 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. - -//! Tests for pallet-assets-holder. - -use crate::mock::*; - -use frame_support::{ - assert_noop, assert_ok, - traits::tokens::fungibles::{Inspect, InspectHold, MutateHold, UnbalancedHold}, -}; -use pallet_assets::BalanceOnHold; - -const WHO: AccountId = 1; -const ASSET_ID: AssetId = 1; - -fn test_hold(id: DummyHoldReason, amount: Balance) { - assert_ok!(AssetsHolder::set_balance_on_hold(ASSET_ID, &id, &WHO, amount)); -} - -fn test_release(id: DummyHoldReason) { - assert_ok!(AssetsHolder::set_balance_on_hold(ASSET_ID, &id, &WHO, 0)); -} - -mod impl_balance_on_hold { - use super::*; - - #[test] - fn balance_on_hold_works() { - new_test_ext(|| { - assert_eq!( - >::balance_on_hold(ASSET_ID, &WHO), - None - ); - test_hold(DummyHoldReason::Governance, 1); - assert_eq!( - >::balance_on_hold(ASSET_ID, &WHO), - Some(1u64) - ); - test_hold(DummyHoldReason::Staking, 3); - assert_eq!( - >::balance_on_hold(ASSET_ID, &WHO), - Some(4u64) - ); - test_hold(DummyHoldReason::Governance, 2); - assert_eq!( - >::balance_on_hold(ASSET_ID, &WHO), - Some(5u64) - ); - // also test releasing works to reduce a balance, and finally releasing everything - // resets to None - test_release(DummyHoldReason::Governance); - assert_eq!( - >::balance_on_hold(ASSET_ID, &WHO), - Some(3u64) - ); - test_release(DummyHoldReason::Staking); - assert_eq!( - >::balance_on_hold(ASSET_ID, &WHO), - None - ); - }); - } - - // `defensive_assert!` only panics when `debug_assertions` are enabled. CI runs - // tests in `--release`, so we split the expectation by build profile. - #[cfg(debug_assertions)] - #[test] - #[should_panic = "The list of Holds should be empty before allowing an account to die"] - fn died_fails_if_holds_exist() { - new_test_ext(|| { - test_hold(DummyHoldReason::Governance, 1); - AssetsHolder::died(ASSET_ID, &WHO); - }); - } - - // In release builds the defensive assertions don't panic; `died` still clears - // any lingering hold state instead. - #[cfg(not(debug_assertions))] - #[test] - fn died_fails_if_holds_exist() { - new_test_ext(|| { - test_hold(DummyHoldReason::Governance, 1); - AssetsHolder::died(ASSET_ID, &WHO); - assert!(BalancesOnHold::::get(ASSET_ID, WHO).is_none()); - assert!(Holds::::get(ASSET_ID, WHO).is_empty()); - }); - } - - #[test] - fn died_works() { - new_test_ext(|| { - test_hold(DummyHoldReason::Governance, 1); - test_release(DummyHoldReason::Governance); - AssetsHolder::died(ASSET_ID, &WHO); - assert!(BalancesOnHold::::get(ASSET_ID, WHO).is_none()); - assert!(Holds::::get(ASSET_ID, WHO).is_empty()); - }); - } -} - -mod impl_hold_inspect { - use super::*; - - #[test] - fn total_balance_on_hold_works() { - new_test_ext(|| { - assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 0u64); - test_hold(DummyHoldReason::Governance, 1); - assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 1u64); - test_hold(DummyHoldReason::Staking, 3); - assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 4u64); - test_hold(DummyHoldReason::Governance, 2); - assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 5u64); - // also test release to reduce a balance, and finally releasing everything resets to - // 0 - test_release(DummyHoldReason::Governance); - assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 3u64); - test_release(DummyHoldReason::Staking); - assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 0u64); - }); - } - - #[test] - fn balance_on_hold_works() { - new_test_ext(|| { - assert_eq!( - >::balance_on_hold( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO - ), - 0u64 - ); - test_hold(DummyHoldReason::Governance, 1); - assert_eq!( - >::balance_on_hold( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO - ), - 1u64 - ); - test_hold(DummyHoldReason::Staking, 3); - assert_eq!( - >::balance_on_hold( - ASSET_ID, - &DummyHoldReason::Staking, - &WHO - ), - 3u64 - ); - test_hold(DummyHoldReason::Staking, 2); - assert_eq!( - >::balance_on_hold( - ASSET_ID, - &DummyHoldReason::Staking, - &WHO - ), - 2u64 - ); - // also test release to reduce a balance, and finally releasing everything resets to - // 0 - test_release(DummyHoldReason::Governance); - assert_eq!( - >::balance_on_hold( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO - ), - 0u64 - ); - test_release(DummyHoldReason::Staking); - assert_eq!( - >::balance_on_hold( - ASSET_ID, - &DummyHoldReason::Staking, - &WHO - ), - 0u64 - ); - }); - } -} - -mod impl_hold_unbalanced { - use super::*; - - // Note: Tests for `handle_dust`, `write_balance`, `set_total_issuance`, `decrease_balance` - // and `increase_balance` are intentionally left out without testing, since: - // 1. It is expected these methods are tested within `pallet-assets`, and - // 2. There are no valid cases that can be directly asserted using those methods in - // the scope of this pallet. - - #[test] - fn set_balance_on_hold_works() { - new_test_ext(|| { - assert_eq!(Holds::::get(ASSET_ID, WHO).to_vec(), vec![]); - assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), None); - // Adding balance on hold works - assert_ok!(AssetsHolder::set_balance_on_hold( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO, - 1 - )); - assert_eq!( - Holds::::get(ASSET_ID, WHO).to_vec(), - vec![IdAmount { id: DummyHoldReason::Governance, amount: 1 }] - ); - assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), Some(1)); - // Increasing hold works - assert_ok!(AssetsHolder::set_balance_on_hold( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO, - 3 - )); - assert_eq!( - Holds::::get(ASSET_ID, WHO).to_vec(), - vec![IdAmount { id: DummyHoldReason::Governance, amount: 3 }] - ); - assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), Some(3)); - // Adding new balance on hold works - assert_ok!(AssetsHolder::set_balance_on_hold( - ASSET_ID, - &DummyHoldReason::Staking, - &WHO, - 2 - )); - assert_eq!( - Holds::::get(ASSET_ID, WHO).to_vec(), - vec![ - IdAmount { id: DummyHoldReason::Governance, amount: 3 }, - IdAmount { id: DummyHoldReason::Staking, amount: 2 } - ] - ); - assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), Some(5)); - - // Note: Assertion skipped to meet @gavofyork's suggestion of matching the number of - // variant count with the number of enum's variants. - // // Adding more than max holds fails - // assert_noop!( - // AssetsHolder::set_balance_on_hold(ASSET_ID, &DummyHoldReason::Other, &WHO, 1), - // Error::::TooManyHolds - // ); - - // Decreasing balance on hold works - assert_ok!(AssetsHolder::set_balance_on_hold( - ASSET_ID, - &DummyHoldReason::Staking, - &WHO, - 1 - )); - assert_eq!( - Holds::::get(ASSET_ID, WHO).to_vec(), - vec![ - IdAmount { id: DummyHoldReason::Governance, amount: 3 }, - IdAmount { id: DummyHoldReason::Staking, amount: 1 } - ] - ); - assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), Some(4)); - // Decreasing until removal of balance on hold works - assert_ok!(AssetsHolder::set_balance_on_hold( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO, - 0 - )); - assert_eq!( - Holds::::get(ASSET_ID, WHO).to_vec(), - vec![IdAmount { id: DummyHoldReason::Staking, amount: 1 }] - ); - assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), Some(1)); - // Clearing ol all holds works - assert_ok!(AssetsHolder::set_balance_on_hold( - ASSET_ID, - &DummyHoldReason::Staking, - &WHO, - 0 - )); - assert_eq!(Holds::::get(ASSET_ID, WHO).to_vec(), vec![]); - assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), None); - }); - } -} - -mod impl_hold_mutate { - use super::*; - use frame_support::traits::tokens::{Fortitude, Precision, Preservation}; - use sp_runtime::TokenError; - - #[test] - fn hold_works() { - super::new_test_ext(|| { - // Holding some `amount` would decrease the asset account balance and change the - // reducible balance, while total issuance is preserved. - assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Governance, &WHO, 10)); - assert_eq!(Assets::balance(ASSET_ID, &WHO), 90); - // Reducible balance is tested once to ensure token balance model is compliant. - assert_eq!( - Assets::reducible_balance( - ASSET_ID, - &WHO, - Preservation::Expendable, - Fortitude::Force - ), - 89 - ); - assert_eq!( - >::balance_on_hold( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO - ), - 10 - ); - assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 10); - // Holding preserves `total_balance` - assert_eq!(Assets::total_balance(ASSET_ID, &WHO), 100); - // Holding preserves `total_issuance` - assert_eq!(Assets::total_issuance(ASSET_ID), 100); - - // Increasing the amount on hold for the same reason has the same effect as described - // above in `set_balance_on_hold_works`, while total issuance is preserved. - // Consideration: holding for an amount `x` will increase the already amount on hold by - // `x`. - assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Governance, &WHO, 20)); - assert_eq!(Assets::balance(ASSET_ID, &WHO), 70); - assert_eq!( - >::balance_on_hold( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO - ), - 30 - ); - assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 30); - assert_eq!(Assets::total_issuance(ASSET_ID), 100); - - // Holding some amount for a different reason has the same effect as described above in - // `set_balance_on_hold_works`, while total issuance is preserved. - assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Staking, &WHO, 20)); - assert_eq!(Assets::balance(ASSET_ID, &WHO), 50); - assert_eq!( - >::balance_on_hold( - ASSET_ID, - &DummyHoldReason::Staking, - &WHO - ), - 20 - ); - assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 50); - assert_eq!(Assets::total_issuance(ASSET_ID), 100); - }); - } - - fn new_test_ext() -> sp_io::TestExternalities { - super::new_test_ext(|| { - assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Governance, &WHO, 30)); - assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Staking, &WHO, 20)); - }) - } - - #[test] - fn release_works() { - // Releasing up to some amount will increase the balance by the released - // amount, while preserving total issuance. - new_test_ext().execute_with(|| { - assert_ok!(AssetsHolder::release( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO, - 20, - Precision::Exact, - )); - assert_eq!( - >::balance_on_hold( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO - ), - 10 - ); - assert_eq!(Assets::balance(ASSET_ID, WHO), 70); - }); - - // Releasing over the max amount on hold with `BestEffort` will increase the - // balance by the previously amount on hold, while preserving total issuance. - new_test_ext().execute_with(|| { - assert_ok!(AssetsHolder::release( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO, - 31, - Precision::BestEffort, - )); - assert_eq!( - >::balance_on_hold( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO - ), - 0 - ); - assert_eq!(Assets::balance(ASSET_ID, WHO), 80); - }); - - // Releasing over the max amount on hold with `Exact` will fail. - new_test_ext().execute_with(|| { - assert_noop!( - AssetsHolder::release( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO, - 31, - Precision::Exact, - ), - TokenError::FundsUnavailable - ); - }); - } - - #[test] - fn burn_held_works() { - // Burning works, reducing total issuance and `total_balance`. - new_test_ext().execute_with(|| { - assert_ok!(AssetsHolder::burn_held( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO, - 1, - Precision::BestEffort, - Fortitude::Polite - )); - assert_eq!(Assets::total_balance(ASSET_ID, &WHO), 99); - assert_eq!(Assets::total_issuance(ASSET_ID), 99); - }); - - // Burning by an amount up to the balance on hold with `Exact` works, reducing balance on - // hold up to the given amount. - new_test_ext().execute_with(|| { - assert_ok!(AssetsHolder::burn_held( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO, - 10, - Precision::Exact, - Fortitude::Polite - )); - assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 40); - assert_eq!(Assets::balance(ASSET_ID, WHO), 50); - }); - - // Burning by an amount over the balance on hold with `BestEffort` works, reducing balance - // on hold up to the given amount. - new_test_ext().execute_with(|| { - assert_ok!(AssetsHolder::burn_held( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO, - 31, - Precision::BestEffort, - Fortitude::Polite - )); - assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 20); - assert_eq!(Assets::balance(ASSET_ID, WHO), 50); - }); - - // Burning by an amount over the balance on hold with `Exact` fails. - new_test_ext().execute_with(|| { - assert_noop!( - AssetsHolder::burn_held( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO, - 31, - Precision::Exact, - Fortitude::Polite - ), - TokenError::FundsUnavailable - ); - }); - } - - #[test] - fn burn_all_held_works() { - new_test_ext().execute_with(|| { - // Burning all balance on hold works as burning passing it as amount with `BestEffort` - assert_ok!(AssetsHolder::burn_all_held( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO, - Precision::BestEffort, - Fortitude::Polite, - )); - assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 20); - assert_eq!(Assets::balance(ASSET_ID, WHO), 50); - }); - } - - #[test] - fn done_held_works() { - new_test_ext().execute_with(|| { - System::assert_has_event( - Event::::Held { - who: WHO, - asset_id: ASSET_ID, - reason: DummyHoldReason::Governance, - amount: 30, - } - .into(), - ); - }); - } - - #[test] - fn done_release_works() { - new_test_ext().execute_with(|| { - assert_ok!(AssetsHolder::release( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO, - 31, - Precision::BestEffort - )); - System::assert_has_event( - Event::::Released { - who: WHO, - asset_id: ASSET_ID, - reason: DummyHoldReason::Governance, - amount: 30, - } - .into(), - ); - }); - } - - #[test] - fn done_burn_held_works() { - new_test_ext().execute_with(|| { - assert_ok!(AssetsHolder::burn_all_held( - ASSET_ID, - &DummyHoldReason::Governance, - &WHO, - Precision::BestEffort, - Fortitude::Polite, - )); - System::assert_has_event( - Event::::Burned { - who: WHO, - asset_id: ASSET_ID, - reason: DummyHoldReason::Governance, - amount: 30, - } - .into(), - ); - }); - } -} diff --git a/pallets/assets/Cargo.toml b/pallets/assets/Cargo.toml deleted file mode 100644 index f018a779..00000000 --- a/pallets/assets/Cargo.toml +++ /dev/null @@ -1,55 +0,0 @@ -[package] -authors.workspace = true -description = "FRAME asset management pallet" -edition.workspace = true -homepage.workspace = true -license = "Apache-2.0" -name = "pallet-assets" -readme = "README.md" -repository.workspace = true -version = "48.1.0" - -# Vendored upstream FRAME pallet: the pallet macros expand to expect/unwrap, so -# we deliberately don't apply the workspace restriction lints here. - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { workspace = true } -frame-benchmarking = { optional = true, workspace = true } -frame-support.workspace = true -frame-system.workspace = true -impl-trait-for-tuples = { workspace = true } -log = { workspace = true } -scale-info = { features = ["derive"], workspace = true } -sp-core.workspace = true -sp-runtime.workspace = true - -[dev-dependencies] -pallet-balances = { workspace = true, default-features = true } -sp-io = { workspace = true, default-features = true } - -[features] -default = ["std"] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "log/std", - "scale-info/std", - "sp-core/std", - "sp-runtime/std", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/pallets/assets/README.md b/pallets/assets/README.md deleted file mode 100644 index 863bcccb..00000000 --- a/pallets/assets/README.md +++ /dev/null @@ -1,124 +0,0 @@ -# Assets Module - -A simple, secure module for dealing with fungible assets. - -## Overview - -The Assets module provides functionality for asset management of fungible asset classes with a fixed supply, including: - -* Asset Issuance -* Asset Transfer -* Asset Destruction - -To use it in your runtime, you need to implement the assets -[`assets::Config`](https://docs.rs/pallet-assets/latest/pallet_assets/pallet/trait.Config.html). - -The supported dispatchable functions are documented in the -[`assets::Call`](https://docs.rs/pallet-assets/latest/pallet_assets/pallet/enum.Call.html) enum. - -### Terminology - -* **Asset issuance:** The creation of a new asset, whose total supply will belong to the account that issues the asset. -* **Asset transfer:** The action of transferring assets from one account to another. -* **Asset destruction:** The process of an account removing its entire holding of an asset. -* **Fungible asset:** An asset whose units are interchangeable. -* **Non-fungible asset:** An asset for which each unit has unique characteristics. - -### Goals - -The assets system in Substrate is designed to make the following possible: - -* Issue a unique asset to its creator's account. -* Move assets between accounts. -* Remove an account's balance of an asset when requested by that account's owner and update the asset's total supply. - -## Interface - -### Dispatchable Functions - -* `issue` - Issues the total supply of a new fungible asset to the account of the caller of the function. -* `transfer` - Transfers an `amount` of units of fungible asset `id` from the balance of the function caller's account -(`origin`) to a `target` account. -* `destroy` - Destroys the entire holding of a fungible asset `id` associated with the account that called the function. - -Please refer to the [`Call`](https://docs.rs/pallet-assets/latest/pallet_assets/enum.Call.html) enum and its associated -variants for documentation on each function. - -### Public Functions - - -* `balance` - Get the asset `id` balance of `who`. -* `total_supply` - Get the total supply of an asset `id`. - -Please refer to the [`Pallet`](https://docs.rs/pallet-assets/latest/pallet_assets/pallet/struct.Pallet.html) struct for -details on publicly available functions. - -## Usage - -The following example shows how to use the Assets module in your runtime by exposing public functions to: - -* Issue a new fungible asset for a token distribution event (airdrop). -* Query the fungible asset holding balance of an account. -* Query the total supply of a fungible asset that has been issued. - -### Prerequisites - -Import the Assets module and types and derive your runtime's configuration traits from the Assets module trait. - -### Simple Code Snippet - -```rust -use pallet_assets as assets; -use sp_runtime::ArithmeticError; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::config] - pub trait Config: frame_system::Config + assets::Config {} - - #[pallet::call] - impl Pallet { - pub fn issue_token_airdrop(origin: OriginFor) -> DispatchResult { - let sender = ensure_signed(origin)?; - - const ACCOUNT_ALICE: u64 = 1; - const ACCOUNT_BOB: u64 = 2; - const COUNT_AIRDROP_RECIPIENTS: u64 = 2; - const TOKENS_FIXED_SUPPLY: u64 = 100; - - ensure!(!COUNT_AIRDROP_RECIPIENTS.is_zero(), ArithmeticError::DivisionByZero); - - let asset_id = Self::next_asset_id(); - - >::mutate(|asset_id| *asset_id += 1); - >::insert((asset_id, &ACCOUNT_ALICE), TOKENS_FIXED_SUPPLY / COUNT_AIRDROP_RECIPIENTS); - >::insert((asset_id, &ACCOUNT_BOB), TOKENS_FIXED_SUPPLY / COUNT_AIRDROP_RECIPIENTS); - >::insert(asset_id, TOKENS_FIXED_SUPPLY); - - Self::deposit_event(Event::Issued(asset_id, sender, TOKENS_FIXED_SUPPLY)); - Ok(()) - } - } -} -``` - -## Assumptions - -Below are assumptions that must be held when using this module. If any of them are violated, the behavior of this -module is undefined. - -* The total count of assets should be less than `Config::AssetId::max_value()`. - -## Related Modules - -* [`System`](https://docs.rs/frame-system/latest/frame_system/) -* [`Support`](https://docs.rs/frame-support/latest/frame_support/) - -License: Apache-2.0 diff --git a/pallets/assets/src/benchmarking.rs b/pallets/assets/src/benchmarking.rs deleted file mode 100644 index 39959039..00000000 --- a/pallets/assets/src/benchmarking.rs +++ /dev/null @@ -1,645 +0,0 @@ -// 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. - -//! Assets pallet benchmarking. - -#![cfg(feature = "runtime-benchmarks")] - -use super::*; -use alloc::vec; -use frame_benchmarking::{ - v1::{ - account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, BenchmarkError, - }, - BenchmarkResult, -}; -use frame_support::traits::{EnsureOrigin, Get, UnfilteredDispatchable}; -use frame_system::RawOrigin as SystemOrigin; -use sp_runtime::{traits::Bounded, Weight}; - -use crate::Pallet as Assets; - -const SEED: u32 = 0; -const MIN_BALANCE: u32 = 1; - -fn default_asset_id, I: 'static>() -> T::AssetIdParameter { - T::BenchmarkHelper::create_asset_id_parameter(0) -} - -fn create_default_asset, I: 'static>( - is_sufficient: bool, -) -> (T::AssetIdParameter, T::AccountId, AccountIdLookupOf) { - let asset_id = default_asset_id::(); - let caller: T::AccountId = whitelisted_caller(); - let caller_lookup = T::Lookup::unlookup(caller.clone()); - let root = SystemOrigin::Root.into(); - assert!(Assets::::force_create( - root, - asset_id.clone(), - caller_lookup.clone(), - is_sufficient, - MIN_BALANCE.into(), - ) - .is_ok()); - (asset_id, caller, caller_lookup) -} - -fn create_default_reserves, I: 'static>( - count: u32, -) -> (T::AssetIdParameter, T::AccountId, Vec) { - // create asset - let (asset_id, caller, _) = create_default_asset::(true); - // build max number of reserves - let mut reserves = Vec::::new(); - for i in 0..count { - reserves.push(T::BenchmarkHelper::create_reserve_id_parameter(i)); - } - (asset_id, caller, reserves) -} - -pub fn create_default_minted_asset, I: 'static>( - is_sufficient: bool, - amount: T::Balance, -) -> (T::AssetIdParameter, T::AccountId, AccountIdLookupOf) { - let (asset_id, caller, caller_lookup) = create_default_asset::(is_sufficient); - if !is_sufficient { - T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance()); - } - assert!(Assets::::mint( - SystemOrigin::Signed(caller.clone()).into(), - asset_id.clone(), - caller_lookup.clone(), - amount, - ) - .is_ok()); - (asset_id, caller, caller_lookup) -} - -fn swap_is_sufficient, I: 'static>(s: &mut bool) { - let asset_id = default_asset_id::(); - Asset::::mutate(&asset_id.into(), |maybe_a| { - if let Some(ref mut a) = maybe_a { - core::mem::swap(s, &mut a.is_sufficient) - } - }); -} - -fn add_sufficients, I: 'static>(minter: T::AccountId, n: u32) { - let asset_id = default_asset_id::(); - let origin = SystemOrigin::Signed(minter); - let mut s = true; - swap_is_sufficient::(&mut s); - for i in 0..n { - let target = account("sufficient", i, SEED); - let target_lookup = T::Lookup::unlookup(target); - assert!(Assets::::mint( - origin.clone().into(), - asset_id.clone(), - target_lookup, - 100u32.into(), - ) - .is_ok()); - } - swap_is_sufficient::(&mut s); -} - -fn add_approvals, I: 'static>(minter: T::AccountId, n: u32) { - let asset_id = default_asset_id::(); - let _ = T::Currency::deposit_creating( - &minter, - T::ApprovalDeposit::get() * n.into() + T::Currency::minimum_balance(), - ); - let minter_lookup = T::Lookup::unlookup(minter.clone()); - let origin = SystemOrigin::Signed(minter); - Assets::::mint( - origin.clone().into(), - asset_id.clone(), - minter_lookup, - (100 * (n + 1)).into(), - ) - .unwrap(); - let enough = T::Currency::minimum_balance(); - for i in 0..n { - let target = account("approval", i, SEED); - T::Currency::make_free_balance_be(&target, enough); - let target_lookup = T::Lookup::unlookup(target); - Assets::::approve_transfer( - origin.clone().into(), - asset_id.clone(), - target_lookup, - 100u32.into(), - ) - .unwrap(); - } -} - -fn assert_last_event, I: 'static>(generic_event: >::RuntimeEvent) { - frame_system::Pallet::::assert_last_event(generic_event.into()); -} - -fn assert_event, I: 'static>(generic_event: >::RuntimeEvent) { - frame_system::Pallet::::assert_has_event(generic_event.into()); -} - -benchmarks_instance_pallet! { - create { - let asset_id = default_asset_id::(); - let origin = T::CreateOrigin::try_successful_origin(&asset_id.clone().into()) - .map_err(|_| BenchmarkError::Weightless)?; - let caller = T::CreateOrigin::ensure_origin(origin.clone(), &asset_id.clone().into()).unwrap(); - let caller_lookup = T::Lookup::unlookup(caller.clone()); - T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - }: _(origin, asset_id.clone(), caller_lookup, 1u32.into()) - verify { - assert_last_event::(Event::Created { asset_id: asset_id.into(), creator: caller.clone(), owner: caller }.into()); - } - - force_create { - let asset_id = default_asset_id::(); - let caller: T::AccountId = whitelisted_caller(); - let caller_lookup = T::Lookup::unlookup(caller.clone()); - }: _(SystemOrigin::Root, asset_id.clone(), caller_lookup, true, 1u32.into()) - verify { - assert_last_event::(Event::ForceCreated { asset_id: asset_id.into(), owner: caller }.into()); - } - - start_destroy { - let (asset_id, caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); - Assets::::freeze_asset( - SystemOrigin::Signed(caller.clone()).into(), - asset_id.clone(), - )?; - }:_(SystemOrigin::Signed(caller), asset_id.clone()) - verify { - assert_last_event::(Event::DestructionStarted { asset_id: asset_id.into() }.into()); - } - - destroy_accounts { - let c in 0 .. T::RemoveItemsLimit::get(); - let (asset_id, caller, _) = create_default_asset::(true); - add_sufficients::(caller.clone(), c); - Assets::::freeze_asset( - SystemOrigin::Signed(caller.clone()).into(), - asset_id.clone(), - )?; - Assets::::start_destroy(SystemOrigin::Signed(caller.clone()).into(), asset_id.clone())?; - }:_(SystemOrigin::Signed(caller), asset_id.clone()) - verify { - assert_last_event::(Event::AccountsDestroyed { - asset_id: asset_id.into(), - accounts_destroyed: c, - accounts_remaining: 0, - }.into()); - } - - destroy_approvals { - let a in 0 .. T::RemoveItemsLimit::get(); - let (asset_id, caller, _) = create_default_minted_asset::(true, 100u32.into()); - add_approvals::(caller.clone(), a); - Assets::::freeze_asset( - SystemOrigin::Signed(caller.clone()).into(), - asset_id.clone(), - )?; - Assets::::start_destroy(SystemOrigin::Signed(caller.clone()).into(), asset_id.clone())?; - }:_(SystemOrigin::Signed(caller), asset_id.clone()) - verify { - assert_last_event::(Event::ApprovalsDestroyed { - asset_id: asset_id.into(), - approvals_destroyed: a, - approvals_remaining: 0, - }.into()); - } - - finish_destroy { - let (asset_id, caller, caller_lookup) = create_default_asset::(true); - Assets::::freeze_asset( - SystemOrigin::Signed(caller.clone()).into(), - asset_id.clone(), - )?; - Assets::::start_destroy(SystemOrigin::Signed(caller.clone()).into(), asset_id.clone())?; - }:_(SystemOrigin::Signed(caller), asset_id.clone()) - verify { - assert_last_event::(Event::Destroyed { - asset_id: asset_id.into(), - }.into() - ); - } - - mint { - let (asset_id, caller, caller_lookup) = create_default_asset::(true); - let amount = T::Balance::from(100u32); - }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), caller_lookup, amount) - verify { - assert_last_event::(Event::Issued { asset_id: asset_id.into(), owner: caller, amount }.into()); - } - - burn { - let amount = T::Balance::from(100u32); - let (asset_id, caller, caller_lookup) = create_default_minted_asset::(true, amount); - }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), caller_lookup, amount) - verify { - assert_last_event::(Event::Burned { asset_id: asset_id.into(), owner: caller, balance: amount }.into()); - } - - transfer { - let amount = T::Balance::from(100u32); - let (asset_id, caller, caller_lookup) = create_default_minted_asset::(true, amount); - let target: T::AccountId = account("target", 0, SEED); - let target_lookup = T::Lookup::unlookup(target.clone()); - }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), target_lookup, amount) - verify { - assert_last_event::(Event::Transferred { asset_id: asset_id.into(), from: caller, to: target, amount }.into()); - } - - transfer_keep_alive { - let mint_amount = T::Balance::from(200u32); - let amount = T::Balance::from(100u32); - let (asset_id, caller, caller_lookup) = create_default_minted_asset::(true, mint_amount); - let target: T::AccountId = account("target", 0, SEED); - let target_lookup = T::Lookup::unlookup(target.clone()); - }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), target_lookup, amount) - verify { - assert!(frame_system::Pallet::::account_exists(&caller)); - assert_last_event::(Event::Transferred { asset_id: asset_id.into(), from: caller, to: target, amount }.into()); - } - - force_transfer { - let amount = T::Balance::from(100u32); - let (asset_id, caller, caller_lookup) = create_default_minted_asset::(true, amount); - let target: T::AccountId = account("target", 0, SEED); - let target_lookup = T::Lookup::unlookup(target.clone()); - }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), caller_lookup, target_lookup, amount) - verify { - assert_last_event::( - Event::Transferred { asset_id: asset_id.into(), from: caller, to: target, amount }.into() - ); - } - - freeze { - let (asset_id, caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); - }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), caller_lookup) - verify { - assert_last_event::(Event::Frozen { asset_id: asset_id.into(), who: caller }.into()); - } - - thaw { - let (asset_id, caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); - Assets::::freeze( - SystemOrigin::Signed(caller.clone()).into(), - asset_id.clone(), - caller_lookup.clone(), - )?; - }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), caller_lookup) - verify { - assert_last_event::(Event::Thawed { asset_id: asset_id.into(), who: caller }.into()); - } - - freeze_asset { - let (asset_id, caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); - }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone()) - verify { - assert_last_event::(Event::AssetFrozen { asset_id: asset_id.into() }.into()); - } - - thaw_asset { - let (asset_id, caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); - Assets::::freeze_asset( - SystemOrigin::Signed(caller.clone()).into(), - asset_id.clone(), - )?; - }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone()) - verify { - assert_last_event::(Event::AssetThawed { asset_id: asset_id.into() }.into()); - } - - transfer_ownership { - let (asset_id, caller, _) = create_default_asset::(true); - let target: T::AccountId = account("target", 0, SEED); - let target_lookup = T::Lookup::unlookup(target.clone()); - }: _(SystemOrigin::Signed(caller), asset_id.clone(), target_lookup) - verify { - assert_last_event::(Event::OwnerChanged { asset_id: asset_id.into(), owner: target }.into()); - } - - set_team { - let (asset_id, caller, _) = create_default_asset::(true); - let target0 = T::Lookup::unlookup(account("target", 0, SEED)); - let target1 = T::Lookup::unlookup(account("target", 1, SEED)); - let target2 = T::Lookup::unlookup(account("target", 2, SEED)); - }: _(SystemOrigin::Signed(caller), asset_id.clone(), target0, target1, target2) - verify { - assert_last_event::(Event::TeamChanged { - asset_id: asset_id.into(), - issuer: account("target", 0, SEED), - admin: account("target", 1, SEED), - freezer: account("target", 2, SEED), - }.into()); - } - - set_reserves { - let n in 0 .. MAX_RESERVES; - let (asset_id, caller, reserves) = create_default_reserves::(n); - T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - let bounded_reserves = reserves.clone().try_into().unwrap(); - }: _(SystemOrigin::Signed(caller), asset_id.clone(), bounded_reserves) - verify { - let expected_event = if reserves.is_empty() { - Event::ReservesRemoved { asset_id: asset_id.into() } - } else { - Event::ReservesUpdated { asset_id: asset_id.into(), reserves: reserves } - }; - assert_last_event::(expected_event.into()); - } - - set_metadata { - let n in 0 .. T::StringLimit::get(); - let s in 0 .. T::StringLimit::get(); - - let name = vec![0u8; n as usize]; - let symbol = vec![0u8; s as usize]; - let decimals = 12; - - let (asset_id, caller, _) = create_default_asset::(true); - T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - }: _(SystemOrigin::Signed(caller), asset_id.clone(), name.clone(), symbol.clone(), decimals) - verify { - assert_last_event::(Event::MetadataSet { asset_id: asset_id.into(), name, symbol, decimals, is_frozen: false }.into()); - } - - clear_metadata { - let (asset_id, caller, _) = create_default_asset::(true); - T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - let dummy = vec![0u8; T::StringLimit::get() as usize]; - let origin = SystemOrigin::Signed(caller.clone()).into(); - Assets::::set_metadata(origin, asset_id.clone(), dummy.clone(), dummy, 12)?; - }: _(SystemOrigin::Signed(caller), asset_id.clone()) - verify { - assert_last_event::(Event::MetadataCleared { asset_id: asset_id.into() }.into()); - } - - force_set_metadata { - let n in 0 .. T::StringLimit::get(); - let s in 0 .. T::StringLimit::get(); - - let name = vec![0u8; n as usize]; - let symbol = vec![0u8; s as usize]; - let decimals = 12; - - let (asset_id, _, _) = create_default_asset::(true); - - let origin = - T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let call = Call::::force_set_metadata { - id: asset_id.clone(), - name: name.clone(), - symbol: symbol.clone(), - decimals, - is_frozen: false, - }; - }: { call.dispatch_bypass_filter(origin)? } - verify { - assert_last_event::(Event::MetadataSet { asset_id: asset_id.into(), name, symbol, decimals, is_frozen: false }.into()); - } - - force_clear_metadata { - let (asset_id, caller, _) = create_default_asset::(true); - T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - let dummy = vec![0u8; T::StringLimit::get() as usize]; - let origin = SystemOrigin::Signed(caller).into(); - Assets::::set_metadata(origin, asset_id.clone(), dummy.clone(), dummy, 12)?; - - let origin = - T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let call = Call::::force_clear_metadata { id: asset_id.clone() }; - }: { call.dispatch_bypass_filter(origin)? } - verify { - assert_last_event::(Event::MetadataCleared { asset_id: asset_id.into() }.into()); - } - - force_asset_status { - let (asset_id, caller, caller_lookup) = create_default_asset::(true); - - let origin = - T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let call = Call::::force_asset_status { - id: asset_id.clone(), - owner: caller_lookup.clone(), - issuer: caller_lookup.clone(), - admin: caller_lookup.clone(), - freezer: caller_lookup, - min_balance: 100u32.into(), - is_sufficient: true, - is_frozen: false, - }; - }: { call.dispatch_bypass_filter(origin)? } - verify { - assert_last_event::(Event::AssetStatusChanged { asset_id: asset_id.into() }.into()); - } - - approve_transfer { - let (asset_id, caller, _) = create_default_minted_asset::(true, 100u32.into()); - T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - - let delegate: T::AccountId = account("delegate", 0, SEED); - let delegate_lookup = T::Lookup::unlookup(delegate.clone()); - let amount = 100u32.into(); - }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), delegate_lookup, amount) - verify { - assert_last_event::(Event::ApprovedTransfer { asset_id: asset_id.into(), source: caller, delegate, amount }.into()); - } - - transfer_approved { - let (asset_id, owner, owner_lookup) = create_default_minted_asset::(true, 100u32.into()); - T::Currency::make_free_balance_be(&owner, DepositBalanceOf::::max_value()); - - let delegate: T::AccountId = account("delegate", 0, SEED); - whitelist_account!(delegate); - let delegate_lookup = T::Lookup::unlookup(delegate.clone()); - let amount = 100u32.into(); - let origin = SystemOrigin::Signed(owner.clone()).into(); - Assets::::approve_transfer(origin, asset_id.clone(), delegate_lookup, amount)?; - - let dest: T::AccountId = account("dest", 0, SEED); - let dest_lookup = T::Lookup::unlookup(dest.clone()); - }: _(SystemOrigin::Signed(delegate.clone()), asset_id.clone(), owner_lookup, dest_lookup, amount) - verify { - assert!(T::Currency::reserved_balance(&owner).is_zero()); - assert_event::(Event::Transferred { asset_id: asset_id.into(), from: owner, to: dest, amount }.into()); - } - - cancel_approval { - let (asset_id, caller, _) = create_default_minted_asset::(true, 100u32.into()); - T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - - let delegate: T::AccountId = account("delegate", 0, SEED); - let delegate_lookup = T::Lookup::unlookup(delegate.clone()); - let amount = 100u32.into(); - let origin = SystemOrigin::Signed(caller.clone()).into(); - Assets::::approve_transfer(origin, asset_id.clone(), delegate_lookup.clone(), amount)?; - }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), delegate_lookup) - verify { - assert_last_event::(Event::ApprovalCancelled { asset_id: asset_id.into(), owner: caller, delegate }.into()); - } - - force_cancel_approval { - let (asset_id, caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); - T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - - let delegate: T::AccountId = account("delegate", 0, SEED); - let delegate_lookup = T::Lookup::unlookup(delegate.clone()); - let amount = 100u32.into(); - let origin = SystemOrigin::Signed(caller.clone()).into(); - Assets::::approve_transfer(origin, asset_id.clone(), delegate_lookup.clone(), amount)?; - }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), caller_lookup, delegate_lookup) - verify { - assert_last_event::(Event::ApprovalCancelled { asset_id: asset_id.into(), owner: caller, delegate }.into()); - } - - set_min_balance { - let (asset_id, caller, caller_lookup) = create_default_asset::(false); - }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), 50u32.into()) - verify { - assert_last_event::(Event::AssetMinBalanceChanged { asset_id: asset_id.into(), new_min_balance: 50u32.into() }.into()); - } - - touch { - let (asset_id, asset_owner, asset_owner_lookup) = create_default_asset::(false); - let new_account: T::AccountId = account("newaccount", 1, SEED); - T::Currency::make_free_balance_be(&new_account, DepositBalanceOf::::max_value()); - assert_ne!(asset_owner, new_account); - assert!(!Account::::contains_key(asset_id.clone().into(), &new_account)); - }: _(SystemOrigin::Signed(new_account.clone()), asset_id.clone()) - verify { - assert!(Account::::contains_key(asset_id.into(), &new_account)); - } - - touch_other { - let (asset_id, asset_owner, asset_owner_lookup) = create_default_asset::(false); - let new_account: T::AccountId = account("newaccount", 1, SEED); - let new_account_lookup = T::Lookup::unlookup(new_account.clone()); - T::Currency::make_free_balance_be(&asset_owner, DepositBalanceOf::::max_value()); - assert_ne!(asset_owner, new_account); - assert!(!Account::::contains_key(asset_id.clone().into(), &new_account)); - }: _(SystemOrigin::Signed(asset_owner.clone()), asset_id.clone(), new_account_lookup) - verify { - assert!(Account::::contains_key(asset_id.into(), &new_account)); - } - - refund { - let (asset_id, asset_owner, asset_owner_lookup) = create_default_asset::(false); - let new_account: T::AccountId = account("newaccount", 1, SEED); - T::Currency::make_free_balance_be(&new_account, DepositBalanceOf::::max_value()); - assert_ne!(asset_owner, new_account); - assert!(Assets::::touch( - SystemOrigin::Signed(new_account.clone()).into(), - asset_id.clone() - ).is_ok()); - // `touch` should reserve balance of the caller according to the `AssetAccountDeposit` amount... - assert_eq!(T::Currency::reserved_balance(&new_account), T::AssetAccountDeposit::get()); - // ...and also create an `Account` entry. - assert!(Account::::contains_key(asset_id.clone().into(), &new_account)); - }: _(SystemOrigin::Signed(new_account.clone()), asset_id, true) - verify { - // `refund`ing should of course repatriate the reserve - assert!(T::Currency::reserved_balance(&new_account).is_zero()); - } - - refund_other { - let (asset_id, asset_owner, asset_owner_lookup) = create_default_asset::(false); - let new_account: T::AccountId = account("newaccount", 1, SEED); - let new_account_lookup = T::Lookup::unlookup(new_account.clone()); - T::Currency::make_free_balance_be(&asset_owner, DepositBalanceOf::::max_value()); - assert_ne!(asset_owner, new_account); - assert!(Assets::::touch_other( - SystemOrigin::Signed(asset_owner.clone()).into(), - asset_id.clone(), - new_account_lookup.clone() - ).is_ok()); - // `touch` should reserve balance of the caller according to the `AssetAccountDeposit` amount... - assert_eq!(T::Currency::reserved_balance(&asset_owner), T::AssetAccountDeposit::get()); - assert!(Account::::contains_key(asset_id.clone().into(), &new_account)); - }: _(SystemOrigin::Signed(asset_owner.clone()), asset_id, new_account_lookup.clone()) - verify { - // this should repatriate the reserved balance of the freezer - assert!(T::Currency::reserved_balance(&asset_owner).is_zero()); - } - - block { - let (asset_id, caller, caller_lookup) = create_default_minted_asset::(true, 100u32.into()); - }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), caller_lookup) - verify { - assert_last_event::(Event::Blocked { asset_id: asset_id.into(), who: caller }.into()); - } - - transfer_all { - let amount = T::Balance::from(2 * MIN_BALANCE); - let (asset_id, caller, caller_lookup) = create_default_minted_asset::(true, amount); - let target: T::AccountId = account("target", 0, SEED); - let target_lookup = T::Lookup::unlookup(target.clone()); - }: _(SystemOrigin::Signed(caller.clone()), asset_id.clone(), target_lookup, false) - verify { - assert_last_event::(Event::Transferred { asset_id: asset_id.into(), from: caller, to: target, amount }.into()); - } - - total_issuance { - use frame_support::traits::fungibles::Inspect; - let (asset_id, _, _) = create_default_minted_asset::(true, 100u32.into()); - let amount; - }: { - amount = Pallet::::total_issuance(asset_id.into()); - } verify { - assert_eq!(amount, 100u32.into()); - } - - balance { - let (asset_id, caller, _) = create_default_minted_asset::(true, 100u32.into()); - let amount; - }: { - amount = Pallet::::balance(asset_id.into(), caller); - } verify { - assert_eq!(amount, 100u32.into()); - } - - allowance { - use frame_support::traits::fungibles::approvals::Inspect; - let (asset_id, caller, _) = create_default_minted_asset::(true, 100u32.into()); - add_approvals::(caller.clone(), 1); - let delegate: T::AccountId = account("approval", 0, SEED); - let amount; - }: { - amount = Pallet::::allowance(asset_id.into(), &caller, &delegate); - } verify { - assert_eq!(amount, 100u32.into()); - } - - migration_v2_foreign_asset_set_reserve_weight { - let (id, _, _) = create_default_asset::(true); - let id: >::AssetId = id.into(); - let reserve = T::BenchmarkHelper::create_reserve_id_parameter(42); - }: { - let asset_id = Asset::::iter_keys().next() - .ok_or_else(|| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?; - assert_eq!(id, asset_id); - let bounded_reserves = vec![reserve.clone()].try_into().unwrap(); - Pallet::::unchecked_update_reserves(asset_id, bounded_reserves).unwrap(); - } - verify { - assert_eq!(Reserves::::get(id)[0], reserve); - } - - impl_benchmark_test_suite!(Assets, crate::mock::new_test_ext(), crate::mock::Test) -} diff --git a/pallets/assets/src/extra_mutator.rs b/pallets/assets/src/extra_mutator.rs deleted file mode 100644 index 7de59d54..00000000 --- a/pallets/assets/src/extra_mutator.rs +++ /dev/null @@ -1,102 +0,0 @@ -// 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. - -//! Datatype for easy mutation of the extra "sidecar" data. - -use super::*; - -/// A mutator type allowing inspection and possible modification of the extra "sidecar" data. -/// -/// This may be used as a `Deref` for the pallet's extra data. If mutated (using `DerefMut`), then -/// any uncommitted changes (see `commit` function) will be automatically committed to storage when -/// dropped. Changes, even after committed, may be reverted to their original values with the -/// `revert` function. -pub struct ExtraMutator, I: 'static = ()> { - id: T::AssetId, - who: T::AccountId, - original: T::Extra, - pending: Option, -} - -impl, I: 'static> Drop for ExtraMutator { - fn drop(&mut self) { - // Always commit pending changes, not just in debug builds. - // The commit() call was previously inside debug_assert!, which meant - // release builds would silently discard uncommitted sidecar mutations. - let result = self.commit(); - debug_assert!(result.is_ok(), "attempt to write to non-existent asset account"); - } -} - -impl, I: 'static> core::ops::Deref for ExtraMutator { - type Target = T::Extra; - fn deref(&self) -> &T::Extra { - match self.pending { - Some(ref value) => value, - None => &self.original, - } - } -} - -impl, I: 'static> core::ops::DerefMut for ExtraMutator { - fn deref_mut(&mut self) -> &mut T::Extra { - if self.pending.is_none() { - self.pending = Some(self.original.clone()); - } - self.pending.as_mut().unwrap() - } -} - -impl, I: 'static> ExtraMutator { - pub(super) fn maybe_new( - id: T::AssetId, - who: impl core::borrow::Borrow, - ) -> Option> { - if let Some(a) = Account::::get(&id, who.borrow()) { - Some(ExtraMutator:: { - id, - who: who.borrow().clone(), - original: a.extra, - pending: None, - }) - } else { - None - } - } - - /// Commit any changes to storage. - pub fn commit(&mut self) -> Result<(), ()> { - if let Some(extra) = self.pending.take() { - Account::::try_mutate(&self.id, &self.who, |maybe_account| { - maybe_account.as_mut().ok_or(()).map(|account| account.extra = extra) - }) - } else { - Ok(()) - } - } - - /// Revert any changes, even those already committed by `self` and drop self. - pub fn revert(mut self) -> Result<(), ()> { - self.pending = None; - Account::::try_mutate(&self.id, &self.who, |maybe_account| { - maybe_account - .as_mut() - .ok_or(()) - .map(|account| account.extra = self.original.clone()) - }) - } -} diff --git a/pallets/assets/src/functions.rs b/pallets/assets/src/functions.rs deleted file mode 100644 index 2e6a690b..00000000 --- a/pallets/assets/src/functions.rs +++ /dev/null @@ -1,1167 +0,0 @@ -// 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. - -//! Functions for the Assets pallet. - -use super::*; -use alloc::vec; -use frame_support::{defensive, traits::Get, BoundedVec}; -use sp_runtime::traits::ConstU32; - -#[must_use] -pub(super) enum DeadConsequence { - Remove, - Keep, -} - -use DeadConsequence::*; - -// The main implementation block for the module. -impl, I: 'static> Pallet { - // Public immutables - - /// Return the extra "sid-car" data for `id`/`who`, or `None` if the account doesn't exist. - pub fn adjust_extra( - id: T::AssetId, - who: impl core::borrow::Borrow, - ) -> Option> { - ExtraMutator::maybe_new(id, who) - } - - /// Get the asset `id` balance of `who`, or zero if the asset-account doesn't exist. - pub fn balance(id: T::AssetId, who: impl core::borrow::Borrow) -> T::Balance { - Self::maybe_balance(id, who).unwrap_or_default() - } - - /// Get the asset `id` balance of `who` if the asset-account exists. - pub fn maybe_balance( - id: T::AssetId, - who: impl core::borrow::Borrow, - ) -> Option { - Account::::get(id, who.borrow()).map(|a| a.balance) - } - - /// Get the total supply of an asset `id`. - pub fn total_supply(id: T::AssetId) -> T::Balance { - Self::maybe_total_supply(id).unwrap_or_default() - } - - /// Get the total supply of an asset `id` if the asset exists. - pub fn maybe_total_supply(id: T::AssetId) -> Option { - Asset::::get(id).map(|x| x.supply) - } - - pub(super) fn new_account( - who: &T::AccountId, - d: &mut AssetDetails>, - maybe_deposit: Option<(&T::AccountId, DepositBalanceOf)>, - ) -> Result, DispatchError> { - let accounts = d.accounts.checked_add(1).ok_or(ArithmeticError::Overflow)?; - let reason = if let Some((depositor, deposit)) = maybe_deposit { - if depositor == who { - ExistenceReason::DepositHeld(deposit) - } else { - ExistenceReason::DepositFrom(depositor.clone(), deposit) - } - } else if d.is_sufficient { - frame_system::Pallet::::inc_sufficients(who); - d.sufficients.saturating_inc(); - ExistenceReason::Sufficient - } else { - frame_system::Pallet::::inc_consumers(who) - .map_err(|_| Error::::UnavailableConsumer)?; - // We ensure that we can still increment consumers once more because we could otherwise - // allow accidental usage of all consumer references which could cause grief. - if !frame_system::Pallet::::can_inc_consumer(who) { - frame_system::Pallet::::dec_consumers(who); - return Err(Error::::UnavailableConsumer.into()) - } - ExistenceReason::Consumer - }; - d.accounts = accounts; - Ok(reason) - } - - pub(super) fn ensure_account_can_die(id: T::AssetId, who: &T::AccountId) -> DispatchResult { - ensure!( - T::Holder::balance_on_hold(id.clone(), who).is_none(), - Error::::ContainsHolds - ); - ensure!(T::Freezer::frozen_balance(id, who).is_none(), Error::::ContainsFreezes); - Ok(()) - } - - pub(super) fn dead_account( - who: &T::AccountId, - d: &mut AssetDetails>, - reason: &ExistenceReasonOf, - force: bool, - ) -> DeadConsequence { - use ExistenceReason::*; - - match *reason { - Consumer => frame_system::Pallet::::dec_consumers(who), - Sufficient => { - d.sufficients = d.sufficients.saturating_sub(1); - frame_system::Pallet::::dec_sufficients(who); - }, - DepositRefunded => {}, - DepositHeld(_) | DepositFrom(..) if !force => return Keep, - DepositHeld(_) | DepositFrom(..) => {}, - } - d.accounts = d.accounts.saturating_sub(1); - Remove - } - - /// Returns `true` when the balance of `account` can be increased by `amount`. - /// - /// - `id`: The id of the asset that should be increased. - /// - `who`: The account of which the balance should be increased. - /// - `amount`: The amount by which the balance should be increased. - /// - `increase_supply`: Will the supply of the asset be increased by `amount` at the same time - /// as crediting the `account`. - pub(super) fn can_increase( - id: T::AssetId, - who: &T::AccountId, - amount: T::Balance, - increase_supply: bool, - ) -> DepositConsequence { - let details = match Asset::::get(&id) { - Some(details) => details, - None => return DepositConsequence::UnknownAsset, - }; - if details.status == AssetStatus::Destroying { - return DepositConsequence::UnknownAsset - } - if increase_supply && details.supply.checked_add(&amount).is_none() { - return DepositConsequence::Overflow - } - if let Some(account) = Account::::get(id, who) { - if account.status.is_blocked() { - return DepositConsequence::Blocked - } - if account.balance.checked_add(&amount).is_none() { - return DepositConsequence::Overflow - } - } else { - if amount < details.min_balance { - return DepositConsequence::BelowMinimum - } - if !details.is_sufficient && !frame_system::Pallet::::can_accrue_consumers(who, 2) { - return DepositConsequence::CannotCreate - } - if details.is_sufficient && details.sufficients.checked_add(1).is_none() { - return DepositConsequence::Overflow - } - } - - DepositConsequence::Success - } - - /// Return the consequence of a withdraw. - pub(super) fn can_decrease( - id: T::AssetId, - who: &T::AccountId, - amount: T::Balance, - keep_alive: bool, - ) -> WithdrawConsequence { - use WithdrawConsequence::*; - let details = match Asset::::get(&id) { - Some(details) => details, - None => return UnknownAsset, - }; - if details.supply.checked_sub(&amount).is_none() { - return Underflow - } - if details.status == AssetStatus::Frozen { - return Frozen - } - if details.status == AssetStatus::Destroying { - return UnknownAsset - } - if amount.is_zero() { - return Success - } - let account = match Account::::get(&id, who) { - Some(a) => a, - None => return BalanceLow, - }; - if account.status.is_frozen() { - return Frozen - } - if let Some(rest) = account.balance.checked_sub(&amount) { - match ( - T::Holder::balance_on_hold(id.clone(), who), - T::Freezer::frozen_balance(id.clone(), who), - ) { - (None, None) => - if rest < details.min_balance { - if keep_alive { - WouldDie - } else { - ReducedToZero(rest) - } - } else { - Success - }, - (maybe_held, maybe_frozen) => { - let frozen = maybe_frozen.unwrap_or_default(); - let held = maybe_held.unwrap_or_default(); - - // The `untouchable` balance of the asset account of `who`. This is described - // here: https://paritytech.github.io/polkadot-sdk/master/frame_support/traits/tokens/fungible/index.html#visualising-balance-components-together- - let untouchable = frozen.saturating_sub(held).max(details.min_balance); - if rest < untouchable { - if !frozen.is_zero() { - Frozen - } else { - WouldDie - } - } else { - Success - } - }, - } - } else { - BalanceLow - } - } - - // Maximum `amount` that can be passed into `can_withdraw` to result in a `WithdrawConsequence` - // of `Success`. - pub(super) fn reducible_balance( - id: T::AssetId, - who: &T::AccountId, - keep_alive: bool, - ) -> Result { - let details = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); - - let account = Account::::get(&id, who).ok_or(Error::::NoAccount)?; - ensure!(!account.status.is_frozen(), Error::::Frozen); - - let untouchable = match ( - T::Holder::balance_on_hold(id.clone(), who), - T::Freezer::frozen_balance(id.clone(), who), - keep_alive, - ) { - (None, None, true) => details.min_balance, - (None, None, false) => Zero::zero(), - (maybe_held, maybe_frozen, _) => { - let held = maybe_held.unwrap_or_default(); - let frozen = maybe_frozen.unwrap_or_default(); - frozen.saturating_sub(held).max(details.min_balance) - }, - }; - let amount = account.balance.saturating_sub(untouchable); - - Ok(amount.min(details.supply)) - } - - /// Make preparatory checks for debiting some funds from an account. Flags indicate requirements - /// of the debit. - /// - /// - `amount`: The amount desired to be debited. The actual amount returned for debit may be - /// less (in the case of `best_effort` being `true`) or greater by up to the minimum balance - /// less one. - /// - `keep_alive`: Require that `target` must stay alive. - /// - `respect_freezer`: Respect any freezes on the account or token (or not). - /// - `best_effort`: The debit amount may be less than `amount`. - /// - /// On success, the amount which should be debited (this will always be at least `amount` unless - /// `best_effort` is `true`) together with an optional value indicating the argument which must - /// be passed into the `melted` function of the `T::Freezer` if `Some`. - /// - /// If no valid debit can be made then return an `Err`. - pub(super) fn prep_debit( - id: T::AssetId, - target: &T::AccountId, - amount: T::Balance, - f: DebitFlags, - ) -> Result { - let actual = Self::reducible_balance(id.clone(), target, f.keep_alive)?.min(amount); - ensure!(f.best_effort || actual >= amount, Error::::BalanceLow); - - let conseq = Self::can_decrease(id, target, actual, f.keep_alive); - let actual = match conseq.into_result(f.keep_alive) { - Ok(dust) => actual.saturating_add(dust), //< guaranteed by reducible_balance - Err(e) => { - debug_assert!(false, "passed from reducible_balance; qed"); - return Err(e) - }, - }; - - Ok(actual) - } - - /// Make preparatory checks for crediting some funds from an account. Flags indicate - /// requirements of the credit. - /// - /// - `amount`: The amount desired to be credited. - /// - `debit`: The amount by which some other account has been debited. If this is greater than - /// `amount`, then the `burn_dust` parameter takes effect. - /// - `burn_dust`: Indicates that in the case of debit being greater than amount, the additional - /// (dust) value should be burned, rather than credited. - /// - /// On success, the amount which should be credited (this will always be at least `amount`) - /// together with an optional value indicating the value which should be burned. The latter - /// will always be `None` as long as `burn_dust` is `false` or `debit` is no greater than - /// `amount`. - /// - /// If no valid credit can be made then return an `Err`. - pub(super) fn prep_credit( - id: T::AssetId, - dest: &T::AccountId, - amount: T::Balance, - debit: T::Balance, - burn_dust: bool, - ) -> Result<(T::Balance, Option), DispatchError> { - let (credit, maybe_burn) = match (burn_dust, debit.checked_sub(&amount)) { - (true, Some(dust)) => (amount, Some(dust)), - _ => (debit, None), - }; - Self::can_increase(id, dest, credit, false).into_result()?; - Ok((credit, maybe_burn)) - } - - /// Creates an account for `who` to hold asset `id` with a zero balance and takes a deposit. - pub(super) fn do_touch( - id: T::AssetId, - who: T::AccountId, - depositor: T::AccountId, - ) -> DispatchResult { - ensure!(!Account::::contains_key(&id, &who), Error::::AlreadyExists); - let deposit = T::AssetAccountDeposit::get(); - let mut details = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); - let reason = Self::new_account(&who, &mut details, Some((&depositor, deposit)))?; - T::Currency::reserve(&depositor, deposit)?; - Asset::::insert(&id, details); - Account::::insert( - &id, - &who, - AssetAccountOf:: { - balance: Zero::zero(), - status: AccountStatus::Liquid, - reason, - extra: T::Extra::default(), - }, - ); - Self::deposit_event(Event::Touched { asset_id: id, who, depositor }); - Ok(()) - } - - /// Returns a deposit or a consumer reference, destroying an asset-account. - /// Non-zero balance accounts refunded and destroyed only if `allow_burn` is true. - pub(super) fn do_refund(id: T::AssetId, who: T::AccountId, allow_burn: bool) -> DispatchResult { - use AssetStatus::*; - use ExistenceReason::*; - - let mut account = Account::::get(&id, &who).ok_or(Error::::NoDeposit)?; - ensure!(matches!(account.reason, Consumer | DepositHeld(..)), Error::::NoDeposit); - let mut details = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!(matches!(details.status, Live | Frozen), Error::::IncorrectStatus); - ensure!(account.balance.is_zero() || allow_burn, Error::::WouldBurn); - Self::ensure_account_can_die(id.clone(), &who)?; - - if let Some(deposit) = account.reason.take_deposit() { - T::Currency::unreserve(&who, deposit); - } - - // If allow_burn is true and account has a non-zero balance, we must decrement - // the total supply to maintain accounting invariants. Otherwise total_supply - // would report phantom issuance that no longer corresponds to any live balances. - let burned = account.balance; - if !burned.is_zero() { - debug_assert!(details.supply >= burned, "account balance exceeds total supply"); - details.supply = details.supply.saturating_sub(burned); - } - - if let Remove = Self::dead_account(&who, &mut details, &account.reason, false) { - Account::::remove(&id, &who); - } else { - debug_assert!(false, "refund did not result in dead account?!"); - // deposit may have been refunded, need to update `Account` - Account::::insert(id, &who, account); - return Ok(()) - } - - Asset::::insert(&id, details); - - // Emit Burned event if we burned a non-zero balance - if !burned.is_zero() { - Self::deposit_event(Event::Burned { - asset_id: id.clone(), - owner: who.clone(), - balance: burned, - }); - } - - // Executing a hook here is safe, since it is not in a `mutate`. - T::Freezer::died(id.clone(), &who); - T::Holder::died(id, &who); - Ok(()) - } - - /// 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, - maybe_check_caller: Option, - ) -> DispatchResult { - let mut account = Account::::get(&id, &who).ok_or(Error::::NoDeposit)?; - let (depositor, deposit) = - account.reason.take_deposit_from().ok_or(Error::::NoDeposit)?; - let mut details = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); - ensure!(!account.status.is_frozen(), Error::::Frozen); - if let Some(caller) = maybe_check_caller { - ensure!(caller == depositor || caller == details.admin, Error::::NoPermission); - } - ensure!(account.balance.is_zero(), Error::::WouldBurn); - Self::ensure_account_can_die(id.clone(), who)?; - - T::Currency::unreserve(&depositor, deposit); - - if let Remove = Self::dead_account(&who, &mut details, &account.reason, false) { - Account::::remove(&id, &who); - } else { - debug_assert!(false, "refund did not result in dead account?!"); - // deposit may have been refunded, need to update `Account` - Account::::insert(&id, &who, account); - return Ok(()) - } - Asset::::insert(&id, details); - // Executing a hook here is safe, since it is not in a `mutate`. - T::Freezer::died(id.clone(), who); - T::Holder::died(id, &who); - return Ok(()) - } - - /// Increases the asset `id` balance of `beneficiary` by `amount`. - /// - /// This alters the registered supply of the asset and emits an event. - /// - /// Will return an error or will increase the amount by exactly `amount`. - pub(super) fn do_mint( - id: T::AssetId, - beneficiary: &T::AccountId, - amount: T::Balance, - maybe_check_issuer: Option, - ) -> DispatchResult { - // Early return for zero amounts - don't emit events or bypass permission checks. - // Without this, zero-amount mints would skip the issuer check in increase_balance's - // callback (which returns early for zero) but still emit the Issued event. - if amount.is_zero() { - return Ok(()) - } - - Self::increase_balance(id.clone(), beneficiary, amount, |details| -> DispatchResult { - if let Some(check_issuer) = maybe_check_issuer { - ensure!(check_issuer == details.issuer, Error::::NoPermission); - } - debug_assert!(details.supply.checked_add(&amount).is_some(), "checked in prep; qed"); - - details.supply = details.supply.saturating_add(amount); - - Ok(()) - })?; - - Self::deposit_event(Event::Issued { asset_id: id, owner: beneficiary.clone(), amount }); - - Ok(()) - } - - /// Increases the asset `id` balance of `beneficiary` by `amount`. - /// - /// LOW-LEVEL: Does not alter the supply of asset or emit an event. Use `do_mint` if you need - /// that. This is not intended to be used alone. - /// - /// Will return an error or will increase the amount by exactly `amount`. - pub(super) fn increase_balance( - id: T::AssetId, - beneficiary: &T::AccountId, - amount: T::Balance, - check: impl FnOnce( - &mut AssetDetails>, - ) -> DispatchResult, - ) -> DispatchResult { - if amount.is_zero() { - return Ok(()) - } - - Self::can_increase(id.clone(), beneficiary, amount, true).into_result()?; - Asset::::try_mutate(&id, |maybe_details| -> DispatchResult { - let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; - ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); - check(details)?; - - Account::::try_mutate(&id, beneficiary, |maybe_account| -> DispatchResult { - match maybe_account { - Some(ref mut account) => { - account.balance.saturating_accrue(amount); - }, - maybe_account @ None => { - // Note this should never fail as it's already checked by - // `can_increase`. - ensure!(amount >= details.min_balance, TokenError::BelowMinimum); - *maybe_account = Some(AssetAccountOf:: { - balance: amount, - reason: Self::new_account(beneficiary, details, None)?, - status: AccountStatus::Liquid, - extra: T::Extra::default(), - }); - }, - } - Ok(()) - })?; - Ok(()) - })?; - Ok(()) - } - - /// Reduces asset `id` balance of `target` by `amount`. Flags `f` can be given to alter whether - /// it attempts a `best_effort` or makes sure to `keep_alive` the account. - /// - /// This alters the registered supply of the asset and emits an event. - /// - /// Will return an error and do nothing or will decrease the amount and return the amount - /// reduced by. - pub(super) fn do_burn( - id: T::AssetId, - target: &T::AccountId, - amount: T::Balance, - maybe_check_admin: Option, - f: DebitFlags, - ) -> Result { - // Early return for zero amounts - don't emit events or bypass permission checks. - // Without this, zero-amount burns would skip the admin check in decrease_balance's - // callback (which returns early for zero) but still emit the Burned event. - if amount.is_zero() { - return Ok(amount) - } - - let d = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!( - d.status == AssetStatus::Live || d.status == AssetStatus::Frozen, - Error::::IncorrectStatus - ); - - let actual = Self::decrease_balance(id.clone(), target, amount, f, |actual, details| { - // Check admin rights. - if let Some(check_admin) = maybe_check_admin { - ensure!(check_admin == details.admin, Error::::NoPermission); - } - - debug_assert!(details.supply >= actual, "checked in prep; qed"); - details.supply = details.supply.saturating_sub(actual); - - Ok(()) - })?; - Self::deposit_event(Event::Burned { asset_id: id, owner: target.clone(), balance: actual }); - Ok(actual) - } - - /// Reduces asset `id` balance of `target` by `amount`. Flags `f` can be given to alter whether - /// it attempts a `best_effort` or makes sure to `keep_alive` the account. - /// - /// LOW-LEVEL: Does not alter the supply of asset or emit an event. Use `do_burn` if you need - /// that. This is not intended to be used alone. - /// - /// Will return an error and do nothing or will decrease the amount and return the amount - /// reduced by. - pub(super) fn decrease_balance( - id: T::AssetId, - target: &T::AccountId, - amount: T::Balance, - f: DebitFlags, - check: impl FnOnce( - T::Balance, - &mut AssetDetails>, - ) -> DispatchResult, - ) -> Result { - if amount.is_zero() { - return Ok(amount) - } - - let details = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); - - let actual = Self::prep_debit(id.clone(), target, amount, f)?; - let mut target_died: Option = None; - - Asset::::try_mutate(&id, |maybe_details| -> DispatchResult { - let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; - check(actual, details)?; - - Account::::try_mutate(&id, target, |maybe_account| -> DispatchResult { - let mut account = maybe_account.take().ok_or(Error::::NoAccount)?; - debug_assert!(account.balance >= actual, "checked in prep; qed"); - - // Make the debit. - account.balance = account.balance.saturating_sub(actual); - if account.balance < details.min_balance { - debug_assert!(account.balance.is_zero(), "checked in prep; qed"); - Self::ensure_account_can_die(id.clone(), target)?; - target_died = Some(Self::dead_account(target, details, &account.reason, false)); - if let Some(Remove) = target_died { - return Ok(()) - } - }; - *maybe_account = Some(account); - Ok(()) - })?; - - Ok(()) - })?; - - // Execute hook outside of `mutate`. - if let Some(Remove) = target_died { - T::Freezer::died(id.clone(), target); - T::Holder::died(id, target); - } - Ok(actual) - } - - /// Reduces the asset `id` balance of `source` by some `amount` and increases the balance of - /// `dest` by (similar) amount. - /// - /// Returns the actual amount placed into `dest`. Exact semantics are determined by the flags - /// `f`. - /// - /// Will fail if the amount transferred is so small that it cannot create the destination due - /// to minimum balance requirements. - pub fn do_transfer( - id: T::AssetId, - source: &T::AccountId, - dest: &T::AccountId, - amount: T::Balance, - maybe_need_admin: Option, - f: TransferFlags, - ) -> Result { - let (balance, died) = - Self::transfer_and_die(id.clone(), source, dest, amount, maybe_need_admin, f)?; - if let Some(Remove) = died { - T::Freezer::died(id.clone(), source); - T::Holder::died(id, source); - } - Ok(balance) - } - - /// Same as `do_transfer` but it does not execute the `FrozenBalance::died` hook and - /// instead returns whether and how the `source` account died in this operation. - fn transfer_and_die( - id: T::AssetId, - source: &T::AccountId, - dest: &T::AccountId, - amount: T::Balance, - maybe_need_admin: Option, - f: TransferFlags, - ) -> Result<(T::Balance, Option), DispatchError> { - // Early exit if no-op. - if amount.is_zero() { - return Ok((amount, None)) - } - let details = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); - - // Figure out the debit and credit, together with side-effects. - let debit = Self::prep_debit(id.clone(), source, amount, f.into())?; - let (credit, maybe_burn) = Self::prep_credit(id.clone(), dest, amount, debit, f.burn_dust)?; - - let mut source_account = - Account::::get(&id, &source).ok_or(Error::::NoAccount)?; - let mut source_died: Option = None; - - Asset::::try_mutate(&id, |maybe_details| -> DispatchResult { - let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; - - // Check admin rights. - if let Some(need_admin) = maybe_need_admin { - ensure!(need_admin == details.admin, Error::::NoPermission); - } - - // Skip if source == dest - if source == dest { - return Ok(()) - } - - // Burn any dust if needed. - if let Some(burn) = maybe_burn { - // Debit dust from supply; this will not saturate since it's already checked in - // prep. - debug_assert!(details.supply >= burn, "checked in prep; qed"); - details.supply = details.supply.saturating_sub(burn); - } - - // Debit balance from source; this will not saturate since it's already checked in prep. - debug_assert!(source_account.balance >= debit, "checked in prep; qed"); - source_account.balance = source_account.balance.saturating_sub(debit); - - // Pre-check that an account can die if is below min balance - if source_account.balance < details.min_balance { - debug_assert!(source_account.balance.is_zero(), "checked in prep; qed"); - Self::ensure_account_can_die(id.clone(), source)?; - } - - Account::::try_mutate(&id, &dest, |maybe_account| -> DispatchResult { - match maybe_account { - Some(ref mut account) => { - // Calculate new balance; this will not saturate since it's already - // checked in prep. - debug_assert!( - account.balance.checked_add(&credit).is_some(), - "checked in prep; qed" - ); - account.balance.saturating_accrue(credit); - }, - maybe_account @ None => { - *maybe_account = Some(AssetAccountOf:: { - balance: credit, - status: AccountStatus::Liquid, - reason: Self::new_account(dest, details, None)?, - extra: T::Extra::default(), - }); - }, - } - Ok(()) - })?; - - // Remove source account if it's now dead. - if source_account.balance < details.min_balance { - debug_assert!(source_account.balance.is_zero(), "checked in prep; qed"); - source_died = - Some(Self::dead_account(source, details, &source_account.reason, false)); - if let Some(Remove) = source_died { - Account::::remove(&id, &source); - return Ok(()) - } - } - Account::::insert(&id, &source, &source_account); - Ok(()) - })?; - - Self::deposit_event(Event::Transferred { - asset_id: id, - from: source.clone(), - to: dest.clone(), - amount: credit, - }); - Ok((credit, source_died)) - } - - /// Create a new asset without taking a deposit. - /// - /// * `id`: The `AssetId` you want the new asset to have. Must not already be in use. - /// * `owner`: The owner, issuer, admin, and freezer of this asset upon creation. - /// * `is_sufficient`: Whether this asset needs users to have an existential deposit to hold - /// this asset. - /// * `min_balance`: The minimum balance a user is allowed to have of this asset before they are - /// considered dust and cleaned up. - pub(super) fn do_force_create( - id: T::AssetId, - owner: T::AccountId, - is_sufficient: bool, - min_balance: T::Balance, - ) -> DispatchResult { - ensure!(!Asset::::contains_key(&id), Error::::InUse); - ensure!(!min_balance.is_zero(), Error::::MinBalanceZero); - if let Some(next_id) = NextAssetId::::get() { - ensure!(id == next_id, Error::::BadAssetId); - } - - Asset::::insert( - &id, - AssetDetails { - owner: owner.clone(), - issuer: owner.clone(), - admin: owner.clone(), - freezer: owner.clone(), - supply: Zero::zero(), - deposit: Zero::zero(), - min_balance, - is_sufficient, - accounts: 0, - sufficients: 0, - approvals: 0, - status: AssetStatus::Live, - }, - ); - ensure!(T::CallbackHandle::created(&id, &owner).is_ok(), Error::::CallbackFailed); - Self::deposit_event(Event::ForceCreated { asset_id: id, owner: owner.clone() }); - Ok(()) - } - - /// Start the process of destroying an asset, by setting the asset status to `Destroying`, and - /// emitting the `DestructionStarted` event. - pub(super) fn do_start_destroy( - id: T::AssetId, - maybe_check_owner: Option, - ) -> DispatchResult { - Asset::::try_mutate_exists(id.clone(), |maybe_details| -> Result<(), DispatchError> { - let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; - if let Some(check_owner) = maybe_check_owner { - ensure!(details.owner == check_owner, Error::::NoPermission); - } - - ensure!(!T::Holder::contains_holds(id.clone()), Error::::ContainsHolds); - ensure!(!T::Freezer::contains_freezes(id.clone()), Error::::ContainsFreezes); - - details.status = AssetStatus::Destroying; - - Self::deposit_event(Event::DestructionStarted { asset_id: id }); - Ok(()) - }) - } - - /// Destroy accounts associated with a given asset up to the max (T::RemoveItemsLimit). - /// - /// Each call emits the `Event::DestroyedAccounts` event. - /// Returns the number of destroyed accounts. - pub(super) fn do_destroy_accounts( - id: T::AssetId, - max_items: u32, - ) -> Result { - let mut dead_accounts: Vec = vec![]; - let mut remaining_accounts = 0; - Asset::::try_mutate_exists(&id, |maybe_details| -> Result<(), DispatchError> { - let mut details = maybe_details.as_mut().ok_or(Error::::Unknown)?; - // Should only destroy accounts while the asset is in a destroying state - ensure!(details.status == AssetStatus::Destroying, Error::::IncorrectStatus); - - for (i, (who, mut v)) in Account::::iter_prefix(&id).enumerate() { - if Self::ensure_account_can_die(id.clone(), &who).is_err() { - continue - } - // unreserve the existence deposit if any - if let Some((depositor, deposit)) = v.reason.take_deposit_from() { - T::Currency::unreserve(&depositor, deposit); - } else if let Some(deposit) = v.reason.take_deposit() { - T::Currency::unreserve(&who, deposit); - } - if let Remove = Self::dead_account(&who, &mut details, &v.reason, false) { - Account::::remove(&id, &who); - dead_accounts.push(who); - } else { - // deposit may have been released, need to update `Account` - Account::::insert(&id, &who, v); - defensive!("destroy did not result in dead account?!"); - } - if i + 1 >= (max_items as usize) { - break - } - } - remaining_accounts = details.accounts; - Ok(()) - })?; - - for who in &dead_accounts { - T::Freezer::died(id.clone(), &who); - T::Holder::died(id.clone(), &who); - } - - Self::deposit_event(Event::AccountsDestroyed { - asset_id: id, - accounts_destroyed: dead_accounts.len() as u32, - accounts_remaining: remaining_accounts as u32, - }); - Ok(dead_accounts.len() as u32) - } - - /// Destroy approvals associated with a given asset up to the max (T::RemoveItemsLimit). - /// - /// Each call emits the `Event::DestroyedApprovals` event - /// Returns the number of destroyed approvals. - pub(super) fn do_destroy_approvals( - id: T::AssetId, - max_items: u32, - ) -> Result { - let mut removed_approvals = 0; - Asset::::try_mutate_exists( - id.clone(), - |maybe_details| -> Result<(), DispatchError> { - let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; - - // Should only destroy accounts while the asset is in a destroying state. - ensure!(details.status == AssetStatus::Destroying, Error::::IncorrectStatus); - - for ((owner, _), approval) in Approvals::::drain_prefix((id.clone(),)) { - T::Currency::unreserve(&owner, approval.deposit); - removed_approvals = removed_approvals.saturating_add(1); - details.approvals = details.approvals.saturating_sub(1); - if removed_approvals >= max_items { - break - } - } - Self::deposit_event(Event::ApprovalsDestroyed { - asset_id: id, - approvals_destroyed: removed_approvals as u32, - approvals_remaining: details.approvals as u32, - }); - Ok(()) - }, - )?; - Ok(removed_approvals) - } - - /// Complete destroying an asset and unreserve the deposit. - /// - /// On success, the `Event::Destroyed` event is emitted. - pub(super) fn do_finish_destroy(id: T::AssetId) -> DispatchResult { - Asset::::try_mutate_exists(id.clone(), |maybe_details| -> Result<(), DispatchError> { - let details = maybe_details.take().ok_or(Error::::Unknown)?; - ensure!(details.status == AssetStatus::Destroying, Error::::IncorrectStatus); - ensure!(details.accounts == 0, Error::::InUse); - ensure!(details.approvals == 0, Error::::InUse); - ensure!(T::CallbackHandle::destroyed(&id).is_ok(), Error::::CallbackFailed); - - let metadata = Metadata::::take(&id); - T::Currency::unreserve( - &details.owner, - details.deposit.saturating_add(metadata.deposit), - ); - Self::deposit_event(Event::Destroyed { asset_id: id }); - - Ok(()) - }) - } - - /// Creates an approval from `owner` to spend `amount` of asset `id` tokens by 'delegate' - /// while reserving `T::ApprovalDeposit` from owner - /// - /// If an approval already exists, the new amount is added to such existing approval - pub fn do_approve_transfer( - id: T::AssetId, - owner: &T::AccountId, - delegate: &T::AccountId, - amount: T::Balance, - ) -> DispatchResult { - let mut d = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!(d.status == AssetStatus::Live, Error::::AssetNotLive); - Approvals::::try_mutate( - (id.clone(), &owner, &delegate), - |maybe_approved| -> DispatchResult { - let mut approved = match maybe_approved.take() { - // an approval already exists and is being updated - Some(a) => a, - // a new approval is created - None => { - d.approvals.saturating_inc(); - Default::default() - }, - }; - let deposit_required = T::ApprovalDeposit::get(); - if approved.deposit < deposit_required { - T::Currency::reserve(owner, deposit_required - approved.deposit)?; - approved.deposit = deposit_required; - } - approved.amount = approved.amount.saturating_add(amount); - *maybe_approved = Some(approved); - Ok(()) - }, - )?; - Asset::::insert(&id, d); - Self::deposit_event(Event::ApprovedTransfer { - asset_id: id, - source: owner.clone(), - delegate: delegate.clone(), - amount, - }); - - Ok(()) - } - - /// Reduces the asset `id` balance of `owner` by some `amount` and increases the balance of - /// `dest` by (similar) amount, checking that 'delegate' has an existing approval from `owner` - /// to spend`amount`. - /// - /// Will fail if `amount` is greater than the approval from `owner` to 'delegate' - /// Will unreserve the deposit from `owner` if the entire approved `amount` is spent by - /// 'delegate' - pub fn do_transfer_approved( - id: T::AssetId, - owner: &T::AccountId, - delegate: &T::AccountId, - destination: &T::AccountId, - amount: T::Balance, - ) -> DispatchResult { - // Early return for zero amounts - don't emit events or consume approvals. - // Without this, zero-amount transfers would emit TransferredApproved even though - // transfer_and_die returns early without moving tokens or emitting Transferred. - if amount.is_zero() { - return Ok(()) - } - - let mut owner_died: Option = None; - - let d = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!(d.status == AssetStatus::Live, Error::::AssetNotLive); - - Approvals::::try_mutate_exists( - (id.clone(), &owner, delegate), - |maybe_approved| -> DispatchResult { - let mut approved = maybe_approved.take().ok_or(Error::::Unapproved)?; - let remaining = - approved.amount.checked_sub(&amount).ok_or(Error::::Unapproved)?; - - let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: true }; - owner_died = - Self::transfer_and_die(id.clone(), owner, destination, amount, None, f)?.1; - - if remaining.is_zero() { - T::Currency::unreserve(owner, approved.deposit); - Asset::::mutate(id.clone(), |maybe_details| { - if let Some(details) = maybe_details { - details.approvals.saturating_dec(); - } - }); - } else { - approved.amount = remaining; - *maybe_approved = Some(approved); - } - Ok(()) - }, - )?; - - // Execute hook outside of `mutate`. - if let Some(Remove) = owner_died { - T::Freezer::died(id.clone(), owner); - T::Holder::died(id.clone(), owner); - } - - // Emit TransferredApproved to identify the delegate responsible for the spend. - // Note: transfer_and_die already emits Event::Transferred, but that event doesn't - // include the delegate. This event allows indexers/monitoring to track delegated activity. - Self::deposit_event(Event::TransferredApproved { - asset_id: id, - owner: owner.clone(), - delegate: delegate.clone(), - destination: destination.clone(), - amount, - }); - - Ok(()) - } - - /// Do set metadata - pub(super) fn do_set_metadata( - id: T::AssetId, - from: &T::AccountId, - name: Vec, - symbol: Vec, - decimals: u8, - ) -> DispatchResult { - let bounded_name: BoundedVec = - name.clone().try_into().map_err(|_| Error::::BadMetadata)?; - let bounded_symbol: BoundedVec = - symbol.clone().try_into().map_err(|_| Error::::BadMetadata)?; - - let d = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!(d.status == AssetStatus::Live, Error::::AssetNotLive); - ensure!(from == &d.owner, Error::::NoPermission); - - Metadata::::try_mutate_exists(id.clone(), |metadata| { - ensure!(metadata.as_ref().map_or(true, |m| !m.is_frozen), Error::::NoPermission); - - let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); - let new_deposit = Self::calc_metadata_deposit(&name, &symbol); - - if new_deposit > old_deposit { - T::Currency::reserve(from, new_deposit - old_deposit)?; - } else { - T::Currency::unreserve(from, old_deposit - new_deposit); - } - - *metadata = Some(AssetMetadata { - deposit: new_deposit, - name: bounded_name, - symbol: bounded_symbol, - decimals, - is_frozen: false, - }); - - Self::deposit_event(Event::MetadataSet { - asset_id: id, - name, - symbol, - decimals, - is_frozen: false, - }); - Ok(()) - }) - } - - /// Calculate the metadata deposit for the provided data. - pub(super) fn calc_metadata_deposit(name: &[u8], symbol: &[u8]) -> DepositBalanceOf { - T::MetadataDepositPerByte::get() - .saturating_mul(((name.len() + symbol.len()) as u32).into()) - .saturating_add(T::MetadataDepositBase::get()) - } - - /// Returns all the non-zero balances for all assets of the given `account`. - pub fn account_balances(account: T::AccountId) -> Vec<(T::AssetId, T::Balance)> { - Asset::::iter_keys() - .filter_map(|id| { - Self::maybe_balance(id.clone(), account.clone()).map(|balance| (id, balance)) - }) - .collect::>() - } - - /// 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::::get(&id).ok_or(Error::::Unknown)?; - d.owner = owner; - d.admin = admin; - d.issuer = issuer; - d.freezer = freezer; - Asset::::insert(&id, d); - Ok(()) - } - - /// Helper function for setting reserves to be used in benchmarking and migrations. - /// Does not check validity of asset id, caller should check it. - pub fn unchecked_update_reserves( - id: T::AssetId, - reserves: BoundedVec>, - ) -> Result<(), Error> { - if reserves.is_empty() { - Reserves::::remove(&id); - Self::deposit_event(Event::ReservesRemoved { asset_id: id }); - } else { - let reserves_vec = reserves.clone().into_inner(); - Reserves::::set(&id, reserves); - Self::deposit_event(Event::ReservesUpdated { asset_id: id, reserves: reserves_vec }); - } - Ok(()) - } -} diff --git a/pallets/assets/src/impl_fungibles.rs b/pallets/assets/src/impl_fungibles.rs deleted file mode 100644 index 6ab7e941..00000000 --- a/pallets/assets/src/impl_fungibles.rs +++ /dev/null @@ -1,360 +0,0 @@ -// 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. - -//! Implementations for fungibles trait. - -use alloc::vec::Vec; -use frame_support::{ - defensive, - traits::tokens::{ - Fortitude, - Precision::{self, BestEffort}, - Preservation::{self, Expendable}, - Provenance::{self, Minted}, - }, -}; - -use super::*; - -impl, I: 'static> fungibles::Inspect<::AccountId> for Pallet { - type AssetId = T::AssetId; - type Balance = T::Balance; - - fn total_issuance(asset: Self::AssetId) -> Self::Balance { - Asset::::get(asset).map(|x| x.supply).unwrap_or_else(Zero::zero) - } - - fn minimum_balance(asset: Self::AssetId) -> Self::Balance { - Asset::::get(asset).map(|x| x.min_balance).unwrap_or_else(Zero::zero) - } - - fn balance(asset: Self::AssetId, who: &::AccountId) -> Self::Balance { - Pallet::::balance(asset, who) - } - - fn total_balance(asset: Self::AssetId, who: &::AccountId) -> Self::Balance { - Pallet::::balance(asset.clone(), who) - .saturating_add(T::Holder::balance_on_hold(asset, who).unwrap_or_default()) - } - - fn reducible_balance( - asset: Self::AssetId, - who: &::AccountId, - preservation: Preservation, - _: Fortitude, - ) -> Self::Balance { - Pallet::::reducible_balance(asset, who, !matches!(preservation, Expendable)) - .unwrap_or(Zero::zero()) - } - - fn can_deposit( - asset: Self::AssetId, - who: &::AccountId, - amount: Self::Balance, - provenance: Provenance, - ) -> DepositConsequence { - Pallet::::can_increase(asset, who, amount, provenance == Minted) - } - - fn can_withdraw( - asset: Self::AssetId, - who: &::AccountId, - amount: Self::Balance, - ) -> WithdrawConsequence { - Pallet::::can_decrease(asset, who, amount, false) - } - - fn asset_exists(asset: Self::AssetId) -> bool { - Asset::::contains_key(asset) - } -} - -impl, I: 'static> fungibles::Mutate<::AccountId> for Pallet { - fn done_mint_into( - asset_id: Self::AssetId, - beneficiary: &::AccountId, - amount: Self::Balance, - ) { - Self::deposit_event(Event::Issued { asset_id, owner: beneficiary.clone(), amount }) - } - - fn done_burn_from( - asset_id: Self::AssetId, - target: &::AccountId, - balance: Self::Balance, - ) { - Self::deposit_event(Event::Burned { asset_id, owner: target.clone(), balance }); - } - - fn done_transfer( - asset_id: Self::AssetId, - source: &::AccountId, - dest: &::AccountId, - amount: Self::Balance, - ) { - Self::deposit_event(Event::Transferred { - asset_id, - from: source.clone(), - to: dest.clone(), - amount, - }); - } -} - -impl, I: 'static> fungibles::Balanced<::AccountId> - for Pallet -{ - type OnDropCredit = fungibles::DecreaseIssuance; - type OnDropDebt = fungibles::IncreaseIssuance; - - fn done_deposit( - asset_id: Self::AssetId, - who: &::AccountId, - amount: Self::Balance, - ) { - Self::deposit_event(Event::Deposited { asset_id, who: who.clone(), amount }) - } - - fn done_withdraw( - asset_id: Self::AssetId, - who: &::AccountId, - amount: Self::Balance, - ) { - Self::deposit_event(Event::Withdrawn { asset_id, who: who.clone(), amount }) - } -} - -impl, I: 'static> fungibles::Unbalanced for Pallet { - fn handle_raw_dust(_: Self::AssetId, _: Self::Balance) {} - fn handle_dust(_: fungibles::Dust) { - defensive!("`decrease_balance` and `increase_balance` have non-default impls; nothing else calls this; qed"); - } - fn write_balance( - _: Self::AssetId, - _: &T::AccountId, - _: Self::Balance, - ) -> Result, DispatchError> { - defensive!("write_balance is not used if other functions are impl'd"); - Err(DispatchError::Unavailable) - } - fn set_total_issuance(id: T::AssetId, amount: Self::Balance) { - Asset::::mutate_exists(id, |maybe_asset| { - if let Some(ref mut asset) = maybe_asset { - asset.supply = amount - } - }); - } - fn decrease_balance( - asset: T::AssetId, - who: &T::AccountId, - amount: Self::Balance, - precision: Precision, - preservation: Preservation, - _: Fortitude, - ) -> Result { - let f = DebitFlags { - keep_alive: preservation != Expendable, - best_effort: precision == BestEffort, - }; - Self::decrease_balance(asset, who, amount, f, |_, _| Ok(())) - } - fn increase_balance( - asset: T::AssetId, - who: &T::AccountId, - amount: Self::Balance, - _: Precision, - ) -> Result { - Self::increase_balance(asset, who, amount, |_| Ok(()))?; - Ok(amount) - } - - // TODO: #13196 implement deactivate/reactivate once we have inactive balance tracking. -} - -impl, I: 'static> fungibles::Create for Pallet { - fn create( - id: T::AssetId, - admin: T::AccountId, - is_sufficient: bool, - min_balance: Self::Balance, - ) -> DispatchResult { - Self::do_force_create(id, admin, is_sufficient, min_balance) - } -} - -impl, I: 'static> fungibles::Destroy for Pallet { - fn start_destroy(id: T::AssetId, maybe_check_owner: Option) -> DispatchResult { - Self::do_start_destroy(id, maybe_check_owner) - } - - fn destroy_accounts(id: T::AssetId, max_items: u32) -> Result { - Self::do_destroy_accounts(id, max_items) - } - - fn destroy_approvals(id: T::AssetId, max_items: u32) -> Result { - Self::do_destroy_approvals(id, max_items) - } - - fn finish_destroy(id: T::AssetId) -> DispatchResult { - Self::do_finish_destroy(id) - } -} - -impl, I: 'static> fungibles::metadata::Inspect<::AccountId> - for Pallet -{ - fn name(asset: T::AssetId) -> Vec { - Metadata::::get(asset).name.to_vec() - } - - fn symbol(asset: T::AssetId) -> Vec { - Metadata::::get(asset).symbol.to_vec() - } - - fn decimals(asset: T::AssetId) -> u8 { - Metadata::::get(asset).decimals - } -} - -impl, I: 'static> fungibles::metadata::Mutate<::AccountId> - for Pallet -{ - fn set( - asset: T::AssetId, - from: &::AccountId, - name: Vec, - symbol: Vec, - decimals: u8, - ) -> DispatchResult { - Self::do_set_metadata(asset, from, name, symbol, decimals) - } -} - -impl, I: 'static> - fungibles::metadata::MetadataDeposit< - ::AccountId>>::Balance, - > for Pallet -{ - fn calc_metadata_deposit( - name: &[u8], - symbol: &[u8], - ) -> ::AccountId>>::Balance { - Self::calc_metadata_deposit(&name, &symbol) - } -} - -impl, I: 'static> fungibles::approvals::Inspect<::AccountId> - for Pallet -{ - // Check the amount approved to be spent by an owner to a delegate - fn allowance( - asset: T::AssetId, - owner: &::AccountId, - delegate: &::AccountId, - ) -> T::Balance { - Approvals::::get((asset, &owner, &delegate)) - .map(|x| x.amount) - .unwrap_or_else(Zero::zero) - } -} - -impl, I: 'static> fungibles::approvals::Mutate<::AccountId> - for Pallet -{ - // Approve spending tokens from a given account - fn approve( - asset: T::AssetId, - owner: &::AccountId, - delegate: &::AccountId, - amount: T::Balance, - ) -> DispatchResult { - Self::do_approve_transfer(asset, owner, delegate, amount) - } - - fn transfer_from( - asset: T::AssetId, - owner: &::AccountId, - delegate: &::AccountId, - dest: &::AccountId, - amount: T::Balance, - ) -> DispatchResult { - Self::do_transfer_approved(asset, owner, delegate, dest, amount) - } -} - -impl, I: 'static> fungibles::roles::Inspect<::AccountId> - for Pallet -{ - fn owner(asset: T::AssetId) -> Option<::AccountId> { - Asset::::get(asset).map(|x| x.owner) - } - - fn issuer(asset: T::AssetId) -> Option<::AccountId> { - Asset::::get(asset).map(|x| x.issuer) - } - - fn admin(asset: T::AssetId) -> Option<::AccountId> { - Asset::::get(asset).map(|x| x.admin) - } - - fn freezer(asset: T::AssetId) -> Option<::AccountId> { - Asset::::get(asset).map(|x| x.freezer) - } -} - -impl, I: 'static> fungibles::InspectEnumerable for Pallet { - type AssetsIterator = KeyPrefixIterator<>::AssetId>; - - /// Returns an iterator of the assets in existence. - /// - /// NOTE: iterating this list invokes a storage read per item. - fn asset_ids() -> Self::AssetsIterator { - Asset::::iter_keys() - } -} - -impl, I: 'static> fungibles::roles::ResetTeam for Pallet { - 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, I: 'static> fungibles::Refund for Pallet { - type AssetId = T::AssetId; - type Balance = DepositBalanceOf; - fn deposit_held(id: Self::AssetId, who: T::AccountId) -> Option<(T::AccountId, Self::Balance)> { - use ExistenceReason::*; - match Account::::get(&id, &who).ok_or(Error::::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::::NoDeposit.into()), - } - } -} diff --git a/pallets/assets/src/impl_stored_map.rs b/pallets/assets/src/impl_stored_map.rs deleted file mode 100644 index a7a5a085..00000000 --- a/pallets/assets/src/impl_stored_map.rs +++ /dev/null @@ -1,54 +0,0 @@ -// 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. - -//! Assets pallet's `StoredMap` implementation. - -use super::*; - -impl, I: 'static> StoredMap<(T::AssetId, T::AccountId), T::Extra> for Pallet { - fn get(id_who: &(T::AssetId, T::AccountId)) -> T::Extra { - let (id, who) = id_who; - Account::::get(id, who).map(|a| a.extra).unwrap_or_default() - } - - fn try_mutate_exists>( - id_who: &(T::AssetId, T::AccountId), - f: impl FnOnce(&mut Option) -> Result, - ) -> Result { - let (id, who) = id_who; - let mut maybe_extra = Account::::get(id, who).map(|a| a.extra); - let r = f(&mut maybe_extra)?; - // They want to write some value or delete it. - // If the account existed and they want to write a value, then we write. - // If the account didn't exist and they want to delete it, then we let it pass. - // Otherwise, we fail. - Account::::try_mutate(id, who, |maybe_account| { - if let Some(extra) = maybe_extra { - // They want to write a value. Let this happen only if the account actually exists. - if let Some(ref mut account) = maybe_account { - account.extra = extra; - } else { - return Err(DispatchError::NoProviders.into()) - } - } else { - // They want to delete it. Let this pass if the item never existed anyway. - ensure!(maybe_account.is_none(), DispatchError::ConsumerRemaining); - } - Ok(r) - }) - } -} diff --git a/pallets/assets/src/lib.rs b/pallets/assets/src/lib.rs deleted file mode 100644 index 106989ab..00000000 --- a/pallets/assets/src/lib.rs +++ /dev/null @@ -1,1963 +0,0 @@ -// 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. - -//! # Assets Pallet -//! -//! A simple, secure module for dealing with sets of assets implementing -//! [`fungible`](frame_support::traits::fungible) traits, via [`fungibles`] traits. -//! -//! The pallet makes heavy use of concepts such as Holds and Freezes from the -//! [`frame_support::traits::fungible`] traits, therefore you should read and understand those docs -//! as a prerequisite to understanding this pallet. -//! -//! See the [`frame_tokens`] reference docs for more information about the place of the -//! Assets pallet in FRAME. -//! -//! ## Overview -//! -//! The Assets module provides functionality for asset management of fungible asset classes -//! with a fixed supply, including: -//! -//! * Asset Issuance (Minting) -//! * Asset Transferal -//! * Asset Freezing -//! * Asset Destruction (Burning) -//! * Delegated Asset Transfers ("Approval API") -//! -//! To use it in your runtime, you need to implement the assets [`Config`]. -//! -//! The supported dispatchable functions are documented in the [`Call`] enum. -//! -//! ### Terminology -//! -//! * **Admin**: An account ID uniquely privileged to be able to unfreeze (thaw) an account and its -//! assets, as well as forcibly transfer a particular class of assets between arbitrary accounts -//! and reduce the balance of a particular class of assets of arbitrary accounts. -//! * **Asset issuance/minting**: The creation of a new asset, whose total supply will belong to the -//! account designated as the beneficiary of the asset. This is a privileged operation. -//! * **Asset transfer**: The reduction of the balance of an asset of one account with the -//! corresponding increase in the balance of another. -//! * **Asset destruction**: The process of reducing the balance of an asset of one account. This is -//! a privileged operation. -//! * **Fungible asset**: An asset whose units are interchangeable. -//! * **Issuer**: An account ID uniquely privileged to be able to mint a particular class of assets. -//! * **Freezer**: An account ID uniquely privileged to be able to freeze an account from -//! transferring a particular class of assets. -//! * **Freezing**: Removing the possibility of an unpermissioned transfer of an asset from a -//! particular account. -//! * **Non-fungible asset**: An asset for which each unit has unique characteristics. -//! * **Owner**: An account ID uniquely privileged to be able to destroy a particular asset class, -//! or to set the Issuer, Freezer, Reserves, or Admin of that asset class. -//! * **Approval**: The act of allowing an account the permission to transfer some balance of asset -//! from the approving account into some third-party destination account. -//! * **Sufficiency**: The idea of a minimum-balance of an asset being sufficient to allow the -//! account's existence on the system without requiring any other existential-deposit. -//! -//! ### Goals -//! -//! The assets system in Substrate is designed to make the following possible: -//! -//! * Issue new assets in a permissioned or permissionless way, if permissionless, then with a -//! deposit required. -//! * Allow accounts to be delegated the ability to transfer assets without otherwise existing -//! on-chain (*approvals*). -//! * Move assets between accounts. -//! * Update an asset class's total supply. -//! * Allow administrative activities by specially privileged accounts including freezing account -//! balances and minting/burning assets. -//! -//! ## Interface -//! -//! ### Permissionless Functions -//! -//! * `create`: Creates a new asset class, taking the required deposit. -//! * `transfer`: Transfer sender's assets to another account. -//! * `transfer_keep_alive`: Transfer sender's assets to another account, keeping the sender alive. -//! * `approve_transfer`: Create or increase an delegated transfer. -//! * `cancel_approval`: Rescind a previous approval. -//! * `transfer_approved`: Transfer third-party's assets to another account. -//! * `touch`: Create an asset account for non-provider assets. Caller must place a deposit. -//! * `refund`: Return the deposit (if any) of the caller's asset account or a consumer reference -//! (if any) of the caller's account. -//! * `refund_other`: Return the deposit (if any) of a specified asset account. -//! * `touch_other`: Create an asset account for specified account. Caller must place a deposit. -//! -//! ### Permissioned Functions -//! -//! * `force_create`: Creates a new asset class without taking any deposit. -//! * `force_set_metadata`: Set the metadata of an asset class. -//! * `force_clear_metadata`: Remove the metadata of an asset class. -//! * `force_asset_status`: Alter an asset class's attributes. -//! * `force_cancel_approval`: Rescind a previous approval. -//! -//! ### Privileged Functions -//! -//! * `destroy`: Destroys an entire asset class; called by the asset class's Owner. -//! * `mint`: Increases the asset balance of an account; called by the asset class's Issuer. -//! * `burn`: Decreases the asset balance of an account; called by the asset class's Admin. -//! * `force_transfer`: Transfers between arbitrary accounts; called by the asset class's Admin. -//! * `freeze`: Disallows further `transfer`s from an account; called by the asset class's Freezer. -//! * `thaw`: Allows further `transfer`s to and from an account; called by the asset class's Admin. -//! * `transfer_ownership`: Changes an asset class's Owner; called by the asset class's Owner. -//! * `set_team`: Changes an asset class's Admin, Freezer and Issuer; called by the asset class's -//! Owner. -//! * `set_metadata`: Set the metadata of an asset class; called by the asset class's Owner. -//! * `clear_metadata`: Remove the metadata of an asset class; called by the asset class's Owner. -//! * `set_reserves`: Set the reserve information of an asset class; called by the asset class's -//! Owner. -//! * `block`: Disallows further `transfer`s to and from an account; called by the asset class's -//! Freezer. -//! -//! Please refer to the [`Call`] enum and its associated variants for documentation on each -//! function. -//! -//! ### Public Functions -//! -//! -//! * `balance` - Get the asset `id` balance of `who`. -//! * `total_supply` - Get the total supply of an asset `id`. -//! -//! Please refer to the [`Pallet`] struct for details on publicly available functions. -//! -//! ### Callbacks -//! -//! Using `CallbackHandle` associated type, user can configure custom callback functions which are -//! executed when new asset is created or an existing asset is destroyed. -//! -//! ## Related Modules -//! -//! * [`System`](../frame_system/index.html) -//! * [`Support`](../frame_support/index.html) -//! -//! [`frame_tokens`]: ../polkadot_sdk_docs/reference_docs/frame_tokens/index.html - -// This recursion limit is needed because we have too many benchmarks and benchmarking will fail if -// we add more without this limit. -#![recursion_limit = "1024"] -// Ensure we're `no_std` when compiling for Wasm. -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "runtime-benchmarks")] -pub mod benchmarking; -pub mod migration; -#[cfg(test)] -pub mod mock; -#[cfg(test)] -mod tests; -pub mod weights; - -mod extra_mutator; -pub use extra_mutator::*; -mod functions; -mod impl_fungibles; -mod impl_stored_map; -mod types; -pub use types::*; - -extern crate alloc; -extern crate core; - -use scale_info::TypeInfo; -use sp_runtime::{ - traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedSub, Saturating, StaticLookup, Zero}, - ArithmeticError, DispatchError, TokenError, -}; - -use alloc::vec::Vec; -use core::{fmt::Debug, marker::PhantomData}; -use frame_support::{ - dispatch::DispatchResult, - ensure, - pallet_prelude::DispatchResultWithPostInfo, - storage::KeyPrefixIterator, - traits::{ - tokens::{ - fungibles, DepositConsequence, Fortitude, - Preservation::{Expendable, Preserve}, - WithdrawConsequence, - }, - BalanceStatus::Reserved, - Currency, EnsureOriginWithArg, Incrementable, ReservableCurrency, StoredMap, - }, -}; -use frame_system::Config as SystemConfig; - -pub use pallet::*; -pub use weights::WeightInfo; - -type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; -const LOG_TARGET: &str = "runtime::assets"; - -/// Trait with callbacks that are executed after successful asset creation or destruction. -pub trait AssetsCallback { - /// Indicates that asset with `id` was successfully created by the `owner` - fn created(_id: &AssetId, _owner: &AccountId) -> Result<(), ()> { - Ok(()) - } - - /// Indicates that asset with `id` has just been destroyed - fn destroyed(_id: &AssetId) -> Result<(), ()> { - Ok(()) - } -} - -#[impl_trait_for_tuples::impl_for_tuples(10)] -impl AssetsCallback for Tuple { - fn created(id: &AssetId, owner: &AccountId) -> Result<(), ()> { - for_tuples!( #( Tuple::created(id, owner)?; )* ); - Ok(()) - } - - fn destroyed(id: &AssetId) -> Result<(), ()> { - for_tuples!( #( Tuple::destroyed(id)?; )* ); - Ok(()) - } -} - -/// Auto-increment the [`NextAssetId`] when an asset is created. -/// -/// This has not effect if the [`NextAssetId`] value is not present. -pub struct AutoIncAssetId(PhantomData<(T, I)>); -impl, I> AssetsCallback for AutoIncAssetId -where - T::AssetId: Incrementable, -{ - fn created(_: &T::AssetId, _: &T::AccountId) -> Result<(), ()> { - let Some(next_id) = NextAssetId::::get() else { - // Auto increment for the asset id is not enabled. - return Ok(()); - }; - let next_id = next_id.increment().ok_or(())?; - NextAssetId::::put(next_id); - Ok(()) - } -} - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use codec::HasCompact; - use frame_support::{ - pallet_prelude::*, - traits::{tokens::ProvideAssetReserves, AccountTouch, ContainsPair}, - }; - use frame_system::pallet_prelude::*; - - /// The in-code storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); - - /// The maximum number of configurable reserve locations for one asset class. - pub const MAX_RESERVES: u32 = 5; - - #[pallet::pallet] - #[pallet::storage_version(STORAGE_VERSION)] - pub struct Pallet(_); - - #[cfg(feature = "runtime-benchmarks")] - pub trait BenchmarkHelper { - fn create_asset_id_parameter(id: u32) -> AssetIdParameter; - fn create_reserve_id_parameter(id: u32) -> ReserveIdParameter; - } - #[cfg(feature = "runtime-benchmarks")] - impl> BenchmarkHelper for () { - fn create_asset_id_parameter(id: u32) -> AssetIdParameter { - id.into() - } - fn create_reserve_id_parameter(_: u32) -> () { - () - } - } - - /// Default implementations of [`DefaultConfig`], which can be used to implement [`Config`]. - pub mod config_preludes { - use super::*; - use frame_support::derive_impl; - pub struct TestDefaultConfig; - - #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)] - impl frame_system::DefaultConfig for TestDefaultConfig {} - - #[frame_support::register_default_impl(TestDefaultConfig)] - impl DefaultConfig for TestDefaultConfig { - #[inject_runtime_type] - type RuntimeEvent = (); - type Balance = u64; - type RemoveItemsLimit = ConstU32<5>; - type AssetId = u32; - type AssetIdParameter = u32; - type ReserveData = (); - type AssetDeposit = ConstUint<1>; - type AssetAccountDeposit = ConstUint<10>; - type MetadataDepositBase = ConstUint<1>; - type MetadataDepositPerByte = ConstUint<1>; - type ApprovalDeposit = ConstUint<1>; - type StringLimit = ConstU32<50>; - type Freezer = (); - type Holder = (); - type Extra = (); - type CallbackHandle = (); - type WeightInfo = (); - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = (); - } - } - - #[pallet::config(with_default)] - /// The module configuration trait. - pub trait Config: frame_system::Config { - /// The overarching event type. - #[pallet::no_default_bounds] - #[allow(deprecated)] - type RuntimeEvent: From> - + IsType<::RuntimeEvent>; - - /// The units in which we record balances. - type Balance: Member - + Parameter - + HasCompact - + AtLeast32BitUnsigned - + Default - + Copy - + MaybeSerializeDeserialize - + MaxEncodedLen - + TypeInfo; - - /// Max number of items to destroy per `destroy_accounts` and `destroy_approvals` call. - /// - /// Must be configured to result in a weight that makes each call fit in a block. - #[pallet::constant] - type RemoveItemsLimit: Get; - - /// Identifier for the class of asset. - type AssetId: Member + Parameter + Clone + MaybeSerializeDeserialize + MaxEncodedLen; - - /// Wrapper around `Self::AssetId` to use in dispatchable call signatures. Allows the use - /// of compact encoding in instances of the pallet, which will prevent breaking changes - /// resulting from the removal of `HasCompact` from `Self::AssetId`. - /// - /// This type includes the `From` bound, since tightly coupled pallets may - /// want to convert an `AssetId` into a parameter for calling dispatchable functions - /// directly. - type AssetIdParameter: Parameter + From + Into + MaxEncodedLen; - - /// Information about reserve locations for a class of asset. - type ReserveData: Debug + Parameter + MaybeSerializeDeserialize + MaxEncodedLen; - - /// The currency mechanism. - #[pallet::no_default] - type Currency: ReservableCurrency; - - /// Standard asset class creation is only allowed if the origin attempting it and the - /// asset class are in this set. - #[pallet::no_default] - type CreateOrigin: EnsureOriginWithArg< - Self::RuntimeOrigin, - Self::AssetId, - Success = Self::AccountId, - >; - - /// The origin which may forcibly create or destroy an asset or otherwise alter privileged - /// attributes. - #[pallet::no_default] - type ForceOrigin: EnsureOrigin; - - /// The basic amount of funds that must be reserved for an asset. - #[pallet::constant] - #[pallet::no_default_bounds] - type AssetDeposit: Get>; - - /// The amount of funds that must be reserved for a non-provider asset account to be - /// maintained. - #[pallet::constant] - #[pallet::no_default_bounds] - type AssetAccountDeposit: Get>; - - /// The basic amount of funds that must be reserved when adding metadata to your asset. - #[pallet::constant] - #[pallet::no_default_bounds] - type MetadataDepositBase: Get>; - - /// The additional funds that must be reserved for the number of bytes you store in your - /// metadata. - #[pallet::constant] - #[pallet::no_default_bounds] - type MetadataDepositPerByte: Get>; - - /// The amount of funds that must be reserved when creating a new approval. - #[pallet::constant] - #[pallet::no_default_bounds] - type ApprovalDeposit: Get>; - - /// The maximum length of a name or symbol stored on-chain. - #[pallet::constant] - type StringLimit: Get; - - /// A hook to allow a per-asset, per-account minimum balance to be enforced. This must be - /// respected in all permissionless operations. - type Freezer: FrozenBalance; - - /// A hook to inspect a per-asset, per-account balance that is held. This goes in - /// accordance with balance model. - type Holder: BalanceOnHold; - - /// Additional data to be stored with an account's asset balance. - type Extra: Member + Parameter + Default + MaxEncodedLen; - - /// Callback methods for asset state change (e.g. asset created or destroyed) - /// - /// Types implementing the [`AssetsCallback`] can be chained when listed together as a - /// tuple. - /// The [`AutoIncAssetId`] callback, in conjunction with the [`NextAssetId`], can be - /// used to set up auto-incrementing asset IDs for this collection. - type CallbackHandle: AssetsCallback; - - /// Weight information for extrinsics in this pallet. - type WeightInfo: WeightInfo; - - /// Helper trait for benchmarks. - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper: BenchmarkHelper; - } - - #[pallet::storage] - /// Details of an asset. - pub type Asset, I: 'static = ()> = StorageMap< - _, - Blake2_128Concat, - T::AssetId, - AssetDetails>, - >; - - #[pallet::storage] - /// The holdings of a specific account for a specific asset. - pub type Account, I: 'static = ()> = StorageDoubleMap< - _, - Blake2_128Concat, - T::AssetId, - Blake2_128Concat, - T::AccountId, - AssetAccountOf, - >; - - #[pallet::storage] - /// Approved balance transfers. First balance is the amount approved for transfer. Second - /// is the amount of `T::Currency` reserved for storing this. - /// First key is the asset ID, second key is the owner and third key is the delegate. - pub type Approvals, I: 'static = ()> = StorageNMap< - _, - ( - NMapKey, - NMapKey, // owner - NMapKey, // delegate - ), - Approval>, - >; - - #[pallet::storage] - /// Metadata of an asset. - pub type Metadata, I: 'static = ()> = StorageMap< - _, - Blake2_128Concat, - T::AssetId, - AssetMetadata, BoundedVec>, - ValueQuery, - >; - - /// Maps an asset to a list of its configured reserve information. - #[pallet::storage] - pub type Reserves, I: 'static = ()> = StorageMap< - _, - Blake2_128Concat, - T::AssetId, - BoundedVec>, - ValueQuery, - >; - - /// The asset ID enforced for the next asset creation, if any present. Otherwise, this storage - /// item has no effect. - /// - /// This can be useful for setting up constraints for IDs of the new assets. For example, by - /// providing an initial [`NextAssetId`] and using the [`crate::AutoIncAssetId`] callback, an - /// auto-increment model can be applied to all new asset IDs. - /// - /// The initial next asset ID can be set using the [`GenesisConfig`] or the - /// [SetNextAssetId](`migration::next_asset_id::SetNextAssetId`) migration. - #[pallet::storage] - pub type NextAssetId, I: 'static = ()> = StorageValue<_, T::AssetId, OptionQuery>; - - #[pallet::genesis_config] - #[derive(frame_support::DefaultNoBound)] - pub struct GenesisConfig, I: 'static = ()> { - /// Genesis assets: id, owner, is_sufficient, min_balance - pub assets: Vec<(T::AssetId, T::AccountId, bool, T::Balance)>, - /// Genesis metadata: id, name, symbol, decimals - pub metadata: Vec<(T::AssetId, Vec, Vec, u8)>, - /// Genesis accounts: id, account_id, balance - pub accounts: Vec<(T::AssetId, T::AccountId, T::Balance)>, - /// Genesis [`NextAssetId`]. - /// - /// Refer to the [`NextAssetId`] item for more information. - /// - /// This does not enforce the asset ID for the [assets](`GenesisConfig::assets`) within the - /// genesis config. It sets the [`NextAssetId`] after they have been created. - pub next_asset_id: Option, - /// Genesis assets and their reserves - pub reserves: Vec<(T::AssetId, Vec)>, - } - - #[pallet::genesis_build] - impl, I: 'static> BuildGenesisConfig for GenesisConfig { - fn build(&self) { - for (id, owner, is_sufficient, min_balance) in &self.assets { - assert!(!Asset::::contains_key(id), "Asset id already in use"); - assert!(!min_balance.is_zero(), "Min balance should not be zero"); - Asset::::insert( - id, - AssetDetails { - owner: owner.clone(), - issuer: owner.clone(), - admin: owner.clone(), - freezer: owner.clone(), - supply: Zero::zero(), - deposit: Zero::zero(), - min_balance: *min_balance, - is_sufficient: *is_sufficient, - accounts: 0, - sufficients: 0, - approvals: 0, - status: AssetStatus::Live, - }, - ); - } - - for (id, name, symbol, decimals) in &self.metadata { - assert!(Asset::::contains_key(id), "Asset does not exist"); - - let bounded_name: BoundedVec = - name.clone().try_into().expect("asset name is too long"); - let bounded_symbol: BoundedVec = - symbol.clone().try_into().expect("asset symbol is too long"); - - let metadata = AssetMetadata { - deposit: Zero::zero(), - name: bounded_name, - symbol: bounded_symbol, - decimals: *decimals, - is_frozen: false, - }; - Metadata::::insert(id, metadata); - } - - for (id, account_id, amount) in &self.accounts { - let result = >::increase_balance( - id.clone(), - account_id, - *amount, - |details| -> DispatchResult { - debug_assert!( - details.supply.checked_add(&amount).is_some(), - "checked in prep; qed" - ); - details.supply = details.supply.saturating_add(*amount); - Ok(()) - }, - ); - assert!(result.is_ok()); - } - - if let Some(next_asset_id) = &self.next_asset_id { - NextAssetId::::put(next_asset_id); - } - - for (id, reserves) in &self.reserves { - assert!(!Reserves::::contains_key(id), "Asset id already in use"); - let reserves = BoundedVec::try_from(reserves.clone()).expect("too many reserves"); - Reserves::::insert(id, reserves); - } - } - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event, I: 'static = ()> { - /// Some asset class was created. - Created { asset_id: T::AssetId, creator: T::AccountId, owner: T::AccountId }, - /// Some assets were issued. - Issued { asset_id: T::AssetId, owner: T::AccountId, amount: T::Balance }, - /// Some assets were transferred. - Transferred { - asset_id: T::AssetId, - from: T::AccountId, - to: T::AccountId, - amount: T::Balance, - }, - /// Some assets were destroyed. - Burned { asset_id: T::AssetId, owner: T::AccountId, balance: T::Balance }, - /// The management team changed. - TeamChanged { - asset_id: T::AssetId, - issuer: T::AccountId, - admin: T::AccountId, - freezer: T::AccountId, - }, - /// The owner changed. - OwnerChanged { asset_id: T::AssetId, owner: T::AccountId }, - /// Some account `who` was frozen. - Frozen { asset_id: T::AssetId, who: T::AccountId }, - /// Some account `who` was thawed. - Thawed { asset_id: T::AssetId, who: T::AccountId }, - /// Some asset `asset_id` was frozen. - AssetFrozen { asset_id: T::AssetId }, - /// Some asset `asset_id` was thawed. - AssetThawed { asset_id: T::AssetId }, - /// Accounts were destroyed for given asset. - AccountsDestroyed { asset_id: T::AssetId, accounts_destroyed: u32, accounts_remaining: u32 }, - /// Approvals were destroyed for given asset. - ApprovalsDestroyed { - asset_id: T::AssetId, - approvals_destroyed: u32, - approvals_remaining: u32, - }, - /// An asset class is in the process of being destroyed. - DestructionStarted { asset_id: T::AssetId }, - /// An asset class was destroyed. - Destroyed { asset_id: T::AssetId }, - /// Some asset class was force-created. - ForceCreated { asset_id: T::AssetId, owner: T::AccountId }, - /// New metadata has been set for an asset. - MetadataSet { - asset_id: T::AssetId, - name: Vec, - symbol: Vec, - decimals: u8, - is_frozen: bool, - }, - /// Metadata has been cleared for an asset. - MetadataCleared { asset_id: T::AssetId }, - /// (Additional) funds have been approved for transfer to a destination account. - ApprovedTransfer { - asset_id: T::AssetId, - source: T::AccountId, - delegate: T::AccountId, - amount: T::Balance, - }, - /// An approval for account `delegate` was cancelled by `owner`. - ApprovalCancelled { asset_id: T::AssetId, owner: T::AccountId, delegate: T::AccountId }, - /// An `amount` was transferred in its entirety from `owner` to `destination` by - /// the approved `delegate`. - TransferredApproved { - asset_id: T::AssetId, - owner: T::AccountId, - delegate: T::AccountId, - destination: T::AccountId, - amount: T::Balance, - }, - /// An asset has had its attributes changed by the `Force` origin. - AssetStatusChanged { asset_id: T::AssetId }, - /// The min_balance of an asset has been updated by the asset owner. - AssetMinBalanceChanged { asset_id: T::AssetId, new_min_balance: T::Balance }, - /// Some account `who` was created with a deposit from `depositor`. - Touched { asset_id: T::AssetId, who: T::AccountId, depositor: T::AccountId }, - /// Some account `who` was blocked. - Blocked { asset_id: T::AssetId, who: T::AccountId }, - /// Some assets were deposited (e.g. for transaction fees). - Deposited { asset_id: T::AssetId, who: T::AccountId, amount: T::Balance }, - /// Some assets were withdrawn from the account (e.g. for transaction fees). - Withdrawn { asset_id: T::AssetId, who: T::AccountId, amount: T::Balance }, - /// Reserve information was set or updated for `asset_id`. - ReservesUpdated { asset_id: T::AssetId, reserves: Vec }, - /// Reserve information was removed for `asset_id`. - ReservesRemoved { asset_id: T::AssetId }, - } - - #[pallet::error] - pub enum Error { - /// Account balance must be greater than or equal to the transfer amount. - BalanceLow, - /// The account to alter does not exist. - NoAccount, - /// The signing account has no permission to do the operation. - NoPermission, - /// The given asset ID is unknown. - Unknown, - /// The origin account is frozen. - Frozen, - /// The asset ID is already taken. - InUse, - /// Invalid witness data given. - BadWitness, - /// Minimum balance should be non-zero. - MinBalanceZero, - /// Unable to increment the consumer reference counters on the account. Either no provider - /// reference exists to allow a non-zero balance of a non-self-sufficient asset, or one - /// fewer then the maximum number of consumers has been reached. - UnavailableConsumer, - /// Invalid metadata given. - BadMetadata, - /// No approval exists that would allow the transfer. - Unapproved, - /// The source account would not survive the transfer and it needs to stay alive. - WouldDie, - /// The asset-account already exists. - AlreadyExists, - /// The asset-account doesn't have an associated deposit. - NoDeposit, - /// The operation would result in funds being burned. - WouldBurn, - /// The asset is a live asset and is actively being used. Usually emit for operations such - /// as `start_destroy` which require the asset to be in a destroying state. - LiveAsset, - /// The asset is not live, and likely being destroyed. - AssetNotLive, - /// The asset status is not the expected status. - IncorrectStatus, - /// The asset should be frozen before the given operation. - NotFrozen, - /// Callback action resulted in error - CallbackFailed, - /// The asset ID must be equal to the [`NextAssetId`]. - BadAssetId, - /// The asset cannot be destroyed because some accounts for this asset contain freezes. - ContainsFreezes, - /// The asset cannot be destroyed because some accounts for this asset contain holds. - ContainsHolds, - /// Tried setting too many reserves. - TooManyReserves, - } - - #[pallet::call(weight(>::WeightInfo))] - impl, I: 'static> Pallet { - /// Issue a new class of fungible assets from a public origin. - /// - /// This new asset class has no assets initially and its owner is the origin. - /// - /// The origin must conform to the configured `CreateOrigin` and have sufficient funds free. - /// - /// Funds of sender are reserved by `AssetDeposit`. - /// - /// Parameters: - /// - `id`: The identifier of the new asset. This must not be currently in use to identify - /// an existing asset. If [`NextAssetId`] is set, then this must be equal to it. - /// - `admin`: The admin of this class of assets. The admin is the initial address of each - /// member of the asset class's admin team. - /// - `min_balance`: The minimum balance of this new asset that any single account must - /// have. If an account's balance is reduced below this, then it collapses to zero. - /// - /// Emits `Created` event when successful. - /// - /// Weight: `O(1)` - #[pallet::call_index(0)] - pub fn create( - origin: OriginFor, - id: T::AssetIdParameter, - admin: AccountIdLookupOf, - min_balance: T::Balance, - ) -> DispatchResult { - let id: T::AssetId = id.into(); - let owner = T::CreateOrigin::ensure_origin(origin, &id)?; - let admin = T::Lookup::lookup(admin)?; - - ensure!(!Asset::::contains_key(&id), Error::::InUse); - ensure!(!min_balance.is_zero(), Error::::MinBalanceZero); - - if let Some(next_id) = NextAssetId::::get() { - ensure!(id == next_id, Error::::BadAssetId); - } - - let deposit = T::AssetDeposit::get(); - T::Currency::reserve(&owner, deposit)?; - - Asset::::insert( - id.clone(), - AssetDetails { - owner: owner.clone(), - issuer: admin.clone(), - admin: admin.clone(), - freezer: admin.clone(), - supply: Zero::zero(), - deposit, - min_balance, - is_sufficient: false, - accounts: 0, - sufficients: 0, - approvals: 0, - status: AssetStatus::Live, - }, - ); - ensure!(T::CallbackHandle::created(&id, &owner).is_ok(), Error::::CallbackFailed); - Self::deposit_event(Event::Created { asset_id: id, creator: owner.clone(), owner }); - - Ok(()) - } - - /// Issue a new class of fungible assets from a privileged origin. - /// - /// This new asset class has no assets initially. - /// - /// The origin must conform to `ForceOrigin`. - /// - /// Unlike `create`, no funds are reserved. - /// - /// - `id`: The identifier of the new asset. This must not be currently in use to identify - /// an existing asset. If [`NextAssetId`] is set, then this must be equal to it. - /// - `owner`: The owner of this class of assets. The owner has full superuser permissions - /// over this asset, but may later change and configure the permissions using - /// `transfer_ownership` and `set_team`. - /// - `min_balance`: The minimum balance of this new asset that any single account must - /// have. If an account's balance is reduced below this, then it collapses to zero. - /// - /// Emits `ForceCreated` event when successful. - /// - /// Weight: `O(1)` - #[pallet::call_index(1)] - pub fn force_create( - origin: OriginFor, - id: T::AssetIdParameter, - owner: AccountIdLookupOf, - is_sufficient: bool, - #[pallet::compact] min_balance: T::Balance, - ) -> DispatchResult { - T::ForceOrigin::ensure_origin(origin)?; - let owner = T::Lookup::lookup(owner)?; - let id: T::AssetId = id.into(); - Self::do_force_create(id, owner, is_sufficient, min_balance) - } - - /// Start the process of destroying a fungible asset class. - /// - /// `start_destroy` is the first in a series of extrinsics that should be called, to allow - /// destruction of an asset class. - /// - /// The origin must conform to `ForceOrigin` or must be `Signed` by the asset's `owner`. - /// - /// - `id`: The identifier of the asset to be destroyed. This must identify an existing - /// asset. - /// - /// It will fail with either [`Error::ContainsHolds`] or [`Error::ContainsFreezes`] if - /// an account contains holds or freezes in place. - #[pallet::call_index(2)] - pub fn start_destroy(origin: OriginFor, id: T::AssetIdParameter) -> DispatchResult { - let maybe_check_owner = match T::ForceOrigin::try_origin(origin) { - Ok(_) => None, - Err(origin) => Some(ensure_signed(origin)?), - }; - let id: T::AssetId = id.into(); - Self::do_start_destroy(id, maybe_check_owner) - } - - /// Destroy all accounts associated with a given asset. - /// - /// `destroy_accounts` should only be called after `start_destroy` has been called, and the - /// asset is in a `Destroying` state. - /// - /// Due to weight restrictions, this function may need to be called multiple times to fully - /// destroy all accounts. It will destroy `RemoveItemsLimit` accounts at a time. - /// - /// - `id`: The identifier of the asset to be destroyed. This must identify an existing - /// asset. - /// - /// Each call emits the `Event::DestroyedAccounts` event. - #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::destroy_accounts(T::RemoveItemsLimit::get()))] - pub fn destroy_accounts( - origin: OriginFor, - id: T::AssetIdParameter, - ) -> DispatchResultWithPostInfo { - ensure_signed(origin)?; - let id: T::AssetId = id.into(); - let removed_accounts = Self::do_destroy_accounts(id, T::RemoveItemsLimit::get())?; - Ok(Some(T::WeightInfo::destroy_accounts(removed_accounts)).into()) - } - - /// Destroy all approvals associated with a given asset up to the max (T::RemoveItemsLimit). - /// - /// `destroy_approvals` should only be called after `start_destroy` has been called, and the - /// asset is in a `Destroying` state. - /// - /// Due to weight restrictions, this function may need to be called multiple times to fully - /// destroy all approvals. It will destroy `RemoveItemsLimit` approvals at a time. - /// - /// - `id`: The identifier of the asset to be destroyed. This must identify an existing - /// asset. - /// - /// Each call emits the `Event::DestroyedApprovals` event. - #[pallet::call_index(4)] - #[pallet::weight(T::WeightInfo::destroy_approvals(T::RemoveItemsLimit::get()))] - pub fn destroy_approvals( - origin: OriginFor, - id: T::AssetIdParameter, - ) -> DispatchResultWithPostInfo { - ensure_signed(origin)?; - let id: T::AssetId = id.into(); - let removed_approvals = Self::do_destroy_approvals(id, T::RemoveItemsLimit::get())?; - Ok(Some(T::WeightInfo::destroy_approvals(removed_approvals)).into()) - } - - /// Complete destroying asset and unreserve currency. - /// - /// `finish_destroy` should only be called after `start_destroy` has been called, and the - /// asset is in a `Destroying` state. All accounts or approvals should be destroyed before - /// hand. - /// - /// - `id`: The identifier of the asset to be destroyed. This must identify an existing - /// asset. - /// - /// Each successful call emits the `Event::Destroyed` event. - #[pallet::call_index(5)] - pub fn finish_destroy(origin: OriginFor, id: T::AssetIdParameter) -> DispatchResult { - ensure_signed(origin)?; - let id: T::AssetId = id.into(); - Self::do_finish_destroy(id) - } - - /// Mint assets of a particular class. - /// - /// The origin must be Signed and the sender must be the Issuer of the asset `id`. - /// - /// - `id`: The identifier of the asset to have some amount minted. - /// - `beneficiary`: The account to be credited with the minted assets. - /// - `amount`: The amount of the asset to be minted. - /// - /// Emits `Issued` event when successful. - /// - /// Weight: `O(1)` - /// Modes: Pre-existing balance of `beneficiary`; Account pre-existence of `beneficiary`. - #[pallet::call_index(6)] - pub fn mint( - origin: OriginFor, - id: T::AssetIdParameter, - beneficiary: AccountIdLookupOf, - #[pallet::compact] amount: T::Balance, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let beneficiary = T::Lookup::lookup(beneficiary)?; - let id: T::AssetId = id.into(); - Self::do_mint(id, &beneficiary, amount, Some(origin))?; - Ok(()) - } - - /// Reduce the balance of `who` by as much as possible up to `amount` assets of `id`. - /// - /// Origin must be Signed and the sender should be the Manager of the asset `id`. - /// - /// Bails with `NoAccount` if the `who` is already dead. - /// - /// - `id`: The identifier of the asset to have some amount burned. - /// - `who`: The account to be debited from. - /// - `amount`: The maximum amount by which `who`'s balance should be reduced. - /// - /// Emits `Burned` with the actual amount burned. If this takes the balance to below the - /// minimum for the asset, then the amount burned is increased to take it to zero. - /// - /// Weight: `O(1)` - /// Modes: Post-existence of `who`; Pre & post Zombie-status of `who`. - #[pallet::call_index(7)] - pub fn burn( - origin: OriginFor, - id: T::AssetIdParameter, - who: AccountIdLookupOf, - #[pallet::compact] amount: T::Balance, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let who = T::Lookup::lookup(who)?; - let id: T::AssetId = id.into(); - - let f = DebitFlags { keep_alive: false, best_effort: true }; - Self::do_burn(id, &who, amount, Some(origin), f)?; - Ok(()) - } - - /// Move some assets from the sender account to another. - /// - /// Origin must be Signed. - /// - /// - `id`: The identifier of the asset to have some amount transferred. - /// - `target`: The account to be credited. - /// - `amount`: The amount by which the sender's balance of assets should be reduced and - /// `target`'s balance increased. The amount actually transferred may be slightly greater in - /// the case that the transfer would otherwise take the sender balance above zero but below - /// the minimum balance. Must be greater than zero. - /// - /// Emits `Transferred` with the actual amount transferred. If this takes the source balance - /// to below the minimum for the asset, then the amount transferred is increased to take it - /// to zero. - /// - /// Weight: `O(1)` - /// Modes: Pre-existence of `target`; Post-existence of sender; Account pre-existence of - /// `target`. - #[pallet::call_index(8)] - pub fn transfer( - origin: OriginFor, - id: T::AssetIdParameter, - target: AccountIdLookupOf, - #[pallet::compact] amount: T::Balance, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let dest = T::Lookup::lookup(target)?; - let id: T::AssetId = id.into(); - - let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false }; - Self::do_transfer(id, &origin, &dest, amount, None, f).map(|_| ()) - } - - /// Move some assets from the sender account to another, keeping the sender account alive. - /// - /// Origin must be Signed. - /// - /// - `id`: The identifier of the asset to have some amount transferred. - /// - `target`: The account to be credited. - /// - `amount`: The amount by which the sender's balance of assets should be reduced and - /// `target`'s balance increased. The amount actually transferred may be slightly greater in - /// the case that the transfer would otherwise take the sender balance above zero but below - /// the minimum balance. Must be greater than zero. - /// - /// Emits `Transferred` with the actual amount transferred. If this takes the source balance - /// to below the minimum for the asset, then the amount transferred is increased to take it - /// to zero. - /// - /// Weight: `O(1)` - /// Modes: Pre-existence of `target`; Post-existence of sender; Account pre-existence of - /// `target`. - #[pallet::call_index(9)] - pub fn transfer_keep_alive( - origin: OriginFor, - id: T::AssetIdParameter, - target: AccountIdLookupOf, - #[pallet::compact] amount: T::Balance, - ) -> DispatchResult { - let source = ensure_signed(origin)?; - let dest = T::Lookup::lookup(target)?; - let id: T::AssetId = id.into(); - - let f = TransferFlags { keep_alive: true, best_effort: false, burn_dust: false }; - Self::do_transfer(id, &source, &dest, amount, None, f).map(|_| ()) - } - - /// Move some assets from one account to another. - /// - /// Origin must be Signed and the sender should be the Admin of the asset `id`. - /// - /// - `id`: The identifier of the asset to have some amount transferred. - /// - `source`: The account to be debited. - /// - `dest`: The account to be credited. - /// - `amount`: The amount by which the `source`'s balance of assets should be reduced and - /// `dest`'s balance increased. The amount actually transferred may be slightly greater in - /// the case that the transfer would otherwise take the `source` balance above zero but - /// below the minimum balance. Must be greater than zero. - /// - /// Emits `Transferred` with the actual amount transferred. If this takes the source balance - /// to below the minimum for the asset, then the amount transferred is increased to take it - /// to zero. - /// - /// Weight: `O(1)` - /// Modes: Pre-existence of `dest`; Post-existence of `source`; Account pre-existence of - /// `dest`. - #[pallet::call_index(10)] - pub fn force_transfer( - origin: OriginFor, - id: T::AssetIdParameter, - source: AccountIdLookupOf, - dest: AccountIdLookupOf, - #[pallet::compact] amount: T::Balance, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let source = T::Lookup::lookup(source)?; - let dest = T::Lookup::lookup(dest)?; - let id: T::AssetId = id.into(); - - let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false }; - Self::do_transfer(id, &source, &dest, amount, Some(origin), f).map(|_| ()) - } - - /// Disallow further unprivileged transfers of an asset `id` from an account `who`. `who` - /// must already exist as an entry in `Account`s of the asset. If you want to freeze an - /// account that does not have an entry, use `touch_other` first. - /// - /// Origin must be Signed and the sender should be the Freezer of the asset `id`. - /// - /// - `id`: The identifier of the asset to be frozen. - /// - `who`: The account to be frozen. - /// - /// Emits `Frozen`. - /// - /// Weight: `O(1)` - #[pallet::call_index(11)] - pub fn freeze( - origin: OriginFor, - id: T::AssetIdParameter, - who: AccountIdLookupOf, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let id: T::AssetId = id.into(); - - let d = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!( - d.status == AssetStatus::Live || d.status == AssetStatus::Frozen, - Error::::IncorrectStatus - ); - ensure!(origin == d.freezer, Error::::NoPermission); - let who = T::Lookup::lookup(who)?; - - Account::::try_mutate(&id, &who, |maybe_account| -> DispatchResult { - maybe_account.as_mut().ok_or(Error::::NoAccount)?.status = - AccountStatus::Frozen; - Ok(()) - })?; - - Self::deposit_event(Event::::Frozen { asset_id: id, who }); - Ok(()) - } - - /// Allow unprivileged transfers to and from an account again. - /// - /// Origin must be Signed and the sender should be the Admin of the asset `id`. - /// - /// - `id`: The identifier of the asset to be frozen. - /// - `who`: The account to be unfrozen. - /// - /// Emits `Thawed`. - /// - /// Weight: `O(1)` - #[pallet::call_index(12)] - pub fn thaw( - origin: OriginFor, - id: T::AssetIdParameter, - who: AccountIdLookupOf, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let id: T::AssetId = id.into(); - - let details = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!( - details.status == AssetStatus::Live || details.status == AssetStatus::Frozen, - Error::::IncorrectStatus - ); - ensure!(origin == details.admin, Error::::NoPermission); - let who = T::Lookup::lookup(who)?; - - Account::::try_mutate(&id, &who, |maybe_account| -> DispatchResult { - maybe_account.as_mut().ok_or(Error::::NoAccount)?.status = - AccountStatus::Liquid; - Ok(()) - })?; - - Self::deposit_event(Event::::Thawed { asset_id: id, who }); - Ok(()) - } - - /// Disallow further unprivileged transfers for the asset class. - /// - /// Origin must be Signed and the sender should be the Freezer of the asset `id`. - /// - /// - `id`: The identifier of the asset to be frozen. - /// - /// Emits `Frozen`. - /// - /// Weight: `O(1)` - #[pallet::call_index(13)] - pub fn freeze_asset(origin: OriginFor, id: T::AssetIdParameter) -> DispatchResult { - let origin = ensure_signed(origin)?; - let id: T::AssetId = id.into(); - - Asset::::try_mutate(id.clone(), |maybe_details| { - let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; - ensure!(d.status == AssetStatus::Live, Error::::AssetNotLive); - ensure!(origin == d.freezer, Error::::NoPermission); - - d.status = AssetStatus::Frozen; - - Self::deposit_event(Event::::AssetFrozen { asset_id: id }); - Ok(()) - }) - } - - /// Allow unprivileged transfers for the asset again. - /// - /// Origin must be Signed and the sender should be the Admin of the asset `id`. - /// - /// - `id`: The identifier of the asset to be thawed. - /// - /// Emits `Thawed`. - /// - /// Weight: `O(1)` - #[pallet::call_index(14)] - pub fn thaw_asset(origin: OriginFor, id: T::AssetIdParameter) -> DispatchResult { - let origin = ensure_signed(origin)?; - let id: T::AssetId = id.into(); - - Asset::::try_mutate(id.clone(), |maybe_details| { - let d = maybe_details.as_mut().ok_or(Error::::Unknown)?; - ensure!(origin == d.admin, Error::::NoPermission); - ensure!(d.status == AssetStatus::Frozen, Error::::NotFrozen); - - d.status = AssetStatus::Live; - - Self::deposit_event(Event::::AssetThawed { asset_id: id }); - Ok(()) - }) - } - - /// Change the Owner of an asset. - /// - /// Origin must be Signed and the sender should be the Owner of the asset `id`. - /// - /// - `id`: The identifier of the asset. - /// - `owner`: The new Owner of this asset. - /// - /// Emits `OwnerChanged`. - /// - /// Weight: `O(1)` - #[pallet::call_index(15)] - pub fn transfer_ownership( - origin: OriginFor, - id: T::AssetIdParameter, - owner: AccountIdLookupOf, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let owner = T::Lookup::lookup(owner)?; - let id: T::AssetId = id.into(); - - Asset::::try_mutate(id.clone(), |maybe_details| { - let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; - ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); - ensure!(origin == details.owner, Error::::NoPermission); - if details.owner == owner { - return Ok(()); - } - - let metadata_deposit = Metadata::::get(&id).deposit; - let deposit = details.deposit + metadata_deposit; - - // Move the deposit to the new owner. - T::Currency::repatriate_reserved(&details.owner, &owner, deposit, Reserved)?; - - details.owner = owner.clone(); - - Self::deposit_event(Event::OwnerChanged { asset_id: id, owner }); - Ok(()) - }) - } - - /// Change the Issuer, Admin and Freezer of an asset. - /// - /// Origin must be Signed and the sender should be the Owner of the asset `id`. - /// - /// - `id`: The identifier of the asset to be frozen. - /// - `issuer`: The new Issuer of this asset. - /// - `admin`: The new Admin of this asset. - /// - `freezer`: The new Freezer of this asset. - /// - /// Emits `TeamChanged`. - /// - /// Weight: `O(1)` - #[pallet::call_index(16)] - pub fn set_team( - origin: OriginFor, - id: T::AssetIdParameter, - issuer: AccountIdLookupOf, - admin: AccountIdLookupOf, - freezer: AccountIdLookupOf, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let issuer = T::Lookup::lookup(issuer)?; - let admin = T::Lookup::lookup(admin)?; - let freezer = T::Lookup::lookup(freezer)?; - let id: T::AssetId = id.into(); - - Asset::::try_mutate(id.clone(), |maybe_details| { - let details = maybe_details.as_mut().ok_or(Error::::Unknown)?; - ensure!(details.status == AssetStatus::Live, Error::::AssetNotLive); - ensure!(origin == details.owner, Error::::NoPermission); - - details.issuer = issuer.clone(); - details.admin = admin.clone(); - details.freezer = freezer.clone(); - - Self::deposit_event(Event::TeamChanged { asset_id: id, issuer, admin, freezer }); - Ok(()) - }) - } - - /// Set the metadata for an asset. - /// - /// Origin must be Signed and the sender should be the Owner of the asset `id`. - /// - /// Funds of sender are reserved according to the formula: - /// `MetadataDepositBase + MetadataDepositPerByte * (name.len + symbol.len)` taking into - /// account any already reserved funds. - /// - /// - `id`: The identifier of the asset to update. - /// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`. - /// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`. - /// - `decimals`: The number of decimals this asset uses to represent one unit. - /// - /// Emits `MetadataSet`. - /// - /// Weight: `O(1)` - #[pallet::call_index(17)] - #[pallet::weight(T::WeightInfo::set_metadata(name.len() as u32, symbol.len() as u32))] - pub fn set_metadata( - origin: OriginFor, - id: T::AssetIdParameter, - name: Vec, - symbol: Vec, - decimals: u8, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let id: T::AssetId = id.into(); - Self::do_set_metadata(id, &origin, name, symbol, decimals) - } - - /// Clear the metadata for an asset. - /// - /// Origin must be Signed and the sender should be the Owner of the asset `id`. - /// - /// Any deposit is freed for the asset owner. - /// - /// - `id`: The identifier of the asset to clear. - /// - /// Emits `MetadataCleared`. - /// - /// Weight: `O(1)` - #[pallet::call_index(18)] - pub fn clear_metadata(origin: OriginFor, id: T::AssetIdParameter) -> DispatchResult { - let origin = ensure_signed(origin)?; - let id: T::AssetId = id.into(); - - let d = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!(d.status == AssetStatus::Live, Error::::AssetNotLive); - ensure!(origin == d.owner, Error::::NoPermission); - - Metadata::::try_mutate_exists(id.clone(), |metadata| { - let deposit = metadata.take().ok_or(Error::::Unknown)?.deposit; - T::Currency::unreserve(&d.owner, deposit); - Self::deposit_event(Event::MetadataCleared { asset_id: id }); - Ok(()) - }) - } - - /// Force the metadata for an asset to some value. - /// - /// Origin must be ForceOrigin. - /// - /// Any deposit is left alone. - /// - /// - `id`: The identifier of the asset to update. - /// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`. - /// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`. - /// - `decimals`: The number of decimals this asset uses to represent one unit. - /// - /// Emits `MetadataSet`. - /// - /// Weight: `O(N + S)` where N and S are the length of the name and symbol respectively. - #[pallet::call_index(19)] - #[pallet::weight(T::WeightInfo::force_set_metadata(name.len() as u32, symbol.len() as u32))] - pub fn force_set_metadata( - origin: OriginFor, - id: T::AssetIdParameter, - name: Vec, - symbol: Vec, - decimals: u8, - is_frozen: bool, - ) -> DispatchResult { - T::ForceOrigin::ensure_origin(origin)?; - let id: T::AssetId = id.into(); - - let bounded_name: BoundedVec = - name.clone().try_into().map_err(|_| Error::::BadMetadata)?; - - let bounded_symbol: BoundedVec = - symbol.clone().try_into().map_err(|_| Error::::BadMetadata)?; - - ensure!(Asset::::contains_key(&id), Error::::Unknown); - Metadata::::try_mutate_exists(id.clone(), |metadata| { - let deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); - *metadata = Some(AssetMetadata { - deposit, - name: bounded_name, - symbol: bounded_symbol, - decimals, - is_frozen, - }); - - Self::deposit_event(Event::MetadataSet { - asset_id: id, - name, - symbol, - decimals, - is_frozen, - }); - Ok(()) - }) - } - - /// Clear the metadata for an asset. - /// - /// Origin must be ForceOrigin. - /// - /// Any deposit is returned. - /// - /// - `id`: The identifier of the asset to clear. - /// - /// Emits `MetadataCleared`. - /// - /// Weight: `O(1)` - #[pallet::call_index(20)] - pub fn force_clear_metadata( - origin: OriginFor, - id: T::AssetIdParameter, - ) -> DispatchResult { - T::ForceOrigin::ensure_origin(origin)?; - let id: T::AssetId = id.into(); - - let d = Asset::::get(&id).ok_or(Error::::Unknown)?; - Metadata::::try_mutate_exists(id.clone(), |metadata| { - let deposit = metadata.take().ok_or(Error::::Unknown)?.deposit; - T::Currency::unreserve(&d.owner, deposit); - Self::deposit_event(Event::MetadataCleared { asset_id: id }); - Ok(()) - }) - } - - /// Alter the attributes of a given asset. - /// - /// Origin must be `ForceOrigin`. - /// - /// - `id`: The identifier of the asset. - /// - `owner`: The new Owner of this asset. - /// - `issuer`: The new Issuer of this asset. - /// - `admin`: The new Admin of this asset. - /// - `freezer`: The new Freezer of this asset. - /// - `min_balance`: The minimum balance of this new asset that any single account must - /// have. If an account's balance is reduced below this, then it collapses to zero. - /// - `is_sufficient`: Whether a non-zero balance of this asset is deposit of sufficient - /// value to account for the state bloat associated with its balance storage. If set to - /// `true`, then non-zero balances may be stored without a `consumer` reference (and thus - /// an ED in the Balances pallet or whatever else is used to control user-account state - /// growth). - /// - `is_frozen`: Whether this asset class is frozen except for permissioned/admin - /// instructions. - /// - /// Emits `AssetStatusChanged` with the identity of the asset. - /// - /// Weight: `O(1)` - #[pallet::call_index(21)] - pub fn force_asset_status( - origin: OriginFor, - id: T::AssetIdParameter, - owner: AccountIdLookupOf, - issuer: AccountIdLookupOf, - admin: AccountIdLookupOf, - freezer: AccountIdLookupOf, - #[pallet::compact] min_balance: T::Balance, - is_sufficient: bool, - is_frozen: bool, - ) -> DispatchResult { - T::ForceOrigin::ensure_origin(origin)?; - let id: T::AssetId = id.into(); - - Asset::::try_mutate(id.clone(), |maybe_asset| { - let mut asset = maybe_asset.take().ok_or(Error::::Unknown)?; - ensure!(asset.status != AssetStatus::Destroying, Error::::AssetNotLive); - asset.owner = T::Lookup::lookup(owner)?; - asset.issuer = T::Lookup::lookup(issuer)?; - asset.admin = T::Lookup::lookup(admin)?; - asset.freezer = T::Lookup::lookup(freezer)?; - asset.min_balance = min_balance; - asset.is_sufficient = is_sufficient; - if is_frozen { - asset.status = AssetStatus::Frozen; - } else { - asset.status = AssetStatus::Live; - } - *maybe_asset = Some(asset); - - Self::deposit_event(Event::AssetStatusChanged { asset_id: id }); - Ok(()) - }) - } - - /// Approve an amount of asset for transfer by a delegated third-party account. - /// - /// Origin must be Signed. - /// - /// Ensures that `ApprovalDeposit` worth of `Currency` is reserved from signing account - /// for the purpose of holding the approval. If some non-zero amount of assets is already - /// approved from signing account to `delegate`, then it is topped up or unreserved to - /// meet the right value. - /// - /// NOTE: The signing account does not need to own `amount` of assets at the point of - /// making this call. - /// - /// - `id`: The identifier of the asset. - /// - `delegate`: The account to delegate permission to transfer asset. - /// - `amount`: The amount of asset that may be transferred by `delegate`. If there is - /// already an approval in place, then this acts additively. - /// - /// Emits `ApprovedTransfer` on success. - /// - /// Weight: `O(1)` - #[pallet::call_index(22)] - pub fn approve_transfer( - origin: OriginFor, - id: T::AssetIdParameter, - delegate: AccountIdLookupOf, - #[pallet::compact] amount: T::Balance, - ) -> DispatchResult { - let owner = ensure_signed(origin)?; - let delegate = T::Lookup::lookup(delegate)?; - let id: T::AssetId = id.into(); - Self::do_approve_transfer(id, &owner, &delegate, amount) - } - - /// Cancel all of some asset approved for delegated transfer by a third-party account. - /// - /// Origin must be Signed and there must be an approval in place between signer and - /// `delegate`. - /// - /// Unreserves any deposit previously reserved by `approve_transfer` for the approval. - /// - /// - `id`: The identifier of the asset. - /// - `delegate`: The account delegated permission to transfer asset. - /// - /// Emits `ApprovalCancelled` on success. - /// - /// Weight: `O(1)` - #[pallet::call_index(23)] - pub fn cancel_approval( - origin: OriginFor, - id: T::AssetIdParameter, - delegate: AccountIdLookupOf, - ) -> DispatchResult { - let owner = ensure_signed(origin)?; - let delegate = T::Lookup::lookup(delegate)?; - let id: T::AssetId = id.into(); - let mut d = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!(d.status == AssetStatus::Live, Error::::AssetNotLive); - - let approval = Approvals::::take((id.clone(), &owner, &delegate)) - .ok_or(Error::::Unknown)?; - T::Currency::unreserve(&owner, approval.deposit); - - d.approvals.saturating_dec(); - Asset::::insert(id.clone(), d); - - Self::deposit_event(Event::ApprovalCancelled { asset_id: id, owner, delegate }); - Ok(()) - } - - /// Cancel all of some asset approved for delegated transfer by a third-party account. - /// - /// Origin must be either ForceOrigin or Signed origin with the signer being the Admin - /// account of the asset `id`. - /// - /// Unreserves any deposit previously reserved by `approve_transfer` for the approval. - /// - /// - `id`: The identifier of the asset. - /// - `delegate`: The account delegated permission to transfer asset. - /// - /// Emits `ApprovalCancelled` on success. - /// - /// Weight: `O(1)` - #[pallet::call_index(24)] - pub fn force_cancel_approval( - origin: OriginFor, - id: T::AssetIdParameter, - owner: AccountIdLookupOf, - delegate: AccountIdLookupOf, - ) -> DispatchResult { - let id: T::AssetId = id.into(); - let mut d = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!(d.status == AssetStatus::Live, Error::::AssetNotLive); - T::ForceOrigin::try_origin(origin) - .map(|_| ()) - .or_else(|origin| -> DispatchResult { - let origin = ensure_signed(origin)?; - ensure!(origin == d.admin, Error::::NoPermission); - Ok(()) - })?; - - let owner = T::Lookup::lookup(owner)?; - let delegate = T::Lookup::lookup(delegate)?; - - let approval = Approvals::::take((id.clone(), &owner, &delegate)) - .ok_or(Error::::Unknown)?; - T::Currency::unreserve(&owner, approval.deposit); - d.approvals.saturating_dec(); - Asset::::insert(id.clone(), d); - - Self::deposit_event(Event::ApprovalCancelled { asset_id: id, owner, delegate }); - Ok(()) - } - - /// Transfer some asset balance from a previously delegated account to some third-party - /// account. - /// - /// Origin must be Signed and there must be an approval in place by the `owner` to the - /// signer. - /// - /// If the entire amount approved for transfer is transferred, then any deposit previously - /// reserved by `approve_transfer` is unreserved. - /// - /// - `id`: The identifier of the asset. - /// - `owner`: The account which previously approved for a transfer of at least `amount` and - /// from which the asset balance will be withdrawn. - /// - `destination`: The account to which the asset balance of `amount` will be transferred. - /// - `amount`: The amount of assets to transfer. - /// - /// Emits `TransferredApproved` on success. - /// - /// Weight: `O(1)` - #[pallet::call_index(25)] - pub fn transfer_approved( - origin: OriginFor, - id: T::AssetIdParameter, - owner: AccountIdLookupOf, - destination: AccountIdLookupOf, - #[pallet::compact] amount: T::Balance, - ) -> DispatchResult { - let delegate = ensure_signed(origin)?; - let owner = T::Lookup::lookup(owner)?; - let destination = T::Lookup::lookup(destination)?; - let id: T::AssetId = id.into(); - Self::do_transfer_approved(id, &owner, &delegate, &destination, amount) - } - - /// Create an asset account for non-provider assets. - /// - /// A deposit will be taken from the signer account. - /// - /// - `origin`: Must be Signed; the signer account must have sufficient funds for a deposit - /// to be taken. - /// - `id`: The identifier of the asset for the account to be created. - /// - /// Emits `Touched` event when successful. - #[pallet::call_index(26)] - #[pallet::weight(T::WeightInfo::touch())] - pub fn touch(origin: OriginFor, id: T::AssetIdParameter) -> DispatchResult { - let who = ensure_signed(origin)?; - let id: T::AssetId = id.into(); - Self::do_touch(id, who.clone(), who) - } - - /// Return the deposit (if any) of an asset account or a consumer reference (if any) of an - /// account. - /// - /// The origin must be Signed. - /// - /// - `id`: The identifier of the asset for which the caller would like the deposit - /// refunded. - /// - `allow_burn`: If `true` then assets may be destroyed in order to complete the refund. - /// - /// It will fail with either [`Error::ContainsHolds`] or [`Error::ContainsFreezes`] if - /// the asset account contains holds or freezes in place. - /// - /// Emits `Refunded` event when successful. - #[pallet::call_index(27)] - #[pallet::weight(T::WeightInfo::refund())] - pub fn refund( - origin: OriginFor, - id: T::AssetIdParameter, - allow_burn: bool, - ) -> DispatchResult { - let id: T::AssetId = id.into(); - Self::do_refund(id, ensure_signed(origin)?, allow_burn) - } - - /// Sets the minimum balance of an asset. - /// - /// Only works if there aren't any accounts that are holding the asset or if - /// the new value of `min_balance` is less than the old one. - /// - /// Origin must be Signed and the sender has to be the Owner of the - /// asset `id`. - /// - /// - `id`: The identifier of the asset. - /// - `min_balance`: The new value of `min_balance`. - /// - /// Emits `AssetMinBalanceChanged` event when successful. - #[pallet::call_index(28)] - pub fn set_min_balance( - origin: OriginFor, - id: T::AssetIdParameter, - min_balance: T::Balance, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let id: T::AssetId = id.into(); - - let mut details = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!(origin == details.owner, Error::::NoPermission); - - // Reject zero min_balance to maintain the invariant enforced by create/force_create. - // A zero min_balance would allow zero-balance accounts to persist (since the reaping - // check is `balance < min_balance`), enabling consumer reference griefing attacks. - ensure!(!min_balance.is_zero(), Error::::MinBalanceZero); - - let old_min_balance = details.min_balance; - // If the asset is marked as sufficient it won't be allowed to - // change the min_balance. - ensure!(!details.is_sufficient, Error::::NoPermission); - - // Ensure that either the new min_balance is less than old - // min_balance or there aren't any accounts holding the asset. - ensure!( - min_balance < old_min_balance || details.accounts == 0, - Error::::NoPermission - ); - - details.min_balance = min_balance; - Asset::::insert(&id, details); - - Self::deposit_event(Event::AssetMinBalanceChanged { - asset_id: id, - new_min_balance: min_balance, - }); - Ok(()) - } - - /// Create an asset account for `who`. - /// - /// A deposit will be taken from the signer account. - /// - /// - `origin`: Must be Signed; the signer account must have sufficient funds for a deposit - /// to be taken. - /// - `id`: The identifier of the asset for the account to be created, the asset status must - /// be live. - /// - `who`: The account to be created. - /// - /// Emits `Touched` event when successful. - #[pallet::call_index(29)] - #[pallet::weight(T::WeightInfo::touch_other())] - pub fn touch_other( - origin: OriginFor, - id: T::AssetIdParameter, - who: AccountIdLookupOf, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let who = T::Lookup::lookup(who)?; - let id: T::AssetId = id.into(); - Self::do_touch(id, who, origin) - } - - /// Return the deposit (if any) of a target asset account. Useful if you are the depositor. - /// - /// The origin must be Signed and either the account owner, depositor, or asset `Admin`. In - /// order to burn a non-zero balance of the asset, the caller must be the account and should - /// use `refund`. - /// - /// - `id`: The identifier of the asset for the account holding a deposit. - /// - `who`: The account to refund. - /// - /// It will fail with either [`Error::ContainsHolds`] or [`Error::ContainsFreezes`] if - /// the asset account contains holds or freezes in place. - /// - /// Emits `Refunded` event when successful. - #[pallet::call_index(30)] - #[pallet::weight(T::WeightInfo::refund_other())] - pub fn refund_other( - origin: OriginFor, - id: T::AssetIdParameter, - who: AccountIdLookupOf, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let who = T::Lookup::lookup(who)?; - let id: T::AssetId = id.into(); - Self::do_refund_other(id, &who, Some(origin)) - } - - /// Disallow further unprivileged transfers of an asset `id` to and from an account `who`. - /// - /// Origin must be Signed and the sender should be the Freezer of the asset `id`. - /// - /// - `id`: The identifier of the account's asset. - /// - `who`: The account to be unblocked. - /// - /// Emits `Blocked`. - /// - /// Weight: `O(1)` - #[pallet::call_index(31)] - pub fn block( - origin: OriginFor, - id: T::AssetIdParameter, - who: AccountIdLookupOf, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - let id: T::AssetId = id.into(); - - let d = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!( - d.status == AssetStatus::Live || d.status == AssetStatus::Frozen, - Error::::IncorrectStatus - ); - ensure!(origin == d.freezer, Error::::NoPermission); - let who = T::Lookup::lookup(who)?; - - Account::::try_mutate(&id, &who, |maybe_account| -> DispatchResult { - maybe_account.as_mut().ok_or(Error::::NoAccount)?.status = - AccountStatus::Blocked; - Ok(()) - })?; - - Self::deposit_event(Event::::Blocked { asset_id: id, who }); - Ok(()) - } - - /// Transfer the entire transferable balance from the caller asset account. - /// - /// NOTE: This function only attempts to transfer _transferable_ balances. This means that - /// any held, frozen, or minimum balance (when `keep_alive` is `true`), will not be - /// transferred by this function. To ensure that this function results in a killed account, - /// you might need to prepare the account by removing any reference counters, storage - /// deposits, etc... - /// - /// The dispatch origin of this call must be Signed. - /// - /// - `id`: The identifier of the asset for the account holding a deposit. - /// - `dest`: The recipient of the transfer. - /// - `keep_alive`: A boolean to determine if the `transfer_all` operation should send all - /// of the funds the asset account has, causing the sender asset account to be killed - /// (false), or transfer everything except at least the minimum balance, which will - /// guarantee to keep the sender asset account alive (true). - #[pallet::call_index(32)] - #[pallet::weight(T::WeightInfo::transfer_all())] - pub fn transfer_all( - origin: OriginFor, - id: T::AssetIdParameter, - dest: AccountIdLookupOf, - keep_alive: bool, - ) -> DispatchResult { - let transactor = ensure_signed(origin)?; - let keep_alive = if keep_alive { Preserve } else { Expendable }; - let reducible_balance = >::reducible_balance( - id.clone().into(), - &transactor, - keep_alive, - Fortitude::Polite, - ); - let dest = T::Lookup::lookup(dest)?; - >::transfer( - id.into(), - &transactor, - &dest, - reducible_balance, - keep_alive, - )?; - Ok(()) - } - - /// Sets the trusted reserve information of an asset. - /// - /// Origin must be the Owner of the asset `id`. The origin must conform to the configured - /// `CreateOrigin` or be the signed `owner` configured during asset creation. - /// - /// - `id`: The identifier of the asset. - /// - `reserves`: The full list of trusted reserves information. - /// - /// Emits `AssetMinBalanceChanged` event when successful. - #[pallet::call_index(33)] - #[pallet::weight(T::WeightInfo::set_reserves(reserves.len() as u32))] - pub fn set_reserves( - origin: OriginFor, - id: T::AssetIdParameter, - reserves: BoundedVec>, - ) -> DispatchResult { - let id: T::AssetId = id.into(); - let origin = ensure_signed(origin.clone()) - .or_else(|_| T::CreateOrigin::ensure_origin(origin, &id))?; - - let details = Asset::::get(&id).ok_or(Error::::Unknown)?; - ensure!(origin == details.owner, Error::::NoPermission); - - Self::unchecked_update_reserves(id, reserves)?; - Ok(()) - } - } - - #[pallet::view_functions] - impl, I: 'static> Pallet { - /// Provide the asset details for asset `id`. - pub fn asset_details( - id: T::AssetId, - ) -> Option>> { - Asset::::get(id) - } - - /// Provide the balance of `who` for asset `id`. - pub fn balance_of(who: T::AccountId, id: T::AssetId) -> Option<>::Balance> { - Account::::get(id, who).map(|account| account.balance) - } - - /// Provide the configured metadata for asset `id`. - pub fn get_metadata( - id: T::AssetId, - ) -> Option, BoundedVec>> { - Metadata::::try_get(id).ok() - } - - /// Provide the configured reserves data for asset `id`. - pub fn get_reserves_data(id: T::AssetId) -> Vec { - Self::reserves(&id) - } - } - - /// Implements [`AccountTouch`] trait. - /// Note that a depositor can be any account, without any specific privilege. - impl, I: 'static> AccountTouch for Pallet { - type Balance = DepositBalanceOf; - - fn deposit_required(_: T::AssetId) -> Self::Balance { - T::AssetAccountDeposit::get() - } - - fn should_touch(asset: T::AssetId, who: &T::AccountId) -> bool { - match Asset::::get(&asset) { - // refer to the [`Self::new_account`] function for more details. - Some(info) if info.is_sufficient => false, - Some(_) if frame_system::Pallet::::can_accrue_consumers(who, 2) => false, - Some(_) => !Account::::contains_key(asset, who), - _ => true, - } - } - - fn touch( - asset: T::AssetId, - who: &T::AccountId, - depositor: &T::AccountId, - ) -> DispatchResult { - Self::do_touch(asset, who.clone(), depositor.clone()) - } - } - - /// Implements [`ContainsPair`] trait for a pair of asset and account IDs. - impl, I: 'static> ContainsPair for Pallet { - /// Check if an account with the given asset ID and account address exists. - fn contains(asset: &T::AssetId, who: &T::AccountId) -> bool { - Account::::contains_key(asset, who) - } - } - - /// Implements [`ProvideAssetReserves`] trait for getting the list of trusted reserves for a - /// given asset. - impl, I: 'static> ProvideAssetReserves for Pallet { - /// Provide the configured reserves for asset `id`. - fn reserves(id: &T::AssetId) -> Vec { - Reserves::::get(id).into_inner() - } - } -} - -sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $); diff --git a/pallets/assets/src/migration.rs b/pallets/assets/src/migration.rs deleted file mode 100644 index 9096f25f..00000000 --- a/pallets/assets/src/migration.rs +++ /dev/null @@ -1,162 +0,0 @@ -// 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. - -use super::*; -use frame_support::traits::OnRuntimeUpgrade; -use log; - -#[cfg(feature = "try-runtime")] -use sp_runtime::TryRuntimeError; - -pub mod next_asset_id { - use super::*; - use sp_core::Get; - - /// Set [`NextAssetId`] to the value of `ID` if [`NextAssetId`] does not exist yet. - pub struct SetNextAssetId, I: 'static = ()>( - core::marker::PhantomData<(ID, T, I)>, - ); - impl, I: 'static> OnRuntimeUpgrade for SetNextAssetId - where - T::AssetId: Incrementable, - ID: Get, - { - fn on_runtime_upgrade() -> frame_support::weights::Weight { - if !NextAssetId::::exists() { - NextAssetId::::put(ID::get()); - T::DbWeight::get().reads_writes(1, 1) - } else { - T::DbWeight::get().reads(1) - } - } - } -} - -pub mod v1 { - use frame_support::{pallet_prelude::*, weights::Weight}; - - use super::*; - - #[derive(Decode)] - pub struct OldAssetDetails { - pub owner: AccountId, - pub issuer: AccountId, - pub admin: AccountId, - pub freezer: AccountId, - pub supply: Balance, - pub deposit: DepositBalance, - pub min_balance: Balance, - pub is_sufficient: bool, - pub accounts: u32, - pub sufficients: u32, - pub approvals: u32, - pub is_frozen: bool, - } - - impl OldAssetDetails { - fn migrate_to_v1(self) -> AssetDetails { - let status = if self.is_frozen { AssetStatus::Frozen } else { AssetStatus::Live }; - - AssetDetails { - owner: self.owner, - issuer: self.issuer, - admin: self.admin, - freezer: self.freezer, - supply: self.supply, - deposit: self.deposit, - min_balance: self.min_balance, - is_sufficient: self.is_sufficient, - accounts: self.accounts, - sufficients: self.sufficients, - approvals: self.approvals, - status, - } - } - } - - pub struct MigrateToV1(core::marker::PhantomData); - impl OnRuntimeUpgrade for MigrateToV1 { - fn on_runtime_upgrade() -> Weight { - let in_code_version = Pallet::::in_code_storage_version(); - let on_chain_version = Pallet::::on_chain_storage_version(); - if on_chain_version == 0 && in_code_version == 1 { - let mut translated = 0u64; - Asset::::translate::< - OldAssetDetails>, - _, - >(|_key, old_value| { - translated.saturating_inc(); - Some(old_value.migrate_to_v1()) - }); - in_code_version.put::>(); - log::info!( - target: LOG_TARGET, - "Upgraded {} pools, storage to version {:?}", - translated, - in_code_version - ); - T::DbWeight::get().reads_writes(translated + 1, translated + 1) - } else { - log::info!( - target: LOG_TARGET, - "Migration did not execute. This probably should be removed" - ); - T::DbWeight::get().reads(1) - } - } - - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, TryRuntimeError> { - frame_support::ensure!( - Pallet::::on_chain_storage_version() == 0, - "must upgrade linearly" - ); - let prev_count = Asset::::iter().count(); - Ok((prev_count as u32).encode()) - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(prev_count: Vec) -> Result<(), TryRuntimeError> { - let prev_count: u32 = Decode::decode(&mut prev_count.as_slice()).expect( - "the state parameter should be something that was generated by pre_upgrade", - ); - let post_count = Asset::::iter().count() as u32; - ensure!( - prev_count == post_count, - "the asset count before and after the migration should be the same" - ); - - let in_code_version = Pallet::::in_code_storage_version(); - let on_chain_version = Pallet::::on_chain_storage_version(); - - frame_support::ensure!(in_code_version == 1, "must_upgrade"); - ensure!( - in_code_version == on_chain_version, - "after migration, the in_code_version and on_chain_version should be the same" - ); - - Asset::::iter().try_for_each(|(_id, asset)| -> Result<(), TryRuntimeError> { - ensure!( - asset.status == AssetStatus::Live || asset.status == AssetStatus::Frozen, - "assets should only be live or frozen. None should be in destroying status, or undefined state" - ); - Ok(()) - })?; - Ok(()) - } - } -} diff --git a/pallets/assets/src/mock.rs b/pallets/assets/src/mock.rs deleted file mode 100644 index 9f4080ba..00000000 --- a/pallets/assets/src/mock.rs +++ /dev/null @@ -1,245 +0,0 @@ -// 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 Assets pallet. - -use super::*; -use crate as pallet_assets; - -use codec::Encode; -use frame_support::{ - assert_ok, construct_runtime, derive_impl, parameter_types, - traits::{AsEnsureOriginWithArg, ConstU32}, -}; -use sp_io::storage; -use sp_runtime::BuildStorage; - -type Block = frame_system::mocking::MockBlock; - -construct_runtime!( - pub enum Test - { - System: frame_system, - Balances: pallet_balances, - Assets: pallet_assets, - } -); - -type AccountId = u64; -type AssetId = u32; - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for Test { - type Block = Block; - type AccountData = pallet_balances::AccountData; - type MaxConsumers = ConstU32<3>; -} - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] -impl pallet_balances::Config for Test { - type AccountStore = System; -} - -pub struct AssetsCallbackHandle; -impl AssetsCallback for AssetsCallbackHandle { - fn created(_id: &AssetId, _owner: &AccountId) -> Result<(), ()> { - if Self::should_err() { - Err(()) - } else { - storage::set(Self::CREATED.as_bytes(), &().encode()); - Ok(()) - } - } - - fn destroyed(_id: &AssetId) -> Result<(), ()> { - if Self::should_err() { - Err(()) - } else { - storage::set(Self::DESTROYED.as_bytes(), &().encode()); - Ok(()) - } - } -} - -impl AssetsCallbackHandle { - pub const CREATED: &'static str = "asset_created"; - pub const DESTROYED: &'static str = "asset_destroyed"; - - const RETURN_ERROR: &'static str = "return_error"; - - // Configures `Self` to return `Ok` when callbacks are invoked - pub fn set_return_ok() { - storage::clear(Self::RETURN_ERROR.as_bytes()); - } - - // Configures `Self` to return `Err` when callbacks are invoked - pub fn set_return_error() { - storage::set(Self::RETURN_ERROR.as_bytes(), &().encode()); - } - - // If `true`, callback should return `Err`, `Ok` otherwise. - fn should_err() -> bool { - storage::exists(Self::RETURN_ERROR.as_bytes()) - } -} - -#[derive_impl(crate::config_preludes::TestDefaultConfig)] -impl Config for Test { - type Currency = Balances; - type CreateOrigin = AsEnsureOriginWithArg>; - type ForceOrigin = frame_system::EnsureRoot; - type Freezer = TestFreezer; - type Holder = TestHolder; - type CallbackHandle = (AssetsCallbackHandle, AutoIncAssetId); - type ReserveData = u128; - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = AssetsBenchmarkHelper; -} - -#[cfg(feature = "runtime-benchmarks")] -pub struct AssetsBenchmarkHelper; -#[cfg(feature = "runtime-benchmarks")] -impl, ReserveIdParameter: From> - BenchmarkHelper for AssetsBenchmarkHelper -{ - fn create_asset_id_parameter(id: u32) -> AssetIdParameter { - id.into() - } - fn create_reserve_id_parameter(id: u32) -> ReserveIdParameter { - id.into() - } -} - -use std::collections::HashMap; - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum Hook { - Died(u32, u64), -} -parameter_types! { - static Frozen: HashMap<(u32, u64), u64> = Default::default(); - static OnHold: HashMap<(u32, u64), u64> = Default::default(); - static Hooks: Vec = Default::default(); -} - -pub struct TestHolder; -impl BalanceOnHold for TestHolder { - fn balance_on_hold(asset: u32, who: &u64) -> Option { - OnHold::get().get(&(asset, *who)).cloned() - } - - fn died(asset: u32, who: &u64) { - Hooks::mutate(|v| v.push(Hook::Died(asset, *who))) - } - - fn contains_holds(asset: AssetId) -> bool { - OnHold::get().iter().any(|((k, _), _)| &asset == k) - } -} - -pub(crate) fn set_balance_on_hold(asset: u32, who: u64, amount: u64) { - OnHold::mutate(|v| { - let amount_on_hold = v.get(&(asset, who)).unwrap_or(&0); - - if &amount > amount_on_hold { - // Hold more funds - let amount = amount - amount_on_hold; - let f = DebitFlags { keep_alive: true, best_effort: false }; - assert_ok!(Assets::decrease_balance(asset, &who, amount, f, |_, _| Ok(()))); - } else { - // Release funds on hold - let amount = amount_on_hold - amount; - assert_ok!(Assets::increase_balance(asset, &who, amount, |_| Ok(()))); - } - - // Asset amount still "exists", we just store it here - v.insert((asset, who), amount); - }); -} - -pub(crate) fn clear_balance_on_hold(asset: u32, who: u64) { - OnHold::mutate(|v| { - v.remove(&(asset, who)); - }); -} -pub struct TestFreezer; -impl FrozenBalance for TestFreezer { - fn frozen_balance(asset: u32, who: &u64) -> Option { - Frozen::get().get(&(asset, *who)).cloned() - } - - fn died(asset: u32, who: &u64) { - Hooks::mutate(|v| v.push(Hook::Died(asset, *who))); - - // Sanity check: dead accounts have no balance. - assert!(Assets::balance(asset, *who).is_zero()); - } - - /// Return a value that indicates if there are registered freezes for a given asset. - fn contains_freezes(asset: AssetId) -> bool { - Frozen::get().iter().any(|((k, _), _)| &asset == k) - } -} - -pub(crate) fn set_frozen_balance(asset: u32, who: u64, amount: u64) { - Frozen::mutate(|v| { - v.insert((asset, who), amount); - }); -} - -pub(crate) fn clear_frozen_balance(asset: u32, who: u64) { - Frozen::mutate(|v| { - v.remove(&(asset, who)); - }); -} - -pub(crate) fn hooks() -> Vec { - Hooks::get().clone() -} - -pub(crate) fn take_hooks() -> Vec { - Hooks::take() -} - -pub(crate) fn new_test_ext() -> sp_io::TestExternalities { - let mut storage = frame_system::GenesisConfig::::default().build_storage().unwrap(); - - let config: pallet_assets::GenesisConfig = pallet_assets::GenesisConfig { - assets: vec![ - // id, owner, is_sufficient, min_balance - (999, 0, true, 1), - ], - metadata: vec![ - // id, name, symbol, decimals - (999, "Token Name".into(), "TOKEN".into(), 10), - ], - accounts: vec![ - // id, account_id, balance - (999, 1, 100), - ], - next_asset_id: None, - reserves: vec![], - }; - - config.assimilate_storage(&mut storage).unwrap(); - - let mut ext: sp_io::TestExternalities = storage.into(); - // Clear thread local vars for https://github.com/paritytech/substrate/issues/10479. - ext.execute_with(|| take_hooks()); - ext.execute_with(|| System::set_block_number(1)); - ext -} diff --git a/pallets/assets/src/tests.rs b/pallets/assets/src/tests.rs deleted file mode 100644 index 9f92ae5a..00000000 --- a/pallets/assets/src/tests.rs +++ /dev/null @@ -1,2371 +0,0 @@ -// 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. - -//! Tests for Assets pallet. - -use super::*; -use crate::{mock::*, Error}; -use frame_support::{ - assert_noop, assert_ok, - dispatch::GetDispatchInfo, - traits::{ - fungibles::{InspectEnumerable, Mutate}, - tokens::{ - Preservation::{Expendable, Protect}, - Provenance, - }, - Currency, - }, - BoundedVec, -}; -use pallet_balances::Error as BalancesError; -use sp_io::storage; -use sp_runtime::{ - traits::{ConstU32, ConvertInto}, - TokenError, -}; - -mod sets; - -fn asset_ids() -> Vec { - let mut s: Vec<_> = Assets::asset_ids().collect(); - s.sort(); - s -} - -/// returns tuple of asset's account and sufficient counts -fn asset_account_counts(asset_id: u32) -> (u32, u32) { - let asset = Asset::::get(asset_id).unwrap(); - (asset.accounts, asset.sufficients) -} - -#[test] -fn transfer_should_never_burn() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - Balances::make_free_balance_be(&1, 100); - Balances::make_free_balance_be(&2, 100); - - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - - while System::inc_consumers(&2).is_ok() {} - let _ = System::dec_consumers(&2); - let _ = System::dec_consumers(&2); - // Exactly one consumer ref remaining. - assert_eq!(System::consumers(&2), 1); - - let _ = >::transfer(0, &1, &2, 50, Protect); - System::assert_has_event(RuntimeEvent::Assets(crate::Event::Transferred { - asset_id: 0, - from: 1, - to: 2, - amount: 50, - })); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(Assets::balance(0, 1) + Assets::balance(0, 2), 100); - }); -} - -#[test] -fn fungible_transfer_credits_reaped_dust() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 10)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::total_supply(0), 100); - - // An expendable transfer that reaps the source debits the full balance, not just the - // requested amount. The generic `fungibles::Mutate::transfer` path must credit that - // actual debit or total issuance no longer matches the sum of account balances. - assert_ok!(>::transfer(0, &1, &2, 91, Expendable)); - assert!(Assets::maybe_balance(0, 1).is_none()); - assert_eq!(Assets::balance(0, 2), 100); - assert_eq!(Assets::total_supply(0), 100); - }); -} - -#[test] -fn transfer_approved_burns_reaping_dust() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 10)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 2); - assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 91)); - - // Reaping dust must be burned, not credited to the delegate's destination beyond the - // approved amount. - assert_ok!(Assets::transfer_approved(RuntimeOrigin::signed(2), 0, 1, 3, 91)); - assert!(Assets::maybe_balance(0, 1).is_none()); - assert_eq!(Assets::balance(0, 3), 91); - assert_eq!(Assets::total_supply(0), 91); - }); -} - -#[test] -fn basic_minting_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 1, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - System::assert_last_event(RuntimeEvent::Assets(crate::Event::Issued { - asset_id: 0, - owner: 1, - amount: 100, - })); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 2, 100)); - System::assert_last_event(RuntimeEvent::Assets(crate::Event::Issued { - asset_id: 0, - owner: 2, - amount: 100, - })); - assert_eq!(Assets::balance(0, 2), 100); - assert_eq!(asset_ids(), vec![0, 1, 999]); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100)); - System::assert_last_event(RuntimeEvent::Assets(crate::Event::Issued { - asset_id: 1, - owner: 1, - amount: 100, - })); - assert_eq!(Assets::account_balances(1), vec![(0, 100), (999, 100), (1, 100)]); - }); -} - -#[test] -fn minting_too_many_insufficient_assets_fails() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 1, 1, false, 1)); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 2, 1, false, 1)); - Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100)); - assert_noop!(Assets::mint(RuntimeOrigin::signed(1), 2, 1, 100), TokenError::CannotCreate); - - Balances::make_free_balance_be(&2, 1); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 100)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 2, 1, 100)); - assert_eq!(asset_ids(), vec![0, 1, 2, 999]); - }); -} - -#[test] -fn minting_insufficient_asset_with_deposit_should_work_when_consumers_exhausted() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 1, 1, false, 1)); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 2, 1, false, 1)); - Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100)); - assert_noop!(Assets::mint(RuntimeOrigin::signed(1), 2, 1, 100), TokenError::CannotCreate); - - assert_ok!(Assets::touch(RuntimeOrigin::signed(1), 2)); - assert_eq!(Balances::reserved_balance(&1), 10); - - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 2, 1, 100)); - }); -} - -#[test] -fn minting_insufficient_assets_with_deposit_without_consumer_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - assert_noop!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100), TokenError::CannotCreate); - Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::touch(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Balances::reserved_balance(&1), 10); - assert_eq!(System::consumers(&1), 1); - }); -} - -#[test] -fn refunding_asset_deposit_with_burn_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::touch(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_ok!(Assets::refund(RuntimeOrigin::signed(1), 0, true)); - assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(Assets::balance(1, 0), 0); - }); -} - -#[test] -fn refunding_asset_deposit_with_burn_disallowed_should_fail() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::touch(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_noop!(Assets::refund(RuntimeOrigin::signed(1), 0, false), Error::::WouldBurn); - }); -} - -#[test] -fn refunding_asset_deposit_without_burn_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - assert_noop!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100), TokenError::CannotCreate); - Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::touch(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&2, 100); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 100)); - assert_eq!(Assets::balance(0, 2), 100); - assert_eq!(Assets::balance(0, 1), 0); - assert_eq!(Balances::reserved_balance(&1), 10); - assert_eq!(asset_account_counts(0), (2, 0)); - assert_ok!(Assets::refund(RuntimeOrigin::signed(1), 0, false)); - assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(Assets::balance(1, 0), 0); - assert_eq!(asset_account_counts(0), (1, 0)); - }); -} - -/// Refunding reaps an account and calls the `FrozenBalance::died` hook. -#[test] -fn refunding_calls_died_hook() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::touch(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_ok!(Assets::refund(RuntimeOrigin::signed(1), 0, true)); - - assert_eq!(Asset::::get(0).unwrap().accounts, 0); - assert_eq!( - hooks(), - vec![ - Hook::Died(0, 1), - // Note: Hooks get called twice because the hook is called from `Holder` AND - // `Freezer`. - Hook::Died(0, 1) - ] - ); - assert_eq!(asset_ids(), vec![0, 999]); - }); -} - -#[test] -fn refunding_with_sufficient_existence_reason_should_fail() { - new_test_ext().execute_with(|| { - // create sufficient asset - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - // create an asset account with sufficient existence reason - // by transferring some sufficient assets - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(Assets::balance(0, 2), 50); - assert_eq!(asset_account_counts(0), (2, 2)); - // fails to refund - assert_noop!(Assets::refund(RuntimeOrigin::signed(2), 0, true), Error::::NoDeposit); - }); -} - -#[test] -fn refunding_with_deposit_from_should_fail() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - Balances::make_free_balance_be(&1, 100); - // create asset account `2` with deposit from `1` - assert_ok!(Assets::touch_other(RuntimeOrigin::signed(1), 0, 2)); - assert_eq!(Balances::reserved_balance(&1), 10); - // fails to refund - assert_noop!(Assets::refund(RuntimeOrigin::signed(2), 0, true), Error::::NoDeposit); - assert!(Account::::contains_key(0, &2)); - }); -} - -#[test] -fn refunding_frozen_with_consumer_ref_works() { - new_test_ext().execute_with(|| { - // 1 will be an admin - // 2 will be a frozen account - Balances::make_free_balance_be(&1, 100); - Balances::make_free_balance_be(&2, 100); - // create non-sufficient asset - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(System::consumers(&2), 0); - // create asset account `2` with a consumer reference by transferring - // non-sufficient funds into - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - assert_eq!(System::consumers(&2), 1); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(Assets::balance(0, 2), 50); - assert_eq!(asset_account_counts(0), (2, 0)); - // freeze asset account `2` and asset `0` - assert_ok!(Assets::freeze(RuntimeOrigin::signed(1), 0, 2)); - assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0)); - // refund works - assert_ok!(Assets::refund(RuntimeOrigin::signed(2), 0, true)); - assert!(!Account::::contains_key(0, &2)); - assert_eq!(System::consumers(&2), 0); - assert_eq!(asset_account_counts(0), (1, 0)); - }); -} - -#[test] -fn refunding_frozen_with_deposit_works() { - new_test_ext().execute_with(|| { - // 1 will be an asset admin - // 2 will be a frozen account - Balances::make_free_balance_be(&1, 100); - Balances::make_free_balance_be(&2, 100); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(System::consumers(&2), 0); - assert_ok!(Assets::touch(RuntimeOrigin::signed(2), 0)); - // reserve deposit holds one consumer ref - assert_eq!(System::consumers(&2), 1); - assert_eq!(Balances::reserved_balance(&2), 10); - assert!(Account::::contains_key(0, &2)); - // transfer some assets to `2` - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - assert_eq!(System::consumers(&2), 1); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(Assets::balance(0, 2), 50); - assert_eq!(asset_account_counts(0), (2, 0)); - // ensure refundable even if asset account and asset is frozen - assert_ok!(Assets::freeze(RuntimeOrigin::signed(1), 0, 2)); - assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0)); - // success - assert_ok!(Assets::refund(RuntimeOrigin::signed(2), 0, true)); - assert!(!Account::::contains_key(0, &2)); - assert_eq!(Balances::reserved_balance(&2), 0); - assert_eq!(System::consumers(&2), 0); - assert_eq!(asset_account_counts(0), (1, 0)); - }); -} - -#[test] -fn approval_lifecycle_works() { - new_test_ext().execute_with(|| { - // can't approve non-existent token - assert_noop!( - Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50), - Error::::Unknown - ); - // so we create it :) - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 2); - assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - assert_eq!(Asset::::get(0).unwrap().approvals, 1); - assert_eq!(Balances::reserved_balance(&1), 1); - assert_ok!(Assets::transfer_approved(RuntimeOrigin::signed(2), 0, 1, 3, 40)); - assert_eq!(Asset::::get(0).unwrap().approvals, 1); - assert_ok!(Assets::cancel_approval(RuntimeOrigin::signed(1), 0, 2)); - assert_eq!(Asset::::get(0).unwrap().approvals, 0); - assert_eq!(Assets::balance(0, 1), 60); - assert_eq!(Assets::balance(0, 3), 40); - assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(asset_ids(), vec![0, 999]); - }); -} - -#[test] -fn transfer_approved_all_funds() { - new_test_ext().execute_with(|| { - // can't approve non-existent token - assert_noop!( - Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50), - Error::::Unknown - ); - // so we create it :) - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 2); - assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - assert_eq!(Asset::::get(0).unwrap().approvals, 1); - assert_eq!(Balances::reserved_balance(&1), 1); - - // transfer the full amount, which should trigger auto-cleanup - assert_ok!(Assets::transfer_approved(RuntimeOrigin::signed(2), 0, 1, 3, 50)); - assert_eq!(Asset::::get(0).unwrap().approvals, 0); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(Assets::balance(0, 3), 50); - assert_eq!(Balances::reserved_balance(&1), 0); - }); -} - -#[test] -fn approval_deposits_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - let e = BalancesError::::InsufficientBalance; - assert_noop!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50), e); - - Balances::make_free_balance_be(&1, 2); - assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - assert_eq!(Balances::reserved_balance(&1), 1); - - assert_ok!(Assets::transfer_approved(RuntimeOrigin::signed(2), 0, 1, 3, 50)); - assert_eq!(Balances::reserved_balance(&1), 0); - - assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - assert_ok!(Assets::cancel_approval(RuntimeOrigin::signed(1), 0, 2)); - assert_eq!(Balances::reserved_balance(&1), 0); - }); -} - -#[test] -fn cannot_transfer_more_than_approved() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 2); - assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - let e = Error::::Unapproved; - assert_noop!(Assets::transfer_approved(RuntimeOrigin::signed(2), 0, 1, 3, 51), e); - }); -} - -#[test] -fn cannot_transfer_more_than_exists() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 2); - assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 101)); - let e = Error::::BalanceLow; - assert_noop!(Assets::transfer_approved(RuntimeOrigin::signed(2), 0, 1, 3, 101), e); - }); -} - -#[test] -fn cancel_approval_works() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 2); - assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - assert_eq!(Asset::::get(0).unwrap().approvals, 1); - assert_noop!( - Assets::cancel_approval(RuntimeOrigin::signed(1), 1, 2), - Error::::Unknown - ); - assert_noop!( - Assets::cancel_approval(RuntimeOrigin::signed(2), 0, 2), - Error::::Unknown - ); - assert_noop!( - Assets::cancel_approval(RuntimeOrigin::signed(1), 0, 3), - Error::::Unknown - ); - assert_eq!(Asset::::get(0).unwrap().approvals, 1); - assert_ok!(Assets::cancel_approval(RuntimeOrigin::signed(1), 0, 2)); - assert_eq!(Asset::::get(0).unwrap().approvals, 0); - assert_noop!( - Assets::cancel_approval(RuntimeOrigin::signed(1), 0, 2), - Error::::Unknown - ); - }); -} - -#[test] -fn force_cancel_approval_works() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 2); - assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - assert_eq!(Asset::::get(0).unwrap().approvals, 1); - let e = Error::::NoPermission; - assert_noop!(Assets::force_cancel_approval(RuntimeOrigin::signed(2), 0, 1, 2), e); - assert_noop!( - Assets::force_cancel_approval(RuntimeOrigin::signed(1), 1, 1, 2), - Error::::Unknown - ); - assert_noop!( - Assets::force_cancel_approval(RuntimeOrigin::signed(1), 0, 2, 2), - Error::::Unknown - ); - assert_noop!( - Assets::force_cancel_approval(RuntimeOrigin::signed(1), 0, 1, 3), - Error::::Unknown - ); - assert_eq!(Asset::::get(0).unwrap().approvals, 1); - assert_ok!(Assets::force_cancel_approval(RuntimeOrigin::signed(1), 0, 1, 2)); - assert_eq!(Asset::::get(0).unwrap().approvals, 0); - assert_noop!( - Assets::force_cancel_approval(RuntimeOrigin::signed(1), 0, 1, 2), - Error::::Unknown - ); - }); -} - -#[test] -fn lifecycle_should_work() { - new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::create(RuntimeOrigin::signed(1), 0, 1, 1)); - assert_eq!(Balances::reserved_balance(&1), 1); - assert!(Asset::::contains_key(0)); - - assert_ok!(Assets::set_metadata(RuntimeOrigin::signed(1), 0, vec![0], vec![0], 12)); - assert_eq!(Balances::reserved_balance(&1), 4); - assert!(Metadata::::contains_key(0)); - assert_ok!(Assets::set_reserves( - RuntimeOrigin::signed(1), - 0, - vec![1234].try_into().unwrap() - )); - assert_eq!(Reserves::::get(0), vec![1234]); - Balances::make_free_balance_be(&10, 100); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 10, 100)); - Balances::make_free_balance_be(&20, 100); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 20, 100)); - assert_eq!(Account::::iter_prefix(0).count(), 2); - - assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0)); - - assert_eq!(Balances::reserved_balance(&1), 0); - - assert!(!Asset::::contains_key(0)); - assert!(!Metadata::::contains_key(0)); - assert_eq!(Account::::iter_prefix(0).count(), 0); - - assert_ok!(Assets::create(RuntimeOrigin::signed(1), 0, 1, 1)); - assert_eq!(Balances::reserved_balance(&1), 1); - assert!(Asset::::contains_key(0)); - - assert_ok!(Assets::set_metadata(RuntimeOrigin::signed(1), 0, vec![0], vec![0], 12)); - assert_eq!(Balances::reserved_balance(&1), 4); - assert!(Metadata::::contains_key(0)); - - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 10, 100)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 20, 100)); - assert_eq!(Account::::iter_prefix(0).count(), 2); - - assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0)); - - assert_eq!(Balances::reserved_balance(&1), 0); - - assert!(!Asset::::contains_key(0)); - assert!(!Metadata::::contains_key(0)); - assert_eq!(Account::::iter_prefix(0).count(), 0); - }); -} - -#[test] -fn destroy_should_refund_approvals() { - new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 10, 100)); - assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 3, 50)); - assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 4, 50)); - assert_eq!(Balances::reserved_balance(&1), 3); - assert_eq!(asset_ids(), vec![0, 999]); - - assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0)); - - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0)); - - assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(asset_ids(), vec![999]); - - // all approvals are removed - assert!(Approvals::::iter().count().is_zero()) - }); -} - -#[test] -fn partial_destroy_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 10)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 2, 10)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 3, 10)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 4, 10)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 5, 10)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 6, 10)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 7, 10)); - assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0)); - - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0)); - // Asset is in use, as all the accounts have not yet been destroyed. - // We need to call destroy_accounts or destroy_approvals again until asset is completely - // cleaned up. - assert_noop!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0), Error::::InUse); - - System::assert_has_event(RuntimeEvent::Assets(crate::Event::AccountsDestroyed { - asset_id: 0, - accounts_destroyed: 5, - accounts_remaining: 2, - })); - System::assert_has_event(RuntimeEvent::Assets(crate::Event::ApprovalsDestroyed { - asset_id: 0, - approvals_destroyed: 0, - approvals_remaining: 0, - })); - // Partially destroyed Asset should continue to exist - assert!(Asset::::contains_key(0)); - - // Second call to destroy on PartiallyDestroyed asset - assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0)); - System::assert_has_event(RuntimeEvent::Assets(crate::Event::AccountsDestroyed { - asset_id: 0, - accounts_destroyed: 2, - accounts_remaining: 0, - })); - assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0)); - - System::assert_has_event(RuntimeEvent::Assets(crate::Event::Destroyed { asset_id: 0 })); - - // Destroyed Asset should not exist - assert!(!Asset::::contains_key(0)); - }) -} - -#[test] -fn non_providing_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - - Balances::make_free_balance_be(&0, 100); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 0, 100)); - - // Cannot mint into account 2 since it doesn't (yet) exist... - assert_noop!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100), TokenError::CannotCreate); - // ...or transfer... - assert_noop!( - Assets::transfer(RuntimeOrigin::signed(0), 0, 1, 50), - TokenError::CannotCreate - ); - // ...or force-transfer - assert_noop!( - Assets::force_transfer(RuntimeOrigin::signed(1), 0, 0, 1, 50), - TokenError::CannotCreate - ); - - Balances::make_free_balance_be(&1, 100); - Balances::make_free_balance_be(&2, 100); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(0), 0, 1, 25)); - assert_ok!(Assets::force_transfer(RuntimeOrigin::signed(1), 0, 0, 2, 25)); - assert_eq!(asset_ids(), vec![0, 999]); - }); -} - -#[test] -fn min_balance_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 10)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Asset::::get(0).unwrap().accounts, 1); - - // Cannot create a new account with a balance that is below minimum... - assert_noop!(Assets::mint(RuntimeOrigin::signed(1), 0, 2, 9), TokenError::BelowMinimum); - assert_noop!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 9), TokenError::BelowMinimum); - assert_noop!( - Assets::force_transfer(RuntimeOrigin::signed(1), 0, 1, 2, 9), - TokenError::BelowMinimum - ); - - // When deducting from an account to below minimum, it should be reaped. - // Death by `transfer`. - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 91)); - assert!(Assets::maybe_balance(0, 1).is_none()); - assert_eq!(Assets::balance(0, 2), 100); - assert_eq!(Asset::::get(0).unwrap().accounts, 1); - assert_eq!( - take_hooks(), - vec![ - Hook::Died(0, 1), - // Note: Hooks get called twice because the hook is called from `Holder` AND - // `Freezer`. - Hook::Died(0, 1) - ] - ); - - // Death by `force_transfer`. - assert_ok!(Assets::force_transfer(RuntimeOrigin::signed(1), 0, 2, 1, 91)); - assert!(Assets::maybe_balance(0, 2).is_none()); - assert_eq!(Assets::balance(0, 1), 100); - assert_eq!(Asset::::get(0).unwrap().accounts, 1); - assert_eq!( - take_hooks(), - vec![ - Hook::Died(0, 2), - // Note: Hooks get called twice because the hook is called from `Holder` AND - // `Freezer`. - Hook::Died(0, 2) - ] - ); - - // Death by `burn`. - assert_ok!(Assets::burn(RuntimeOrigin::signed(1), 0, 1, 91)); - assert!(Assets::maybe_balance(0, 1).is_none()); - assert_eq!(Asset::::get(0).unwrap().accounts, 0); - assert_eq!( - take_hooks(), - vec![ - Hook::Died(0, 1), - // Note: Hooks get called twice because the hook is called from `Holder` AND - // `Freezer`. - Hook::Died(0, 1) - ] - ); - - // Death by `transfer_approved`. - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 2); - assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 100)); - assert_ok!(Assets::transfer_approved(RuntimeOrigin::signed(2), 0, 1, 3, 91)); - assert_eq!( - take_hooks(), - vec![ - Hook::Died(0, 1), - // Note: Hooks get called twice because the hook is called from `Holder` AND - // `Freezer`. - Hook::Died(0, 1) - ] - ); - }); -} - -#[test] -fn querying_total_supply_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(Assets::balance(0, 2), 50); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(2), 0, 3, 31)); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(Assets::balance(0, 2), 19); - assert_eq!(Assets::balance(0, 3), 31); - assert_ok!(Assets::burn(RuntimeOrigin::signed(1), 0, 3, u64::MAX)); - assert_eq!(Assets::total_supply(0), 69); - }); -} - -#[test] -fn transferring_amount_below_available_balance_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(Assets::balance(0, 2), 50); - }); -} - -#[test] -fn transferring_enough_to_kill_source_when_keep_alive_should_fail() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 10)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_noop!( - Assets::transfer_keep_alive(RuntimeOrigin::signed(1), 0, 2, 91), - Error::::BalanceLow - ); - assert_ok!(Assets::transfer_keep_alive(RuntimeOrigin::signed(1), 0, 2, 90)); - assert_eq!(Assets::balance(0, 1), 10); - assert_eq!(Assets::balance(0, 2), 90); - assert!(hooks().is_empty()); - assert_eq!(asset_ids(), vec![0, 999]); - }); -} - -#[test] -fn transferring_frozen_user_should_not_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::freeze(RuntimeOrigin::signed(1), 0, 1)); - assert_noop!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50), Error::::Frozen); - assert_ok!(Assets::thaw(RuntimeOrigin::signed(1), 0, 1)); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - }); -} - -#[test] -fn transferring_frozen_asset_should_not_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0)); - assert_noop!( - Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50), - Error::::AssetNotLive - ); - assert_ok!(Assets::thaw_asset(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - }); -} - -#[test] -fn approve_transfer_frozen_asset_should_not_work() { - new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0)); - assert_noop!( - Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50), - Error::::AssetNotLive - ); - assert_ok!(Assets::thaw_asset(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - }); -} - -#[test] -fn transferring_from_blocked_account_should_not_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::block(RuntimeOrigin::signed(1), 0, 1)); - // behaves as frozen when transferring from blocked - assert_noop!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50), Error::::Frozen); - assert_ok!(Assets::thaw(RuntimeOrigin::signed(1), 0, 1)); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 50)); - }); -} - -#[test] -fn transferring_to_blocked_account_should_not_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 2, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_eq!(Assets::balance(0, 2), 100); - assert_ok!(Assets::block(RuntimeOrigin::signed(1), 0, 1)); - assert_noop!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 50), TokenError::Blocked); - assert_ok!(Assets::thaw(RuntimeOrigin::signed(1), 0, 1)); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 50)); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - }); -} - -#[test] -fn transfer_all_works_1() { - new_test_ext().execute_with(|| { - // setup - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 0, true, 100)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(0), 0, 1, 200)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(0), 0, 2, 100)); - // transfer all and allow death - assert_ok!(Assets::transfer_all(Some(1).into(), 0, 2, false)); - assert_eq!(Assets::balance(0, &1), 0); - assert_eq!(Assets::balance(0, &2), 300); - }); -} - -#[test] -fn transfer_all_works_2() { - new_test_ext().execute_with(|| { - // setup - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 0, true, 100)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(0), 0, 1, 200)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(0), 0, 2, 100)); - // transfer all and allow death - assert_ok!(Assets::transfer_all(Some(1).into(), 0, 2, true)); - assert_eq!(Assets::balance(0, &1), 100); - assert_eq!(Assets::balance(0, &2), 200); - }); -} - -#[test] -fn transfer_all_works_3() { - new_test_ext().execute_with(|| { - // setup - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 0, true, 100)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(0), 0, 1, 210)); - set_frozen_balance(0, 1, 10); - assert_ok!(Assets::mint(RuntimeOrigin::signed(0), 0, 2, 100)); - // transfer all and allow death w/ frozen - assert_ok!(Assets::transfer_all(Some(1).into(), 0, 2, false)); - assert_eq!(Assets::balance(0, &1), 100); - assert_eq!(Assets::balance(0, &2), 210); - }); -} - -#[test] -fn origin_guards_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_noop!( - Assets::transfer_ownership(RuntimeOrigin::signed(2), 0, 2), - Error::::NoPermission - ); - assert_noop!( - Assets::set_team(RuntimeOrigin::signed(2), 0, 2, 2, 2), - Error::::NoPermission - ); - assert_noop!(Assets::freeze(RuntimeOrigin::signed(2), 0, 1), Error::::NoPermission); - assert_noop!(Assets::thaw(RuntimeOrigin::signed(2), 0, 2), Error::::NoPermission); - assert_noop!( - Assets::mint(RuntimeOrigin::signed(2), 0, 2, 100), - Error::::NoPermission - ); - assert_noop!( - Assets::burn(RuntimeOrigin::signed(2), 0, 1, 100), - Error::::NoPermission - ); - assert_noop!( - Assets::force_transfer(RuntimeOrigin::signed(2), 0, 1, 2, 100), - Error::::NoPermission - ); - assert_noop!( - Assets::start_destroy(RuntimeOrigin::signed(2), 0), - Error::::NoPermission - ); - }); -} - -#[test] -fn transfer_owner_should_work() { - new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); - Balances::make_free_balance_be(&2, 100); - assert_ok!(Assets::create(RuntimeOrigin::signed(1), 0, 1, 1)); - assert_eq!(asset_ids(), vec![0, 999]); - - assert_eq!(Balances::reserved_balance(&1), 1); - - assert_ok!(Assets::transfer_ownership(RuntimeOrigin::signed(1), 0, 2)); - assert_eq!(Balances::reserved_balance(&2), 1); - assert_eq!(Balances::reserved_balance(&1), 0); - - assert_noop!( - Assets::transfer_ownership(RuntimeOrigin::signed(1), 0, 1), - Error::::NoPermission - ); - - // Set metadata now and make sure that deposit gets transferred back. - assert_ok!(Assets::set_metadata( - RuntimeOrigin::signed(2), - 0, - vec![0u8; 10], - vec![0u8; 10], - 12 - )); - assert_ok!(Assets::transfer_ownership(RuntimeOrigin::signed(2), 0, 1)); - assert_eq!(Balances::reserved_balance(&1), 22); - assert_eq!(Balances::reserved_balance(&2), 0); - }); -} - -#[test] -fn set_team_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::set_team(RuntimeOrigin::signed(1), 0, 2, 3, 4)); - - assert_ok!(Assets::mint(RuntimeOrigin::signed(2), 0, 2, 100)); - assert_ok!(Assets::freeze(RuntimeOrigin::signed(4), 0, 2)); - assert_ok!(Assets::thaw(RuntimeOrigin::signed(3), 0, 2)); - assert_ok!(Assets::force_transfer(RuntimeOrigin::signed(3), 0, 2, 3, 100)); - assert_ok!(Assets::burn(RuntimeOrigin::signed(3), 0, 3, 100)); - }); -} - -#[test] -fn transferring_from_frozen_account_should_not_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 2, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_eq!(Assets::balance(0, 2), 100); - assert_ok!(Assets::freeze(RuntimeOrigin::signed(1), 0, 2)); - // can transfer to `2` - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - // cannot transfer from `2` - assert_noop!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 25), Error::::Frozen); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(Assets::balance(0, 2), 150); - }); -} - -#[test] -fn touching_and_freezing_account_with_zero_asset_balance_should_work() { - new_test_ext().execute_with(|| { - // need some deposit for the touch - Balances::make_free_balance_be(&2, 100); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_eq!(Assets::balance(0, 2), 0); - // cannot freeze an account that doesn't have an `Assets` entry - assert_noop!(Assets::freeze(RuntimeOrigin::signed(1), 0, 2), Error::::NoAccount); - assert_ok!(Assets::touch(RuntimeOrigin::signed(2), 0)); - // now it can be frozen - assert_ok!(Assets::freeze(RuntimeOrigin::signed(1), 0, 2)); - // can transfer to `2` even though its frozen - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - // cannot transfer from `2` - assert_noop!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 25), Error::::Frozen); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(Assets::balance(0, 2), 50); - }); -} - -// In the past only admin and freezer could call `touch_other`. -// Test that this behavior is still supported. -#[test] -fn touch_other_works_legacy() { - new_test_ext().execute_with(|| { - // 1 will be admin - // 2 will be freezer - // 4 will be an account successfully attempting to execute `touch_other` - Balances::make_free_balance_be(&1, 100); - Balances::make_free_balance_be(&2, 100); - Balances::make_free_balance_be(&4, 100); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - assert_ok!(Assets::set_team(RuntimeOrigin::signed(1), 0, 1, 1, 2)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - // account `3` does not exist - assert!(!Account::::contains_key(0, &3)); - // creation of asset account `30` by account `4` works - assert_ok!(Assets::touch_other(RuntimeOrigin::signed(4), 0, 30)); - // creation of asset account `3` by admin `1` works - assert!(!Account::::contains_key(0, &3)); - assert_ok!(Assets::touch_other(RuntimeOrigin::signed(1), 0, 3)); - assert!(Account::::contains_key(0, &3)); - // creation of asset account `4` by freezer `2` works - assert!(!Account::::contains_key(0, &4)); - assert_ok!(Assets::touch_other(RuntimeOrigin::signed(2), 0, 4)); - }); -} - -#[test] -fn touch_other_works() { - new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&3, 100); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 3, 100)); - assert_eq!(Assets::balance(0, 3), 100); - - // account `4` does not exist - assert!(!Account::::contains_key(0, &4)); - - // creation of asset account `4` by funded account `3` works - assert_ok!(Assets::touch_other(RuntimeOrigin::signed(3), 0, 4)); - assert!(Account::::contains_key(0, &4)); - - // account `6` does not exist - assert!(!Account::::contains_key(0, &6)); - - // creation of asset account `6` by not funded account `5` fails - assert_noop!( - Assets::touch_other(RuntimeOrigin::signed(5), 0, 6), - BalancesError::::InsufficientBalance, - ); - assert!(!Account::::contains_key(0, &6)); - }); -} - -#[test] -fn touch_other_and_freeze_works() { - new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - // account `2` does not exist - assert!(!Account::::contains_key(0, &2)); - // create account `2` with touch_other - assert_ok!(Assets::touch_other(RuntimeOrigin::signed(1), 0, 2)); - assert!(Account::::contains_key(0, &2)); - // now it can be frozen - assert_ok!(Assets::freeze(RuntimeOrigin::signed(1), 0, 2)); - // can transfer to `2` even though its frozen - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - // cannot transfer from `2` - assert_noop!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 25), Error::::Frozen); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(Assets::balance(0, 2), 50); - }); -} - -#[test] -fn account_with_deposit_not_destroyed() { - new_test_ext().execute_with(|| { - // 1 will be the asset admin - // 2 will exist without balance but with deposit - Balances::make_free_balance_be(&1, 100); - Balances::make_free_balance_be(&2, 100); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_eq!(Assets::balance(0, 2), 0); - // case 1; account `2` not destroyed with a holder's deposit - assert_ok!(Assets::touch(RuntimeOrigin::signed(2), 0)); - assert_eq!(Balances::reserved_balance(&2), 10); - assert!(Account::::contains_key(0, &2)); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 50)); - assert_eq!(Assets::balance(0, 2), 0); - assert!(Account::::contains_key(0, &2)); - - // destroy account `2` - assert_ok!(Assets::refund(RuntimeOrigin::signed(2), 0, false)); - assert!(!Account::::contains_key(0, &2)); - - // case 2; account `2` not destroyed with a deposit from `1` - assert_ok!(Assets::touch_other(RuntimeOrigin::signed(1), 0, 2)); - assert_eq!(Balances::reserved_balance(&1), 10); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 50)); - assert!(Account::::contains_key(0, &2)); - }); -} - -#[test] -fn refund_other_should_fails() { - new_test_ext().execute_with(|| { - // 1 will be the asset admin - // 2 will be the asset freezer - // 3 will be created with deposit of 2 - Balances::make_free_balance_be(&1, 100); - Balances::make_free_balance_be(&2, 100); - Balances::make_free_balance_be(&3, 0); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::set_team(RuntimeOrigin::signed(1), 0, 1, 1, 2)); - assert!(!Account::::contains_key(0, &3)); - - // create asset account `3` with a deposit from freezer `2` - assert_ok!(Assets::touch_other(RuntimeOrigin::signed(2), 0, 3)); - assert_eq!(Balances::reserved_balance(&2), 10); - - // fail case; non-existing asset account `10` - assert_noop!( - Assets::refund_other(RuntimeOrigin::signed(2), 0, 10), - Error::::NoDeposit - ); - // fail case; non-existing asset `3` - assert_noop!( - Assets::refund_other(RuntimeOrigin::signed(2), 1, 3), - Error::::NoDeposit - ); - // fail case; no `DepositFrom` for asset account `1` - assert_noop!( - Assets::refund_other(RuntimeOrigin::signed(2), 0, 1), - Error::::NoDeposit - ); - // fail case; asset `0` is frozen - assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(2), 0)); - assert_noop!( - Assets::refund_other(RuntimeOrigin::signed(2), 0, 3), - Error::::AssetNotLive - ); - assert_ok!(Assets::thaw_asset(RuntimeOrigin::signed(1), 0)); - // fail case; asset `1` is being destroyed - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 10, 1, true, 1)); - assert_ok!(Assets::touch_other(RuntimeOrigin::signed(1), 10, 3)); - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 10)); - assert_noop!( - Assets::refund_other(RuntimeOrigin::signed(2), 10, 3), - Error::::AssetNotLive - ); - assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 10)); - assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 10)); - // fail case; account is frozen - assert_ok!(Assets::freeze(RuntimeOrigin::signed(2), 0, 3)); - assert_noop!(Assets::refund_other(RuntimeOrigin::signed(2), 0, 3), Error::::Frozen); - assert_ok!(Assets::thaw(RuntimeOrigin::signed(1), 0, 3)); - // fail case; not a freezer or an admin - assert_noop!( - Assets::refund_other(RuntimeOrigin::signed(4), 0, 3), - Error::::NoPermission - ); - // fail case; would burn - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 3, 100)); - assert_noop!( - Assets::refund_other(RuntimeOrigin::signed(1), 0, 3), - Error::::WouldBurn - ); - assert_ok!(Assets::burn(RuntimeOrigin::signed(1), 0, 3, 100)); - }) -} - -#[test] -fn refund_other_works() { - new_test_ext().execute_with(|| { - // 1 will be the asset admin - // 2 will be the asset freezer - // 3 will be created with deposit of 2 - Balances::make_free_balance_be(&1, 100); - Balances::make_free_balance_be(&2, 100); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::set_team(RuntimeOrigin::signed(1), 0, 1, 1, 2)); - assert!(!Account::::contains_key(0, &3)); - assert_eq!(asset_account_counts(0), (0, 0)); - - // success case; freezer is depositor - assert_ok!(Assets::touch_other(RuntimeOrigin::signed(2), 0, 3)); - assert_eq!(Balances::reserved_balance(&2), 10); - assert_eq!(asset_account_counts(0), (1, 0)); - assert_ok!(Assets::refund_other(RuntimeOrigin::signed(2), 0, 3)); - assert_eq!(Balances::reserved_balance(&2), 0); - assert!(!Account::::contains_key(0, &3)); - assert_eq!(asset_account_counts(0), (0, 0)); - - // success case; admin is depositor - assert_ok!(Assets::touch_other(RuntimeOrigin::signed(1), 0, 3)); - assert_eq!(Balances::reserved_balance(&1), 10); - assert_eq!(asset_account_counts(0), (1, 0)); - assert_ok!(Assets::refund_other(RuntimeOrigin::signed(1), 0, 3)); - assert_eq!(Balances::reserved_balance(&1), 0); - assert!(!Account::::contains_key(0, &3)); - assert_eq!(asset_account_counts(0), (0, 0)); - }) -} - -#[test] -fn transferring_amount_more_than_available_balance_should_not_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(Assets::balance(0, 2), 50); - assert_ok!(Assets::burn(RuntimeOrigin::signed(1), 0, 1, u64::MAX)); - assert_eq!(Assets::balance(0, 1), 0); - assert_noop!( - Assets::transfer(RuntimeOrigin::signed(1), 0, 1, 50), - Error::::NoAccount - ); - assert_noop!( - Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 51), - Error::::BalanceLow - ); - }); -} - -#[test] -fn transferring_less_than_one_unit_is_fine() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 0)); - // `ForceCreated` and `Issued` but no `Transferred` event. - assert_eq!(System::events().len(), 2); - }); -} - -#[test] -fn transferring_more_units_than_total_supply_should_not_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_noop!( - Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 101), - Error::::BalanceLow - ); - }); -} - -#[test] -fn burning_asset_balance_with_positive_balance_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - assert_ok!(Assets::burn(RuntimeOrigin::signed(1), 0, 1, u64::MAX)); - System::assert_last_event(RuntimeEvent::Assets(crate::Event::Burned { - asset_id: 0, - owner: 1, - balance: 100, - })); - assert_eq!(Assets::balance(0, 1), 0); - }); -} - -#[test] -fn burning_asset_balance_with_zero_balance_does_nothing() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 2), 0); - assert_noop!( - Assets::burn(RuntimeOrigin::signed(1), 0, 2, u64::MAX), - Error::::NoAccount - ); - assert_eq!(Assets::balance(0, 2), 0); - assert_eq!(Assets::total_supply(0), 100); - }); -} - -#[test] -fn set_metadata_should_work() { - new_test_ext().execute_with(|| { - // Cannot add metadata to unknown asset - assert_noop!( - Assets::set_metadata(RuntimeOrigin::signed(1), 0, vec![0u8; 10], vec![0u8; 10], 12), - Error::::Unknown, - ); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - // Cannot add metadata to unowned asset - assert_noop!( - Assets::set_metadata(RuntimeOrigin::signed(2), 0, vec![0u8; 10], vec![0u8; 10], 12), - Error::::NoPermission, - ); - - // Cannot add oversized metadata - assert_noop!( - Assets::set_metadata(RuntimeOrigin::signed(1), 0, vec![0u8; 100], vec![0u8; 10], 12), - Error::::BadMetadata, - ); - assert_noop!( - Assets::set_metadata(RuntimeOrigin::signed(1), 0, vec![0u8; 10], vec![0u8; 100], 12), - Error::::BadMetadata, - ); - - // Successfully add metadata and take deposit - Balances::make_free_balance_be(&1, 30); - assert_ok!(Assets::set_metadata( - RuntimeOrigin::signed(1), - 0, - vec![0u8; 10], - vec![0u8; 10], - 12 - )); - assert_eq!(Balances::free_balance(&1), 9); - - // Update deposit - assert_ok!(Assets::set_metadata( - RuntimeOrigin::signed(1), - 0, - vec![0u8; 10], - vec![0u8; 5], - 12 - )); - assert_eq!(Balances::free_balance(&1), 14); - assert_ok!(Assets::set_metadata( - RuntimeOrigin::signed(1), - 0, - vec![0u8; 10], - vec![0u8; 15], - 12 - )); - assert_eq!(Balances::free_balance(&1), 4); - - // Cannot over-reserve - assert_noop!( - Assets::set_metadata(RuntimeOrigin::signed(1), 0, vec![0u8; 20], vec![0u8; 20], 12), - BalancesError::::InsufficientBalance, - ); - - // Clear Metadata - assert!(Metadata::::contains_key(0)); - assert_noop!( - Assets::clear_metadata(RuntimeOrigin::signed(2), 0), - Error::::NoPermission - ); - assert_noop!(Assets::clear_metadata(RuntimeOrigin::signed(1), 1), Error::::Unknown); - assert_ok!(Assets::clear_metadata(RuntimeOrigin::signed(1), 0)); - assert!(!Metadata::::contains_key(0)); - }); -} - -/// Calling on `dead_account` should be either unreachable, or fail if either a freeze or some -/// balance on hold exists. -/// -/// ### Case 1: Sufficient asset -/// -/// This asserts for `dead_account` on `decrease_balance`, `transfer_and_die` and -/// `do_destry_accounts`. -#[test] -fn calling_dead_account_fails_if_freezes_or_balances_on_hold_exist_1() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 50)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - - set_frozen_balance(0, 1, 50); - // Cannot transfer out less than max(freezes, ed). This happens in - // `prep_debit` under `transfer_and_die`. Would not reach `dead_account`. - assert_noop!( - Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 100), - Error::::BalanceLow - ); - assert_noop!( - Assets::transfer_keep_alive(RuntimeOrigin::signed(1), 0, 2, 100), - Error::::BalanceLow - ); - assert_noop!( - Assets::force_transfer(RuntimeOrigin::signed(1), 0, 1, 2, 100), - Error::::BalanceLow - ); - // Cannot start destroying the asset, because some accounts contain freezes - assert_noop!( - Assets::start_destroy(RuntimeOrigin::signed(1), 0), - Error::::ContainsFreezes - ); - clear_frozen_balance(0, 1); - - set_balance_on_hold(0, 1, 50); - // Cannot transfer out less than max(freezes, ed). This happens in - // `prep_debit` under `transfer_and_die`. Would not reach `dead_account`. - assert_noop!( - Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 100), - Error::::BalanceLow - ); - assert_noop!( - Assets::transfer_keep_alive(RuntimeOrigin::signed(1), 0, 2, 100), - Error::::BalanceLow - ); - assert_noop!( - Assets::force_transfer(RuntimeOrigin::signed(1), 0, 1, 2, 100), - Error::::BalanceLow - ); - // Cannot start destroying the asset, because some accounts contain freezes - assert_noop!( - Assets::start_destroy(RuntimeOrigin::signed(1), 0), - Error::::ContainsHolds - ); - }) -} - -/// Calling on `dead_account` should be either unreachable, or fail if either a freeze or some -/// balance on hold exists. -/// -/// ### Case 2: Inufficient asset -/// -/// This asserts for `dead_account` on `do_refund` and `do_refund_other`. -#[test] -fn calling_dead_account_fails_if_freezes_or_balances_on_hold_exist_2() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::touch(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - - set_frozen_balance(0, 1, 50); - - let mut account = - Account::::get(&0, &1).expect("account has already been touched; qed"); - let touch_deposit = - account.reason.take_deposit().expect("account was created by touching it; qed"); - - assert_noop!( - Assets::refund(RuntimeOrigin::signed(1), 0, true), - Error::::ContainsFreezes - ); - - // Assert touch deposit is not tainted. - let deposit_after_noop = - Account::::get(&0, &1).and_then(|mut account| account.reason.take_deposit()); - assert_eq!(deposit_after_noop, Some(touch_deposit)); - - clear_frozen_balance(0, 1); - - set_balance_on_hold(0, 1, 50); - assert_noop!( - Assets::refund(RuntimeOrigin::signed(1), 0, true), - Error::::ContainsHolds - ); - clear_balance_on_hold(0, 1); - assert_ok!(Assets::refund(RuntimeOrigin::signed(1), 0, true)); - }); - - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::touch_other(RuntimeOrigin::signed(1), 0, 2)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 2, 100)); - - set_frozen_balance(0, 2, 100); - assert_noop!( - Assets::refund_other(RuntimeOrigin::signed(1), 0, 2), - Error::::WouldBurn - ); - clear_frozen_balance(0, 2); - - // Note: It's not possible to set balance on hold for the maximum balance, - // as it `WouldBurn` because of how setting the balance works on mock. - set_balance_on_hold(0, 2, 99); - assert_noop!( - Assets::refund_other(RuntimeOrigin::signed(1), 0, 2), - Error::::WouldBurn - ); - clear_balance_on_hold(0, 2); - }) -} - -/// Regression test: refund with allow_burn=true must decrement total_supply. -/// -/// Previously, do_refund would destroy a non-zero balance account without -/// updating AssetDetails.supply, leaving phantom issuance in total_supply. -#[test] -fn refund_with_allow_burn_decrements_total_supply() { - new_test_ext().execute_with(|| { - // Create asset with admin=1, min_balance=1 - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - - // Give account 2 native balance for deposit - Balances::make_free_balance_be(&2, 100); - - // Account 2 touches the asset (creates deposit-held account) - assert_ok!(Assets::touch(RuntimeOrigin::signed(2), 0)); - - // Mint 50 tokens to account 2 - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 2, 50)); - - // Verify initial state - assert_eq!(Assets::total_supply(0), 50); - assert_eq!(Assets::balance(0, 2), 50); - - // Account 2 refunds with allow_burn=true, burning their 50 tokens - assert_ok!(Assets::refund(RuntimeOrigin::signed(2), 0, true)); - - // Key assertion: total_supply must be decremented by the burned amount - assert_eq!( - Assets::total_supply(0), - 0, - "total_supply should be 0 after burning 50 tokens via refund" - ); - - // Account should be gone - assert!(Account::::get(&0, &2).is_none()); - }); -} - -/// Destroying an asset calls the `FrozenBalance::died` hooks of all accounts. -#[test] -fn destroy_accounts_calls_died_hooks() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 50)); - // Create account 1 and 2. - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 2, 100)); - // Destroy the accounts. - assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0)); - - // Accounts 1 and 2 died. - assert_eq!( - hooks(), - vec![ - Hook::Died(0, 1), - // Note: Hooks get called twice because the hook is called from `Holder` AND - // `Freezer`. - Hook::Died(0, 1), - Hook::Died(0, 2), - // Note: Hooks get called twice because the hook is called from `Holder` AND - // `Freezer`. - Hook::Died(0, 2) - ] - ); - }) -} - -/// Destroying an asset calls the `FrozenBalance::died` hooks of all accounts. -#[test] -fn finish_destroy_asset_destroys_asset() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 50)); - // Destroy the accounts. - assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0)); - - // Asset is gone - assert!(Asset::::get(0).is_none()); - }) -} - -#[test] -fn freezer_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 10)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - - // freeze 50 of it. - set_frozen_balance(0, 1, 50); - - // Note: The amount to be transferred in this step changed deliberately from 20 to 30 - // (https://github.com/paritytech/polkadot-sdk/pull/4530/commits/2ab35354d86904c035b21a2229452841b79b0457) - // to reflect the change in how `reducible_balance` is calculated: from untouchable = ed + - // frozen, to untouchalbe = max(ed, frozen) - // - // This is done in this line so most of the remaining test is preserved without changes - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 30)); - // cannot transfer another 21 away as this would take the spendable balance (30) to below - // zero. - assert_noop!( - Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 21), - Error::::BalanceLow - ); - - // create an approved transfer... - Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50)); - let e = Error::::BalanceLow; - // ...but that wont work either: - assert_noop!(Assets::transfer_approved(RuntimeOrigin::signed(2), 0, 1, 2, 21), e); - // a force transfer won't work also. - let e = Error::::BalanceLow; - assert_noop!(Assets::force_transfer(RuntimeOrigin::signed(1), 0, 1, 2, 21), e); - - // reduce it to only 49 frozen... - set_frozen_balance(0, 1, 49); - // ...and it's all good: - assert_ok!(Assets::force_transfer(RuntimeOrigin::signed(1), 0, 1, 2, 21)); - - // and if we clear it, we can remove the account completely. - clear_frozen_balance(0, 1); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 49)); - assert_eq!( - hooks(), - vec![ - Hook::Died(0, 1), - // Note: Hooks get called twice because the hook is called from `Holder` AND - // `Freezer`. - Hook::Died(0, 1) - ] - ); - }); -} - -#[test] -fn freezing_and_holds_work() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 10)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - - // Hold 50 of it - set_balance_on_hold(0, 1, 50); - assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(TestHolder::balance_on_hold(0, &1), Some(50)); - - // Can freeze up to held + min_balance without affecting reducible - set_frozen_balance(0, 1, 59); - assert_eq!(Assets::reducible_balance(0, &1, true), Ok(40)); - set_frozen_balance(0, 1, 61); - assert_eq!(Assets::reducible_balance(0, &1, true), Ok(39)); - - // Increasing hold is not necessarily restricted by the frozen balance - set_balance_on_hold(0, 1, 62); - assert_eq!(Assets::reducible_balance(0, &1, true), Ok(28)); - - // Transfers are bound to the spendable amount - assert_noop!( - Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 29), - Error::::BalanceLow - ); - // Approved transfers fail as well - Balances::make_free_balance_be(&1, 2); - assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 29)); - assert_noop!( - Assets::transfer_approved(RuntimeOrigin::signed(2), 0, 1, 2, 29), - Error::::BalanceLow - ); - // Also forced transfers fail - assert_noop!( - Assets::force_transfer(RuntimeOrigin::signed(1), 0, 1, 2, 29), - Error::::BalanceLow - ); - // ...but transferring up to spendable works - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 28)); - }); -} - -#[test] -fn imbalances_should_work() { - use frame_support::traits::fungibles::Balanced; - - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - - let imb = Assets::issue(0, 100); - assert_eq!(Assets::total_supply(0), 100); - assert_eq!(imb.peek(), 100); - - let (imb1, imb2) = imb.split(30); - assert_eq!(imb1.peek(), 30); - assert_eq!(imb2.peek(), 70); - - drop(imb2); - assert_eq!(Assets::total_supply(0), 30); - - assert!(Assets::resolve(&1, imb1).is_ok()); - assert_eq!(Assets::balance(0, 1), 30); - assert_eq!(Assets::total_supply(0), 30); - }); -} - -#[test] -fn deposit_checks_total_issuance_headroom() { - use frame_support::traits::{ - fungibles::{Balanced, Unbalanced}, - tokens::Precision::{BestEffort, Exact}, - }; - use sp_runtime::ArithmeticError; - - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - - // Leave only a small amount of headroom below the balance-type maximum. - let headroom = 10u64; - Assets::set_total_issuance(0, u64::MAX - headroom); - - // An `Exact` deposit that issuance cannot fully represent must fail and change nothing, - // rather than crediting the account and later saturating issuance on debt drop. - match >::deposit(0, &1, headroom + 1, Exact) { - Err(e) => assert_eq!(e, ArithmeticError::Overflow.into()), - Ok(_) => panic!("exact deposit exceeding issuance headroom must fail"), - } - assert_eq!(Assets::balance(0, 1), 100, "failed deposit must not credit"); - assert_eq!(Assets::total_supply(0), u64::MAX - headroom); - - // A `BestEffort` deposit is capped to the remaining issuance headroom, so the credited - // amount always matches the growth in issuance. - let debt = >::deposit(0, &1, headroom + 100, BestEffort) - .expect("best-effort deposit should succeed"); - assert_eq!(Assets::balance(0, 1), 100 + headroom, "credit is capped to headroom"); - - // Dropping the debt grows issuance by exactly the credited amount (no saturation loss). - drop(debt); - assert_eq!(Assets::total_supply(0), u64::MAX); - }); -} - -#[test] -fn force_metadata_should_work() { - new_test_ext().execute_with(|| { - // force set metadata works - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::force_set_metadata( - RuntimeOrigin::root(), - 0, - vec![0u8; 10], - vec![0u8; 10], - 8, - false - )); - assert!(Metadata::::contains_key(0)); - - // overwrites existing metadata - let asset_original_metadata = Metadata::::get(0); - assert_ok!(Assets::force_set_metadata( - RuntimeOrigin::root(), - 0, - vec![1u8; 10], - vec![1u8; 10], - 8, - false - )); - assert_ne!(Metadata::::get(0), asset_original_metadata); - - // attempt to set metadata for non-existent asset class - assert_noop!( - Assets::force_set_metadata( - RuntimeOrigin::root(), - 1, - vec![0u8; 10], - vec![0u8; 10], - 8, - false - ), - Error::::Unknown - ); - - // string length limit check - let limit = 50usize; - assert_noop!( - Assets::force_set_metadata( - RuntimeOrigin::root(), - 0, - vec![0u8; limit + 1], - vec![0u8; 10], - 8, - false - ), - Error::::BadMetadata - ); - assert_noop!( - Assets::force_set_metadata( - RuntimeOrigin::root(), - 0, - vec![0u8; 10], - vec![0u8; limit + 1], - 8, - false - ), - Error::::BadMetadata - ); - - // force clear metadata works - assert!(Metadata::::contains_key(0)); - assert_ok!(Assets::force_clear_metadata(RuntimeOrigin::root(), 0)); - assert!(!Metadata::::contains_key(0)); - - // Error handles clearing non-existent asset class - assert_noop!( - Assets::force_clear_metadata(RuntimeOrigin::root(), 1), - Error::::Unknown - ); - }); -} - -#[test] -fn force_asset_status_should_work() { - new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 10); - Balances::make_free_balance_be(&2, 10); - assert_ok!(Assets::create(RuntimeOrigin::signed(1), 0, 1, 30)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 50)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 2, 150)); - - // force asset status to change min_balance > balance - assert_ok!(Assets::force_asset_status( - RuntimeOrigin::root(), - 0, - 1, - 1, - 1, - 1, - 100, - true, - false - )); - assert_eq!(Assets::balance(0, 1), 50); - - // account can receive assets for balance < min_balance - assert_ok!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 1)); - assert_eq!(Assets::balance(0, 1), 51); - - // account on outbound transfer will cleanup for balance < min_balance - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 1)); - assert_eq!(Assets::balance(0, 1), 0); - - // won't create new account with balance below min_balance - assert_noop!( - Assets::transfer(RuntimeOrigin::signed(2), 0, 3, 50), - TokenError::BelowMinimum - ); - - // force asset status will not execute for non-existent class - assert_noop!( - Assets::force_asset_status(RuntimeOrigin::root(), 1, 1, 1, 1, 1, 90, true, false), - Error::::Unknown - ); - - // account drains to completion when funds dip below min_balance - assert_ok!(Assets::force_asset_status( - RuntimeOrigin::root(), - 0, - 1, - 1, - 1, - 1, - 110, - true, - false - )); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 110)); - assert_eq!(Assets::balance(0, 1), 200); - assert_eq!(Assets::balance(0, 2), 0); - assert_eq!(Assets::total_supply(0), 200); - }); -} - -#[test] -fn set_min_balance_should_work() { - new_test_ext().execute_with(|| { - let id = 42; - Balances::make_free_balance_be(&1, 10); - assert_ok!(Assets::create(RuntimeOrigin::signed(1), id, 1, 30)); - - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), id, 1, 100)); - // Won't execute because there is an asset holder. - assert_noop!( - Assets::set_min_balance(RuntimeOrigin::signed(1), id, 50), - Error::::NoPermission - ); - - // Force asset status to make this a sufficient asset. - assert_ok!(Assets::force_asset_status( - RuntimeOrigin::root(), - id, - 1, - 1, - 1, - 1, - 30, - true, - false - )); - - // Won't execute because there is an account holding the asset and the asset is marked as - // sufficient. - assert_noop!( - Assets::set_min_balance(RuntimeOrigin::signed(1), id, 10), - Error::::NoPermission - ); - - // Make the asset not sufficient. - assert_ok!(Assets::force_asset_status( - RuntimeOrigin::root(), - id, - 1, - 1, - 1, - 1, - 60, - false, - false - )); - - // Will execute because the new value of min_balance is less than the - // old value. 10 < 30 - assert_ok!(Assets::set_min_balance(RuntimeOrigin::signed(1), id, 10)); - assert_eq!(Asset::::get(id).unwrap().min_balance, 10); - - assert_ok!(Assets::burn(RuntimeOrigin::signed(1), id, 1, 100)); - - assert_ok!(Assets::set_min_balance(RuntimeOrigin::signed(1), id, 50)); - assert_eq!(Asset::::get(id).unwrap().min_balance, 50); - }); -} - -/// Regression test: set_min_balance must reject zero to prevent consumer reference griefing. -/// -/// A zero min_balance would allow zero-balance accounts to persist (since the reaping check -/// is `balance < min_balance`, which is never true when min_balance is 0). An attacker could -/// then strand consumer references on victim accounts. -#[test] -fn set_min_balance_rejects_zero() { - new_test_ext().execute_with(|| { - let id = 42; - Balances::make_free_balance_be(&1, 10); - assert_ok!(Assets::create(RuntimeOrigin::signed(1), id, 1, 30)); - - // Attempting to set min_balance to zero should fail - assert_noop!( - Assets::set_min_balance(RuntimeOrigin::signed(1), id, 0), - Error::::MinBalanceZero - ); - - // Verify min_balance is unchanged - assert_eq!(Asset::::get(id).unwrap().min_balance, 30); - }); -} - -#[test] -fn balance_conversion_should_work() { - new_test_ext().execute_with(|| { - use frame_support::traits::tokens::ConversionToAssetBalance; - - let id = 42; - assert_ok!(Assets::force_create(RuntimeOrigin::root(), id, 1, true, 10)); - let not_sufficient = 23; - assert_ok!(Assets::force_create(RuntimeOrigin::root(), not_sufficient, 1, false, 10)); - assert_eq!(asset_ids(), vec![23, 42, 999]); - assert_eq!( - BalanceToAssetBalance::::to_asset_balance(100, 1234), - Err(ConversionError::AssetMissing) - ); - assert_eq!( - BalanceToAssetBalance::::to_asset_balance( - 100, - not_sufficient - ), - Err(ConversionError::AssetNotSufficient) - ); - // 10 / 1 == 10 -> the conversion should 10x the value - assert_eq!( - BalanceToAssetBalance::::to_asset_balance(100, id), - Ok(100 * 10) - ); - }); -} - -#[test] -fn assets_from_genesis_should_exist() { - new_test_ext().execute_with(|| { - assert_eq!(asset_ids(), vec![999]); - assert!(Metadata::::contains_key(999)); - assert_eq!(Assets::balance(999, 1), 100); - assert_eq!(Assets::total_supply(999), 100); - }); -} - -#[test] -fn querying_name_symbol_and_decimals_should_work() { - new_test_ext().execute_with(|| { - use frame_support::traits::fungibles::metadata::Inspect; - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::force_set_metadata( - RuntimeOrigin::root(), - 0, - vec![0u8; 10], - vec![1u8; 10], - 12, - false - )); - assert_eq!(Assets::name(0), vec![0u8; 10]); - assert_eq!(Assets::symbol(0), vec![1u8; 10]); - assert_eq!(Assets::decimals(0), 12); - }); -} - -#[test] -fn querying_allowance_should_work() { - new_test_ext().execute_with(|| { - use frame_support::traits::fungibles::approvals::{Inspect, Mutate}; - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - Balances::make_free_balance_be(&1, 2); - assert_ok!(Assets::approve(0, &1, &2, 50)); - assert_eq!(Assets::allowance(0, &1, &2), 50); - // Transfer asset 0, from owner 1 and delegate 2 to destination 3 - assert_ok!(Assets::transfer_from(0, &1, &2, &3, 50)); - assert_eq!(Assets::allowance(0, &1, &2), 0); - }); -} - -#[test] -fn transfer_large_asset() { - new_test_ext().execute_with(|| { - let amount = u64::pow(2, 63) + 2; - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, amount)); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, amount - 1)); - }) -} - -#[test] -fn querying_roles_should_work() { - new_test_ext().execute_with(|| { - use frame_support::traits::fungibles::roles::Inspect; - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::set_team( - RuntimeOrigin::signed(1), - 0, - // Issuer - 2, - // Admin - 3, - // Freezer - 4, - )); - assert_eq!(Assets::owner(0), Some(1)); - assert_eq!(Assets::issuer(0), Some(2)); - assert_eq!(Assets::admin(0), Some(3)); - assert_eq!(Assets::freezer(0), Some(4)); - }); -} - -#[test] -fn normal_asset_create_and_destroy_callbacks_should_work() { - new_test_ext().execute_with(|| { - assert!(storage::get(AssetsCallbackHandle::CREATED.as_bytes()).is_none()); - assert!(storage::get(AssetsCallbackHandle::DESTROYED.as_bytes()).is_none()); - - Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::create(RuntimeOrigin::signed(1), 0, 1, 1)); - assert!(storage::get(AssetsCallbackHandle::CREATED.as_bytes()).is_some()); - assert!(storage::get(AssetsCallbackHandle::DESTROYED.as_bytes()).is_none()); - - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0)); - // Callback still hasn't been invoked - assert!(storage::get(AssetsCallbackHandle::DESTROYED.as_bytes()).is_none()); - - assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0)); - assert!(storage::get(AssetsCallbackHandle::DESTROYED.as_bytes()).is_some()); - }); -} - -#[test] -fn root_asset_create_should_work() { - new_test_ext().execute_with(|| { - assert!(storage::get(AssetsCallbackHandle::CREATED.as_bytes()).is_none()); - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert!(storage::get(AssetsCallbackHandle::CREATED.as_bytes()).is_some()); - assert!(storage::get(AssetsCallbackHandle::DESTROYED.as_bytes()).is_none()); - }); -} - -#[test] -fn asset_start_destroy_fails_if_there_are_holds_or_freezes() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - - set_frozen_balance(0, 1, 50); - assert_noop!( - Assets::start_destroy(RuntimeOrigin::signed(1), 0), - Error::::ContainsFreezes - ); - - set_balance_on_hold(0, 1, 50); - assert_noop!( - Assets::start_destroy(RuntimeOrigin::signed(1), 0), - Error::::ContainsHolds - ); - - clear_frozen_balance(0, 1); - clear_balance_on_hold(0, 1); - - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0)); - }); -} - -#[test] -fn asset_create_and_destroy_is_reverted_if_callback_fails() { - new_test_ext().execute_with(|| { - // Asset creation fails due to callback failure - AssetsCallbackHandle::set_return_error(); - Balances::make_free_balance_be(&1, 100); - assert_noop!( - Assets::create(RuntimeOrigin::signed(1), 0, 1, 1), - Error::::CallbackFailed - ); - - // Callback succeeds, so asset creation succeeds - AssetsCallbackHandle::set_return_ok(); - assert_ok!(Assets::create(RuntimeOrigin::signed(1), 0, 1, 1)); - - // Asset destroy should fail due to callback failure - AssetsCallbackHandle::set_return_error(); - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0)); - assert_noop!( - Assets::finish_destroy(RuntimeOrigin::signed(1), 0), - Error::::CallbackFailed - ); - }); -} - -#[test] -fn multiple_transfer_alls_work_ok() { - new_test_ext().execute_with(|| { - // Only run PoC when the system pallet is enabled, since the underlying bug is in the - // system pallet it won't work with BalancesAccountStore - // Start with a balance of 100 - Balances::force_set_balance(RuntimeOrigin::root(), 1, 100).unwrap(); - // Emulate a sufficient, in reality this could be reached by transferring a sufficient - // asset to the account - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - // Spend the same balance multiple times - assert_ok!(Balances::transfer_all(RuntimeOrigin::signed(1), 1337, false)); - assert_ok!(Balances::transfer_all(RuntimeOrigin::signed(1), 1337, false)); - - assert_eq!(Balances::free_balance(&1), 0); - assert_eq!(Balances::free_balance(&1337), 100); - }); -} - -#[test] -fn weights_sane() { - let info = crate::Call::::create { id: 10, admin: 4, min_balance: 3 }.get_dispatch_info(); - assert_eq!(<() as crate::WeightInfo>::create(), info.call_weight); - - let info = crate::Call::::finish_destroy { id: 10 }.get_dispatch_info(); - assert_eq!(<() as crate::WeightInfo>::finish_destroy(), info.call_weight); -} - -#[test] -fn asset_destroy_refund_existence_deposit() { - new_test_ext().execute_with(|| { - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1)); - Balances::make_free_balance_be(&1, 100); - let admin = 1; - let admin_origin = RuntimeOrigin::signed(admin); - - let account2 = 2; // account with own deposit - let account3 = 3; // account with admin's deposit - Balances::make_free_balance_be(&account2, 100); - - assert_eq!(Balances::reserved_balance(&account2), 0); - assert_eq!(Balances::reserved_balance(&account3), 0); - assert_eq!(Balances::reserved_balance(&admin), 0); - - assert_ok!(Assets::touch(RuntimeOrigin::signed(account2), 0)); - assert_ok!(Assets::touch_other(admin_origin.clone(), 0, account3)); - - assert_eq!(Balances::reserved_balance(&account2), 10); - assert_eq!(Balances::reserved_balance(&account3), 0); - assert_eq!(Balances::reserved_balance(&admin), 10); - - assert_ok!(Assets::start_destroy(admin_origin.clone(), 0)); - assert_ok!(Assets::destroy_accounts(admin_origin.clone(), 0)); - assert_ok!(Assets::destroy_approvals(admin_origin.clone(), 0)); - assert_ok!(Assets::finish_destroy(admin_origin.clone(), 0)); - - assert_eq!(Balances::reserved_balance(&account2), 0); - assert_eq!(Balances::reserved_balance(&account3), 0); - assert_eq!(Balances::reserved_balance(&admin), 0); - }); -} - -#[test] -fn increasing_or_decreasing_destroying_asset_should_not_work() { - new_test_ext().execute_with(|| { - use frame_support::traits::fungibles::Inspect; - - let admin = 1; - let admin_origin = RuntimeOrigin::signed(admin); - - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, admin, true, 1)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); - assert_eq!(Assets::balance(0, 1), 100); - - assert_eq!(Assets::can_deposit(0, &1, 10, Provenance::Extant), DepositConsequence::Success); - assert_eq!(Assets::can_withdraw(0, &1, 10), WithdrawConsequence::<_>::Success); - assert_eq!(Assets::can_increase(0, &1, 10, false), DepositConsequence::Success); - assert_eq!(Assets::can_decrease(0, &1, 10, false), WithdrawConsequence::<_>::Success); - - assert_ok!(Assets::start_destroy(admin_origin, 0)); - - assert_eq!( - Assets::can_deposit(0, &1, 10, Provenance::Extant), - DepositConsequence::UnknownAsset - ); - assert_eq!(Assets::can_withdraw(0, &1, 10), WithdrawConsequence::<_>::UnknownAsset); - assert_eq!(Assets::can_increase(0, &1, 10, false), DepositConsequence::UnknownAsset); - assert_eq!(Assets::can_decrease(0, &1, 10, false), WithdrawConsequence::<_>::UnknownAsset); - }); -} - -#[test] -fn asset_id_cannot_be_reused() { - new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); - // Asset id can be reused till auto increment is not enabled. - assert_ok!(Assets::create(RuntimeOrigin::signed(1), 0, 1, 1)); - - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0)); - - assert!(!Asset::::contains_key(0)); - - // Asset id `0` is reused. - assert_ok!(Assets::create(RuntimeOrigin::signed(1), 0, 1, 1)); - assert!(Asset::::contains_key(0)); - - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0)); - assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0)); - - assert!(!Asset::::contains_key(0)); - - // Enable auto increment. Next asset id must be 5. - pallet::NextAssetId::::put(5); - - assert_noop!(Assets::create(RuntimeOrigin::signed(1), 0, 1, 1), Error::::BadAssetId); - assert_noop!(Assets::create(RuntimeOrigin::signed(1), 1, 1, 1), Error::::BadAssetId); - assert_noop!( - Assets::force_create(RuntimeOrigin::root(), 0, 1, false, 1), - Error::::BadAssetId - ); - assert_noop!( - Assets::force_create(RuntimeOrigin::root(), 1, 1, true, 1), - Error::::BadAssetId - ); - - // Asset with id `5` is created. - assert_ok!(Assets::create(RuntimeOrigin::signed(1), 5, 1, 1)); - assert!(Asset::::contains_key(5)); - - // Destroy asset with id `6`. - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 5)); - assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 5)); - - assert!(!Asset::::contains_key(0)); - - // Asset id `5` cannot be reused. - assert_noop!(Assets::create(RuntimeOrigin::signed(1), 5, 1, 1), Error::::BadAssetId); - - assert_ok!(Assets::create(RuntimeOrigin::signed(1), 6, 1, 1)); - assert!(Asset::::contains_key(6)); - - // Destroy asset with id `6`. - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 6)); - assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 6)); - - assert!(!Asset::::contains_key(6)); - - // Asset id `6` cannot be reused with force. - assert_noop!( - Assets::force_create(RuntimeOrigin::root(), 6, 1, false, 1), - Error::::BadAssetId - ); - - assert_ok!(Assets::force_create(RuntimeOrigin::root(), 7, 1, false, 1)); - assert!(Asset::::contains_key(7)); - }); -} - -#[test] -fn setting_too_many_reserves_fails() { - new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&1, 100); - assert_ok!(Assets::create(RuntimeOrigin::signed(1), 0, 1, 1)); - assert_eq!(Balances::reserved_balance(&1), 1); - assert!(Asset::::contains_key(0)); - - let mut reserves = vec![]; - for i in 0..MAX_RESERVES + 1 { - reserves.push(1234u128 + i as u128); - } - // Attempting to create a BoundedVec with too many reserves should fail - let result: Result>, _> = - reserves.clone().try_into(); - assert!(result.is_err()); - assert_eq!(Reserves::::get(0), vec![]); - }); -} diff --git a/pallets/assets/src/tests/sets.rs b/pallets/assets/src/tests/sets.rs deleted file mode 100644 index 4d75b8ae..00000000 --- a/pallets/assets/src/tests/sets.rs +++ /dev/null @@ -1,358 +0,0 @@ -// 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. - -//! Tests for [`ItemOf`], [`fungible::UnionOf`] and [`fungibles::UnionOf`] set types. - -use super::*; -use frame_support::{ - parameter_types, - traits::{ - fungible, - fungible::ItemOf, - fungibles, - tokens::{ - fungibles::{ - Balanced as FungiblesBalanced, Create as FungiblesCreate, - Inspect as FungiblesInspect, Mutate as FungiblesMutate, - }, - Fortitude, Precision, Preservation, - }, - }, -}; -use sp_runtime::{traits::ConvertToValue, Either}; - -const FIRST_ASSET: u32 = 0; -const UNKNOWN_ASSET: u32 = 10; - -parameter_types! { - pub const LeftAsset: Either<(), u32> = Either::Left(()); - pub const RightAsset: Either = Either::Right(()); - pub const RightUnitAsset: Either<(), ()> = Either::Right(()); -} - -/// Implementation of the `fungible` traits through the [`ItemOf`] type, specifically for a -/// single asset class from [`T`] identified by [`FIRST_ASSET`]. -type FirstFungible = ItemOf, u64>; - -/// Implementation of the `fungible` traits through the [`ItemOf`] type, specifically for a -/// single asset class from [`T`] identified by [`UNKNOWN_ASSET`]. -type UnknownFungible = ItemOf, u64>; - -/// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes -/// the [`FirstFungible`] from the left. -type LeftFungible = fungible::UnionOf, T, ConvertToValue, (), u64>; - -/// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes -/// the [`LeftFungible`] from the right. -type RightFungible = - fungible::UnionOf, LeftFungible, ConvertToValue, (), u64>; - -/// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes -/// the [`RightFungible`] from the left. -type LeftFungibles = fungibles::UnionOf, T, ConvertToValue, (), u64>; - -/// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes -/// the [`LeftFungibles`] from the right. -/// -/// By using this type, we can navigate through each branch of [`fungible::UnionOf`], -/// [`fungibles::UnionOf`], and [`ItemOf`] to access the underlying `fungibles::*` -/// implementation provided by the pallet. -type First = fungibles::UnionOf, ConvertToValue, (), u64>; - -#[test] -fn deposit_from_set_types_works() { - new_test_ext().execute_with(|| { - let asset1 = 0; - let account1 = 1; - let account2 = 2; - - assert_ok!(>::create(asset1, account1, true, 1)); - assert_ok!(Assets::mint_into(asset1, &account1, 100)); - - assert_eq!(First::::total_issuance(()), 100); - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - - let imb = First::::deposit((), &account2, 50, Precision::Exact).unwrap(); - assert_eq!(First::::balance((), &account2), 50); - assert_eq!(First::::total_issuance(()), 100); - - System::assert_has_event(RuntimeEvent::Assets(crate::Event::Deposited { - asset_id: asset1, - who: account2, - amount: 50, - })); - - assert_eq!(imb.peek(), 50); - - let (imb1, imb2) = imb.split(30); - assert_eq!(imb1.peek(), 30); - assert_eq!(imb2.peek(), 20); - - drop(imb2); - assert_eq!(First::::total_issuance(()), 120); - - assert!(First::::settle(&account1, imb1, Preservation::Preserve).is_ok()); - assert_eq!(First::::balance((), &account1), 70); - assert_eq!(First::::balance((), &account2), 50); - assert_eq!(First::::total_issuance(()), 120); - - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - }); -} - -#[test] -fn issue_from_set_types_works() { - new_test_ext().execute_with(|| { - let asset1: u32 = 0; - let account1: u64 = 1; - - assert_ok!(>::create(asset1, account1, true, 1)); - assert_ok!(Assets::mint_into(asset1, &account1, 100)); - - assert_eq!(First::::balance((), &account1), 100); - assert_eq!(First::::total_issuance(()), 100); - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - - let imb = First::::issue((), 100); - assert_eq!(First::::total_issuance(()), 200); - assert_eq!(imb.peek(), 100); - - let (imb1, imb2) = imb.split(30); - assert_eq!(imb1.peek(), 30); - assert_eq!(imb2.peek(), 70); - - drop(imb2); - assert_eq!(First::::total_issuance(()), 130); - - assert!(First::::resolve(&account1, imb1).is_ok()); - assert_eq!(First::::balance((), &account1), 130); - assert_eq!(First::::total_issuance(()), 130); - - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - }); -} - -#[test] -fn pair_from_set_types_works() { - new_test_ext().execute_with(|| { - let asset1: u32 = 0; - let account1: u64 = 1; - - assert_ok!(>::create(asset1, account1, true, 1)); - assert_ok!(Assets::mint_into(asset1, &account1, 100)); - - assert_eq!(First::::balance((), &account1), 100); - assert_eq!(First::::total_issuance(()), 100); - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - - let (debt, credit) = First::::pair((), 100).unwrap(); - assert_eq!(First::::total_issuance(()), 100); - assert_eq!(debt.peek(), 100); - assert_eq!(credit.peek(), 100); - - let (debt1, debt2) = debt.split(30); - assert_eq!(debt1.peek(), 30); - assert_eq!(debt2.peek(), 70); - - drop(debt2); - assert_eq!(First::::total_issuance(()), 170); - - assert!(First::::settle(&account1, debt1, Preservation::Preserve).is_ok()); - assert_eq!(First::::balance((), &account1), 70); - assert_eq!(First::::total_issuance(()), 170); - - let (credit1, credit2) = credit.split(40); - assert_eq!(credit1.peek(), 40); - assert_eq!(credit2.peek(), 60); - - drop(credit2); - assert_eq!(First::::total_issuance(()), 110); - - assert!(First::::resolve(&account1, credit1).is_ok()); - assert_eq!(First::::balance((), &account1), 110); - assert_eq!(First::::total_issuance(()), 110); - - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - }); -} - -#[test] -fn rescind_from_set_types_works() { - new_test_ext().execute_with(|| { - let asset1: u32 = 0; - let account1: u64 = 1; - - assert_ok!(>::create(asset1, account1, true, 1)); - assert_ok!(Assets::mint_into(asset1, &account1, 100)); - - assert_eq!(First::::total_issuance(()), 100); - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - - let imb = First::::rescind((), 20); - assert_eq!(First::::total_issuance(()), 80); - - assert_eq!(imb.peek(), 20); - - let (imb1, imb2) = imb.split(15); - assert_eq!(imb1.peek(), 15); - assert_eq!(imb2.peek(), 5); - - drop(imb2); - assert_eq!(First::::total_issuance(()), 85); - - assert!(First::::settle(&account1, imb1, Preservation::Preserve).is_ok()); - assert_eq!(First::::balance((), &account1), 85); - assert_eq!(First::::total_issuance(()), 85); - - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - }); -} - -#[test] -fn resolve_from_set_types_works() { - new_test_ext().execute_with(|| { - let asset1: u32 = 0; - let account1: u64 = 1; - let account2: u64 = 2; - let ed = 11; - - assert_ok!(>::create(asset1, account1, true, ed)); - assert_ok!(Assets::mint_into(asset1, &account1, 100)); - - assert_eq!(First::::balance((), &account1), 100); - assert_eq!(First::::total_issuance(()), 100); - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - - let imb = First::::issue((), 100); - assert_eq!(First::::total_issuance(()), 200); - assert_eq!(imb.peek(), 100); - - let (imb1, imb2) = imb.split(10); - assert_eq!(imb1.peek(), 10); - assert_eq!(imb2.peek(), 90); - assert_eq!(First::::total_issuance(()), 200); - - // ed requirements not met. - let imb1 = First::::resolve(&account2, imb1).unwrap_err(); - assert_eq!(imb1.peek(), 10); - drop(imb1); - assert_eq!(First::::total_issuance(()), 190); - assert_eq!(First::::balance((), &account2), 0); - - // resolve to new account `2`. - assert_ok!(First::::resolve(&account2, imb2)); - assert_eq!(First::::total_issuance(()), 190); - assert_eq!(First::::balance((), &account2), 90); - - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - }); -} - -#[test] -fn settle_from_set_types_works() { - new_test_ext().execute_with(|| { - let asset1: u32 = 0; - let account1: u64 = 1; - let account2: u64 = 2; - let ed = 11; - - assert_ok!(>::create(asset1, account1, true, ed)); - assert_ok!(Assets::mint_into(asset1, &account1, 100)); - assert_ok!(Assets::mint_into(asset1, &account2, 100)); - - assert_eq!(First::::balance((), &account2), 100); - assert_eq!(First::::total_issuance(()), 200); - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - - let imb = First::::rescind((), 100); - assert_eq!(First::::total_issuance(()), 100); - assert_eq!(imb.peek(), 100); - - let (imb1, imb2) = imb.split(10); - assert_eq!(imb1.peek(), 10); - assert_eq!(imb2.peek(), 90); - assert_eq!(First::::total_issuance(()), 100); - - // ed requirements not met. - let imb2 = First::::settle(&account2, imb2, Preservation::Preserve).unwrap_err(); - assert_eq!(imb2.peek(), 90); - drop(imb2); - assert_eq!(First::::total_issuance(()), 190); - assert_eq!(First::::balance((), &account2), 100); - - // settle to account `1`. - assert_ok!(First::::settle(&account2, imb1, Preservation::Preserve)); - assert_eq!(First::::total_issuance(()), 190); - assert_eq!(First::::balance((), &account2), 90); - - let imb = First::::rescind((), 85); - assert_eq!(First::::total_issuance(()), 105); - assert_eq!(imb.peek(), 85); - - // settle to account `1` and expect some dust. - let imb = First::::settle(&account2, imb, Preservation::Expendable).unwrap(); - assert_eq!(imb.peek(), 5); - assert_eq!(First::::total_issuance(()), 105); - assert_eq!(First::::balance((), &account2), 0); - - drop(imb); - assert_eq!(First::::total_issuance(()), 100); - - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - }); -} - -#[test] -fn withdraw_from_set_types_works() { - new_test_ext().execute_with(|| { - let asset1 = 0; - let account1 = 1; - let account2 = 2; - - assert_ok!(>::create(asset1, account1, true, 1)); - assert_ok!(Assets::mint_into(asset1, &account1, 100)); - assert_ok!(Assets::mint_into(asset1, &account2, 100)); - - assert_eq!(First::::total_issuance(()), 200); - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - - let imb = First::::withdraw( - (), - &account2, - 50, - Precision::Exact, - Preservation::Preserve, - Fortitude::Polite, - ) - .unwrap(); - assert_eq!(First::::balance((), &account2), 50); - assert_eq!(First::::total_issuance(()), 200); - - System::assert_has_event(RuntimeEvent::Assets(crate::Event::Withdrawn { - asset_id: asset1, - who: account2, - amount: 50, - })); - - assert_eq!(imb.peek(), 50); - drop(imb); - assert_eq!(First::::total_issuance(()), 150); - assert_eq!(First::::balance((), &account2), 50); - - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - }); -} diff --git a/pallets/assets/src/types.rs b/pallets/assets/src/types.rs deleted file mode 100644 index baa53056..00000000 --- a/pallets/assets/src/types.rs +++ /dev/null @@ -1,361 +0,0 @@ -// 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. - -//! Various basic types for use in the assets pallet. - -use super::*; -use frame_support::{ - pallet_prelude::*, - traits::{fungible, tokens::ConversionToAssetBalance}, -}; -use sp_runtime::{traits::Convert, FixedPointNumber, FixedU128}; - -pub type DepositBalanceOf = - <>::Currency as Currency<::AccountId>>::Balance; -pub type AssetAccountOf = AssetAccount< - >::Balance, - DepositBalanceOf, - >::Extra, - ::AccountId, ->; -pub type ExistenceReasonOf = - ExistenceReason, ::AccountId>; - -/// AssetStatus holds the current state of the asset. It could either be Live and available for use, -/// or in a Destroying state. -#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub enum AssetStatus { - /// The asset is active and able to be used. - Live, - /// Whether the asset is frozen for non-admin transfers. - Frozen, - /// The asset is currently being destroyed, and all actions are no longer permitted on the - /// asset. Once set to `Destroying`, the asset can never transition back to a `Live` state. - Destroying, -} - -#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub struct AssetDetails { - /// Can change `owner`, `issuer`, `freezer` and `admin` accounts. - pub owner: AccountId, - /// Can mint tokens. - pub issuer: AccountId, - /// Can thaw tokens, force transfers and burn tokens from any account. - pub admin: AccountId, - /// Can freeze tokens. - pub freezer: AccountId, - /// The total supply across all accounts. - pub supply: Balance, - /// The balance deposited for this asset. This pays for the data stored here. - pub deposit: DepositBalance, - /// The ED for virtual accounts. - pub min_balance: Balance, - /// If `true`, then any account with this asset is given a provider reference. Otherwise, it - /// requires a consumer reference. - pub is_sufficient: bool, - /// The total number of accounts. - pub accounts: u32, - /// The total number of accounts for which we have placed a self-sufficient reference. - pub sufficients: u32, - /// The total number of approvals. - pub approvals: u32, - /// The status of the asset - pub status: AssetStatus, -} - -/// Data concerning an approval. -#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, MaxEncodedLen, TypeInfo)] -pub struct Approval { - /// The amount of funds approved for the balance transfer from the owner to some delegated - /// target. - pub amount: Balance, - /// The amount reserved on the owner's account to hold this item in storage. - pub deposit: DepositBalance, -} - -#[test] -fn ensure_bool_decodes_to_consumer_or_sufficient() { - assert_eq!(false.encode(), ExistenceReason::<(), ()>::Consumer.encode()); - assert_eq!(true.encode(), ExistenceReason::<(), ()>::Sufficient.encode()); -} - -/// The reason for an account's existence within an asset class. -#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub enum ExistenceReason { - /// A consumer reference was used to create this account. - #[codec(index = 0)] - Consumer, - /// The asset class is `sufficient` for account existence. - #[codec(index = 1)] - Sufficient, - /// The account holder has placed a deposit to exist within an asset class. - #[codec(index = 2)] - DepositHeld(Balance), - /// A deposit was placed for this account to exist, but it has been refunded. - #[codec(index = 3)] - DepositRefunded, - /// Some other `AccountId` has placed a deposit to make this account exist. - /// An account with such a reason might not be referenced in `system`. - #[codec(index = 4)] - DepositFrom(AccountId, Balance), -} - -impl ExistenceReason -where - AccountId: Clone, -{ - pub fn take_deposit(&mut self) -> Option { - if !matches!(self, ExistenceReason::DepositHeld(_)) { - return None - } - if let ExistenceReason::DepositHeld(deposit) = - core::mem::replace(self, ExistenceReason::DepositRefunded) - { - Some(deposit) - } else { - None - } - } - - pub fn take_deposit_from(&mut self) -> Option<(AccountId, Balance)> { - if !matches!(self, ExistenceReason::DepositFrom(..)) { - return None - } - if let ExistenceReason::DepositFrom(depositor, deposit) = - core::mem::replace(self, ExistenceReason::DepositRefunded) - { - Some((depositor, deposit)) - } else { - None - } - } -} - -#[test] -fn ensure_bool_decodes_to_liquid_or_frozen() { - assert_eq!(false.encode(), AccountStatus::Liquid.encode()); - assert_eq!(true.encode(), AccountStatus::Frozen.encode()); -} - -/// The status of an asset account. -#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub enum AccountStatus { - /// Asset account can receive and transfer the assets. - Liquid, - /// Asset account cannot transfer the assets. - Frozen, - /// Asset account cannot receive and transfer the assets. - Blocked, -} -impl AccountStatus { - /// Returns `true` if frozen or blocked. - pub fn is_frozen(&self) -> bool { - matches!(self, AccountStatus::Frozen | AccountStatus::Blocked) - } - /// Returns `true` if blocked. - pub fn is_blocked(&self) -> bool { - matches!(self, AccountStatus::Blocked) - } -} - -#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub struct AssetAccount { - /// The account's balance. - /// - /// The part of the `balance` may be frozen by the [`Config::Freezer`]. The on-hold portion is - /// not included here and is tracked by the [`Config::Holder`]. - pub balance: Balance, - /// The status of the account. - pub status: AccountStatus, - /// The reason for the existence of the account. - pub reason: ExistenceReason, - /// Additional "sidecar" data, in case some other pallet wants to use this storage item. - pub extra: Extra, -} - -#[derive(Clone, Encode, Decode, Eq, PartialEq, Default, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub struct AssetMetadata { - /// The balance deposited for this metadata. - /// - /// This pays for the data stored in this struct. - pub deposit: DepositBalance, - /// The user friendly name of this asset. Limited in length by `StringLimit`. - pub name: BoundedString, - /// The ticker symbol for this asset. Limited in length by `StringLimit`. - pub symbol: BoundedString, - /// The number of decimals this asset uses to represent one unit. - pub decimals: u8, - /// Whether the asset metadata may be changed by a non Force origin. - pub is_frozen: bool, -} - -/// Trait for allowing a minimum balance on the account to be specified, beyond the -/// `minimum_balance` of the asset. This is additive - the `minimum_balance` of the asset must be -/// met *and then* anything here in addition. -pub trait FrozenBalance { - /// Return the frozen balance. - /// - /// Generally, the balance of every account must be at least the sum of this (if `Some`) and - /// the asset's `minimum_balance` (the latter since there may be complications to destroying an - /// asset's account completely). - /// - /// Under normal behaviour, the account balance should not go below the sum of this (if `Some`) - /// and the asset's minimum balance. However, the account balance may reasonably begin below - /// this sum (e.g. if less than the sum had ever been transferred into the account). - /// - /// In special cases (privileged intervention) the account balance may also go below the sum. - /// - /// If `None` is returned, then nothing special is enforced. - fn frozen_balance(asset: AssetId, who: &AccountId) -> Option; - - /// Called after an account has been removed. - fn died(asset: AssetId, who: &AccountId); - - /// Return a value that indicates if there are registered freezes for a given asset. - fn contains_freezes(asset: AssetId) -> bool; -} - -impl FrozenBalance for () { - fn frozen_balance(_: AssetId, _: &AccountId) -> Option { - None - } - fn died(_: AssetId, _: &AccountId) {} - fn contains_freezes(_: AssetId) -> bool { - false - } -} - -/// This trait indicates a balance that is _on hold_ for an asset account. -/// -/// A balance _on hold_ is a balance that, while is assigned to an account, -/// is outside the direct control of it. Instead, is being _held_ by the -/// system logic (i.e. Pallets) and can be eventually burned or released. -pub trait BalanceOnHold { - /// Return the held balance. - /// - /// If `Some`, it means some balance is _on hold_, and it can be - /// infallibly burned. - /// - /// If `None` is returned, then no balance is _on hold_ for `who`'s asset - /// account. - fn balance_on_hold(asset: AssetId, who: &AccountId) -> Option; - - /// Called after an account has been removed. - /// - /// It is expected that this method is called only when there is no balance - /// on hold. Otherwise, an account should not be removed. - fn died(asset: AssetId, who: &AccountId); - - /// Return a value that indicates if there are registered holds for a given asset. - fn contains_holds(asset: AssetId) -> bool; -} - -impl BalanceOnHold for () { - fn balance_on_hold(_: AssetId, _: &AccountId) -> Option { - None - } - fn died(_: AssetId, _: &AccountId) {} - fn contains_holds(_: AssetId) -> bool { - false - } -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct TransferFlags { - /// The debited account must stay alive at the end of the operation; an error is returned if - /// this cannot be achieved legally. - pub keep_alive: bool, - /// Less than the amount specified needs be debited by the operation for it to be considered - /// successful. If `false`, then the amount debited will always be at least the amount - /// specified. - pub best_effort: bool, - /// Any additional funds debited (due to minimum balance requirements) should be burned rather - /// than credited to the destination account. - pub burn_dust: bool, -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct DebitFlags { - /// The debited account must stay alive at the end of the operation; an error is returned if - /// this cannot be achieved legally. - pub keep_alive: bool, - /// Less than the amount specified needs be debited by the operation for it to be considered - /// successful. If `false`, then the amount debited will always be at least the amount - /// specified. - pub best_effort: bool, -} - -impl From for DebitFlags { - fn from(f: TransferFlags) -> Self { - Self { keep_alive: f.keep_alive, best_effort: f.best_effort } - } -} - -/// Possible errors when converting between external and asset balances. -#[derive(Eq, PartialEq, Copy, Clone, RuntimeDebug, Encode, Decode)] -pub enum ConversionError { - /// The external minimum balance must not be zero. - MinBalanceZero, - /// The asset is not present in storage. - AssetMissing, - /// The asset is not sufficient and thus does not have a reliable `min_balance` so it cannot be - /// converted. - AssetNotSufficient, -} - -// Type alias for `frame_system`'s account id. -type AccountIdOf = ::AccountId; -// This pallet's asset id and balance type. -type AssetIdOf = >::AssetId; -type AssetBalanceOf = >::Balance; -// Generic fungible balance type. -type BalanceOf = >>::Balance; - -/// Converts a balance value into an asset balance based on the ratio between the fungible's -/// minimum balance and the minimum asset balance. -pub struct BalanceToAssetBalance(PhantomData<(F, T, CON, I)>); -impl ConversionToAssetBalance, AssetIdOf, AssetBalanceOf> - for BalanceToAssetBalance -where - F: fungible::Inspect>, - T: Config, - I: 'static, - CON: Convert, AssetBalanceOf>, -{ - type Error = ConversionError; - - /// Convert the given balance value into an asset balance based on the ratio between the - /// fungible's minimum balance and the minimum asset balance. - /// - /// Will return `Err` if the asset is not found, not sufficient or the fungible's minimum - /// balance is zero. - fn to_asset_balance( - balance: BalanceOf, - asset_id: AssetIdOf, - ) -> Result, ConversionError> { - let asset = Asset::::get(asset_id).ok_or(ConversionError::AssetMissing)?; - // only sufficient assets have a min balance with reliable value - ensure!(asset.is_sufficient, ConversionError::AssetNotSufficient); - let min_balance = CON::convert(F::minimum_balance()); - // make sure we don't divide by zero - ensure!(!min_balance.is_zero(), ConversionError::MinBalanceZero); - let balance = CON::convert(balance); - // balance * asset.min_balance / min_balance - Ok(FixedU128::saturating_from_rational(asset.min_balance, min_balance) - .saturating_mul_int(balance)) - } -} diff --git a/pallets/assets/src/weights.rs b/pallets/assets/src/weights.rs deleted file mode 100644 index 8504d598..00000000 --- a/pallets/assets/src/weights.rs +++ /dev/null @@ -1,1131 +0,0 @@ -// 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. - -// 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_assets` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2025-05-18, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `c47a012f15ca`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` - -// Executed Command: -// frame-omni-bencher -// v1 -// benchmark -// pallet -// --extrinsic=* -// --runtime=target/production/wbuild/kitchensink-runtime/kitchensink_runtime.wasm -// --pallet=pallet_assets -// --header=/__w/polkadot-sdk/polkadot-sdk/substrate/HEADER-APACHE2 -// --output=/__w/polkadot-sdk/polkadot-sdk/substrate/frame/assets/src/weights.rs -// --wasm-execution=compiled -// --steps=50 -// --repeat=20 -// --heap-pages=4096 -// --template=substrate/.maintain/frame-weight-template.hbs -// --no-storage-info -// --no-min-squares -// --no-median-slopes -// --exclude-pallets=pallet_xcm,pallet_xcm_benchmarks::fungible,pallet_xcm_benchmarks::generic,pallet_nomination_pools,pallet_remark,pallet_transaction_storage - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] -#![allow(dead_code)] - -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use core::marker::PhantomData; - -/// Weight functions needed for `pallet_assets`. -pub trait WeightInfo { - fn create() -> Weight; - fn force_create() -> Weight; - fn start_destroy() -> Weight; - fn destroy_accounts(c: u32) -> Weight; - fn destroy_approvals(a: u32) -> Weight; - fn finish_destroy() -> Weight; - fn mint() -> Weight; - fn burn() -> Weight; - fn transfer() -> Weight; - fn transfer_keep_alive() -> Weight; - fn force_transfer() -> Weight; - fn freeze() -> Weight; - fn thaw() -> Weight; - fn freeze_asset() -> Weight; - fn thaw_asset() -> Weight; - fn transfer_ownership() -> Weight; - fn set_team() -> Weight; - fn set_metadata(n: u32, s: u32) -> Weight; - fn clear_metadata() -> Weight; - fn force_set_metadata(n: u32, s: u32) -> Weight; - fn force_clear_metadata() -> Weight; - fn force_asset_status() -> Weight; - fn approve_transfer() -> Weight; - fn transfer_approved() -> Weight; - fn cancel_approval() -> Weight; - fn force_cancel_approval() -> Weight; - fn set_min_balance() -> Weight; - fn touch() -> Weight; - fn touch_other() -> Weight; - fn refund() -> Weight; - fn refund_other() -> Weight; - fn block() -> Weight; - fn transfer_all() -> Weight; - fn total_issuance() -> Weight; - fn balance() -> Weight; - fn allowance() -> Weight; - fn set_reserves(n: u32) -> Weight; - fn migration_v2_foreign_asset_set_reserve_weight() -> Weight { - // disabled by default, force explicit benchmarking - Weight::MAX - } -} - -/// Weights for `pallet_assets` using the Substrate node and recommended hardware. -pub struct SubstrateWeight(PhantomData); - -impl WeightInfo for SubstrateWeight { - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::NextAssetId` (r:1 w:1) - /// Proof: `Assets::NextAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// - /// Note: NextAssetId write accounts for AutoIncAssetId callback which increments the ID. - fn create() -> Weight { - // Proof Size summary in bytes: - // Measured: `326` - // Estimated: `3675` - // Minimum execution time: 30_517_000 picoseconds. - Weight::from_parts(31_518_000, 3675) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::NextAssetId` (r:1 w:1) - /// Proof: `Assets::NextAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// - /// Note: NextAssetId write accounts for AutoIncAssetId callback which increments the ID. - fn force_create() -> Weight { - // Proof Size summary in bytes: - // Measured: `186` - // Estimated: `3675` - // Minimum execution time: 12_469_000 picoseconds. - Weight::from_parts(13_002_000, 3675) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn start_destroy() -> Weight { - // Proof Size summary in bytes: - // Measured: `418` - // Estimated: `3675` - // Minimum execution time: 14_767_000 picoseconds. - Weight::from_parts(15_425_000, 3675) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:1001 w:1000) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1000 w:1000) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// The range of component `c` is `[0, 1000]`. - fn destroy_accounts(c: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `104 + c * (208 ±0)` - // Estimated: `3675 + c * (2609 ±0)` - // Minimum execution time: 18_900_000 picoseconds. - Weight::from_parts(19_222_000, 3675) - // Standard Error: 25_984 - .saturating_add(Weight::from_parts(15_436_025, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_parts(0, 2609).saturating_mul(c.into())) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Approvals` (r:1001 w:1000) - /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) - /// The range of component `a` is `[0, 1000]`. - fn destroy_approvals(a: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `555 + a * (86 ±0)` - // Estimated: `3675 + a * (2623 ±0)` - // Minimum execution time: 19_685_000 picoseconds. - Weight::from_parts(20_138_000, 3675) - // Standard Error: 10_401 - .saturating_add(Weight::from_parts(15_779_729, 0).saturating_mul(a.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_parts(0, 2623).saturating_mul(a.into())) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Metadata` (r:1 w:0) - /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - fn finish_destroy() -> Weight { - // Proof Size summary in bytes: - // Measured: `384` - // Estimated: `3675` - // Minimum execution time: 15_652_000 picoseconds. - Weight::from_parts(16_159_000, 3675) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn mint() -> Weight { - // Proof Size summary in bytes: - // Measured: `384` - // Estimated: `3675` - // Minimum execution time: 25_446_000 picoseconds. - Weight::from_parts(26_623_000, 3675) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn burn() -> Weight { - // Proof Size summary in bytes: - // Measured: `492` - // Estimated: `3675` - // Minimum execution time: 33_608_000 picoseconds. - Weight::from_parts(34_662_000, 3675) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn transfer() -> Weight { - // Proof Size summary in bytes: - // Measured: `531` - // Estimated: `6208` - // Minimum execution time: 46_426_000 picoseconds. - Weight::from_parts(47_772_000, 6208) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn transfer_keep_alive() -> Weight { - // Proof Size summary in bytes: - // Measured: `531` - // Estimated: `6208` - // Minimum execution time: 42_162_000 picoseconds. - Weight::from_parts(42_964_000, 6208) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn force_transfer() -> Weight { - // Proof Size summary in bytes: - // Measured: `531` - // Estimated: `6208` - // Minimum execution time: 46_399_000 picoseconds. - Weight::from_parts(47_580_000, 6208) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:0) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn freeze() -> Weight { - // Proof Size summary in bytes: - // Measured: `492` - // Estimated: `3675` - // Minimum execution time: 19_369_000 picoseconds. - Weight::from_parts(19_799_000, 3675) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:0) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn thaw() -> Weight { - // Proof Size summary in bytes: - // Measured: `492` - // Estimated: `3675` - // Minimum execution time: 19_145_000 picoseconds. - Weight::from_parts(19_873_000, 3675) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn freeze_asset() -> Weight { - // Proof Size summary in bytes: - // Measured: `418` - // Estimated: `3675` - // Minimum execution time: 14_728_000 picoseconds. - Weight::from_parts(15_405_000, 3675) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn thaw_asset() -> Weight { - // Proof Size summary in bytes: - // Measured: `418` - // Estimated: `3675` - // Minimum execution time: 14_730_000 picoseconds. - Weight::from_parts(15_284_000, 3675) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Metadata` (r:1 w:0) - /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - fn transfer_ownership() -> Weight { - // Proof Size summary in bytes: - // Measured: `384` - // Estimated: `3675` - // Minimum execution time: 16_247_000 picoseconds. - Weight::from_parts(16_890_000, 3675) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn set_team() -> Weight { - // Proof Size summary in bytes: - // Measured: `384` - // Estimated: `3675` - // Minimum execution time: 14_206_000 picoseconds. - Weight::from_parts(14_856_000, 3675) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:0) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Metadata` (r:1 w:1) - /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - /// The range of component `n` is `[0, 50]`. - /// The range of component `s` is `[0, 50]`. - fn set_metadata(_n: u32, s: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `384` - // Estimated: `3675` - // Minimum execution time: 30_217_000 picoseconds. - Weight::from_parts(31_869_256, 3675) - // Standard Error: 1_165 - .saturating_add(Weight::from_parts(826, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:0) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Metadata` (r:1 w:1) - /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - fn clear_metadata() -> Weight { - // Proof Size summary in bytes: - // Measured: `548` - // Estimated: `3675` - // Minimum execution time: 29_914_000 picoseconds. - Weight::from_parts(30_680_000, 3675) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:0) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Metadata` (r:1 w:1) - /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - /// The range of component `n` is `[0, 50]`. - /// The range of component `s` is `[0, 50]`. - fn force_set_metadata(n: u32, s: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `223` - // Estimated: `3675` - // Minimum execution time: 13_045_000 picoseconds. - Weight::from_parts(13_680_991, 3675) - // Standard Error: 528 - .saturating_add(Weight::from_parts(3_162, 0).saturating_mul(n.into())) - // Standard Error: 528 - .saturating_add(Weight::from_parts(2_931, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:0) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Metadata` (r:1 w:1) - /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - fn force_clear_metadata() -> Weight { - // Proof Size summary in bytes: - // Measured: `548` - // Estimated: `3675` - // Minimum execution time: 29_440_000 picoseconds. - Weight::from_parts(30_302_000, 3675) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn force_asset_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `384` - // Estimated: `3675` - // Minimum execution time: 14_175_000 picoseconds. - Weight::from_parts(14_802_000, 3675) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Approvals` (r:1 w:1) - /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) - fn approve_transfer() -> Weight { - // Proof Size summary in bytes: - // Measured: `418` - // Estimated: `3675` - // Minimum execution time: 34_027_000 picoseconds. - Weight::from_parts(34_976_000, 3675) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Approvals` (r:1 w:1) - /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn transfer_approved() -> Weight { - // Proof Size summary in bytes: - // Measured: `701` - // Estimated: `6208` - // Minimum execution time: 66_073_000 picoseconds. - Weight::from_parts(69_478_000, 6208) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(5_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Approvals` (r:1 w:1) - /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) - fn cancel_approval() -> Weight { - // Proof Size summary in bytes: - // Measured: `588` - // Estimated: `3675` - // Minimum execution time: 35_452_000 picoseconds. - Weight::from_parts(36_883_000, 3675) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Approvals` (r:1 w:1) - /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) - fn force_cancel_approval() -> Weight { - // Proof Size summary in bytes: - // Measured: `588` - // Estimated: `3675` - // Minimum execution time: 35_154_000 picoseconds. - Weight::from_parts(36_578_000, 3675) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn set_min_balance() -> Weight { - // Proof Size summary in bytes: - // Measured: `384` - // Estimated: `3675` - // Minimum execution time: 14_904_000 picoseconds. - Weight::from_parts(15_505_000, 3675) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn touch() -> Weight { - // Proof Size summary in bytes: - // Measured: `486` - // Estimated: `3675` - // Minimum execution time: 35_434_000 picoseconds. - Weight::from_parts(36_636_000, 3675) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn touch_other() -> Weight { - // Proof Size summary in bytes: - // Measured: `384` - // Estimated: `3675` - // Minimum execution time: 33_361_000 picoseconds. - Weight::from_parts(34_522_000, 3675) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn refund() -> Weight { - // Proof Size summary in bytes: - // Measured: `612` - // Estimated: `3675` - // Minimum execution time: 33_977_000 picoseconds. - Weight::from_parts(34_979_000, 3675) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn refund_other() -> Weight { - // Proof Size summary in bytes: - // Measured: `543` - // Estimated: `3675` - // Minimum execution time: 31_928_000 picoseconds. - Weight::from_parts(33_214_000, 3675) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:0) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn block() -> Weight { - // Proof Size summary in bytes: - // Measured: `492` - // Estimated: `3675` - // Minimum execution time: 19_186_000 picoseconds. - Weight::from_parts(20_104_000, 3675) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn transfer_all() -> Weight { - // Proof Size summary in bytes: - // Measured: `531` - // Estimated: `6208` - // Minimum execution time: 55_413_000 picoseconds. - Weight::from_parts(56_798_000, 6208) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:0) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn total_issuance() -> Weight { - // Proof Size summary in bytes: - // Measured: `418` - // Estimated: `3675` - // Minimum execution time: 8_792_000 picoseconds. - Weight::from_parts(9_095_000, 3675) - .saturating_add(T::DbWeight::get().reads(1_u64)) - } - /// Storage: `Assets::Account` (r:1 w:0) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn balance() -> Weight { - // Proof Size summary in bytes: - // Measured: `255` - // Estimated: `3599` - // Minimum execution time: 8_924_000 picoseconds. - Weight::from_parts(9_407_000, 3599) - .saturating_add(T::DbWeight::get().reads(1_u64)) - } - /// Storage: `Assets::Approvals` (r:1 w:0) - /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) - fn allowance() -> Weight { - // Proof Size summary in bytes: - // Measured: `350` - // Estimated: `3613` - // Minimum execution time: 11_348_000 picoseconds. - Weight::from_parts(11_882_000, 3613) - .saturating_add(T::DbWeight::get().reads(1_u64)) - } - /// The range of component `n` is `[0, 5]`. - /// The range of component `n` is `[0, 5]`. - /// The range of component `n` is `[0, 5]`. - fn set_reserves(n: u32) -> Weight { - Weight::from_parts(31_972_000, 3675) - // Standard Error: 13_748 - .saturating_add(Weight::from_parts(198_975, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } -} - -// For backwards compatibility and tests. -impl WeightInfo for () { - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::NextAssetId` (r:1 w:1) - /// Proof: `Assets::NextAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// - /// Note: NextAssetId write accounts for AutoIncAssetId callback which increments the ID. - fn create() -> Weight { - // Proof Size summary in bytes: - // Measured: `326` - // Estimated: `3675` - // Minimum execution time: 30_517_000 picoseconds. - Weight::from_parts(31_518_000, 3675) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::NextAssetId` (r:1 w:1) - /// Proof: `Assets::NextAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// - /// Note: NextAssetId write accounts for AutoIncAssetId callback which increments the ID. - fn force_create() -> Weight { - // Proof Size summary in bytes: - // Measured: `186` - // Estimated: `3675` - // Minimum execution time: 12_469_000 picoseconds. - Weight::from_parts(13_002_000, 3675) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn start_destroy() -> Weight { - // Proof Size summary in bytes: - // Measured: `418` - // Estimated: `3675` - // Minimum execution time: 14_767_000 picoseconds. - Weight::from_parts(15_425_000, 3675) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:1001 w:1000) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1000 w:1000) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// The range of component `c` is `[0, 1000]`. - fn destroy_accounts(c: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `104 + c * (208 ±0)` - // Estimated: `3675 + c * (2609 ±0)` - // Minimum execution time: 18_900_000 picoseconds. - Weight::from_parts(19_222_000, 3675) - // Standard Error: 25_984 - .saturating_add(Weight::from_parts(15_436_025, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(c.into()))) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_parts(0, 2609).saturating_mul(c.into())) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Approvals` (r:1001 w:1000) - /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) - /// The range of component `a` is `[0, 1000]`. - fn destroy_approvals(a: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `555 + a * (86 ±0)` - // Estimated: `3675 + a * (2623 ±0)` - // Minimum execution time: 19_685_000 picoseconds. - Weight::from_parts(20_138_000, 3675) - // Standard Error: 10_401 - .saturating_add(Weight::from_parts(15_779_729, 0).saturating_mul(a.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_parts(0, 2623).saturating_mul(a.into())) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Metadata` (r:1 w:0) - /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - fn finish_destroy() -> Weight { - // Proof Size summary in bytes: - // Measured: `384` - // Estimated: `3675` - // Minimum execution time: 15_652_000 picoseconds. - Weight::from_parts(16_159_000, 3675) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn mint() -> Weight { - // Proof Size summary in bytes: - // Measured: `384` - // Estimated: `3675` - // Minimum execution time: 25_446_000 picoseconds. - Weight::from_parts(26_623_000, 3675) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn burn() -> Weight { - // Proof Size summary in bytes: - // Measured: `492` - // Estimated: `3675` - // Minimum execution time: 33_608_000 picoseconds. - Weight::from_parts(34_662_000, 3675) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn transfer() -> Weight { - // Proof Size summary in bytes: - // Measured: `531` - // Estimated: `6208` - // Minimum execution time: 46_426_000 picoseconds. - Weight::from_parts(47_772_000, 6208) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn transfer_keep_alive() -> Weight { - // Proof Size summary in bytes: - // Measured: `531` - // Estimated: `6208` - // Minimum execution time: 42_162_000 picoseconds. - Weight::from_parts(42_964_000, 6208) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn force_transfer() -> Weight { - // Proof Size summary in bytes: - // Measured: `531` - // Estimated: `6208` - // Minimum execution time: 46_399_000 picoseconds. - Weight::from_parts(47_580_000, 6208) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:0) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn freeze() -> Weight { - // Proof Size summary in bytes: - // Measured: `492` - // Estimated: `3675` - // Minimum execution time: 19_369_000 picoseconds. - Weight::from_parts(19_799_000, 3675) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:0) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn thaw() -> Weight { - // Proof Size summary in bytes: - // Measured: `492` - // Estimated: `3675` - // Minimum execution time: 19_145_000 picoseconds. - Weight::from_parts(19_873_000, 3675) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn freeze_asset() -> Weight { - // Proof Size summary in bytes: - // Measured: `418` - // Estimated: `3675` - // Minimum execution time: 14_728_000 picoseconds. - Weight::from_parts(15_405_000, 3675) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn thaw_asset() -> Weight { - // Proof Size summary in bytes: - // Measured: `418` - // Estimated: `3675` - // Minimum execution time: 14_730_000 picoseconds. - Weight::from_parts(15_284_000, 3675) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Metadata` (r:1 w:0) - /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - fn transfer_ownership() -> Weight { - // Proof Size summary in bytes: - // Measured: `384` - // Estimated: `3675` - // Minimum execution time: 16_247_000 picoseconds. - Weight::from_parts(16_890_000, 3675) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn set_team() -> Weight { - // Proof Size summary in bytes: - // Measured: `384` - // Estimated: `3675` - // Minimum execution time: 14_206_000 picoseconds. - Weight::from_parts(14_856_000, 3675) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:0) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Metadata` (r:1 w:1) - /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - /// The range of component `n` is `[0, 50]`. - /// The range of component `s` is `[0, 50]`. - fn set_metadata(_n: u32, s: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `384` - // Estimated: `3675` - // Minimum execution time: 30_217_000 picoseconds. - Weight::from_parts(31_869_256, 3675) - // Standard Error: 1_165 - .saturating_add(Weight::from_parts(826, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:0) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Metadata` (r:1 w:1) - /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - fn clear_metadata() -> Weight { - // Proof Size summary in bytes: - // Measured: `548` - // Estimated: `3675` - // Minimum execution time: 29_914_000 picoseconds. - Weight::from_parts(30_680_000, 3675) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:0) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Metadata` (r:1 w:1) - /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - /// The range of component `n` is `[0, 50]`. - /// The range of component `s` is `[0, 50]`. - fn force_set_metadata(n: u32, s: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `223` - // Estimated: `3675` - // Minimum execution time: 13_045_000 picoseconds. - Weight::from_parts(13_680_991, 3675) - // Standard Error: 528 - .saturating_add(Weight::from_parts(3_162, 0).saturating_mul(n.into())) - // Standard Error: 528 - .saturating_add(Weight::from_parts(2_931, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:0) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Metadata` (r:1 w:1) - /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - fn force_clear_metadata() -> Weight { - // Proof Size summary in bytes: - // Measured: `548` - // Estimated: `3675` - // Minimum execution time: 29_440_000 picoseconds. - Weight::from_parts(30_302_000, 3675) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn force_asset_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `384` - // Estimated: `3675` - // Minimum execution time: 14_175_000 picoseconds. - Weight::from_parts(14_802_000, 3675) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Approvals` (r:1 w:1) - /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) - fn approve_transfer() -> Weight { - // Proof Size summary in bytes: - // Measured: `418` - // Estimated: `3675` - // Minimum execution time: 34_027_000 picoseconds. - Weight::from_parts(34_976_000, 3675) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Approvals` (r:1 w:1) - /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn transfer_approved() -> Weight { - // Proof Size summary in bytes: - // Measured: `701` - // Estimated: `6208` - // Minimum execution time: 66_073_000 picoseconds. - Weight::from_parts(69_478_000, 6208) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(5_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Approvals` (r:1 w:1) - /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) - fn cancel_approval() -> Weight { - // Proof Size summary in bytes: - // Measured: `588` - // Estimated: `3675` - // Minimum execution time: 35_452_000 picoseconds. - Weight::from_parts(36_883_000, 3675) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Approvals` (r:1 w:1) - /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) - fn force_cancel_approval() -> Weight { - // Proof Size summary in bytes: - // Measured: `588` - // Estimated: `3675` - // Minimum execution time: 35_154_000 picoseconds. - Weight::from_parts(36_578_000, 3675) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn set_min_balance() -> Weight { - // Proof Size summary in bytes: - // Measured: `384` - // Estimated: `3675` - // Minimum execution time: 14_904_000 picoseconds. - Weight::from_parts(15_505_000, 3675) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn touch() -> Weight { - // Proof Size summary in bytes: - // Measured: `486` - // Estimated: `3675` - // Minimum execution time: 35_434_000 picoseconds. - Weight::from_parts(36_636_000, 3675) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn touch_other() -> Weight { - // Proof Size summary in bytes: - // Measured: `384` - // Estimated: `3675` - // Minimum execution time: 33_361_000 picoseconds. - Weight::from_parts(34_522_000, 3675) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn refund() -> Weight { - // Proof Size summary in bytes: - // Measured: `612` - // Estimated: `3675` - // Minimum execution time: 33_977_000 picoseconds. - Weight::from_parts(34_979_000, 3675) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn refund_other() -> Weight { - // Proof Size summary in bytes: - // Measured: `543` - // Estimated: `3675` - // Minimum execution time: 31_928_000 picoseconds. - Weight::from_parts(33_214_000, 3675) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:0) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:1 w:1) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn block() -> Weight { - // Proof Size summary in bytes: - // Measured: `492` - // Estimated: `3675` - // Minimum execution time: 19_186_000 picoseconds. - Weight::from_parts(20_104_000, 3675) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn transfer_all() -> Weight { - // Proof Size summary in bytes: - // Measured: `531` - // Estimated: `6208` - // Minimum execution time: 55_413_000 picoseconds. - Weight::from_parts(56_798_000, 6208) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) - } - /// Storage: `Assets::Asset` (r:1 w:0) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn total_issuance() -> Weight { - // Proof Size summary in bytes: - // Measured: `418` - // Estimated: `3675` - // Minimum execution time: 8_792_000 picoseconds. - Weight::from_parts(9_095_000, 3675) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - } - /// Storage: `Assets::Account` (r:1 w:0) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn balance() -> Weight { - // Proof Size summary in bytes: - // Measured: `255` - // Estimated: `3599` - // Minimum execution time: 8_924_000 picoseconds. - Weight::from_parts(9_407_000, 3599) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - } - /// Storage: `Assets::Approvals` (r:1 w:0) - /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) - fn allowance() -> Weight { - // Proof Size summary in bytes: - // Measured: `350` - // Estimated: `3613` - // Minimum execution time: 11_348_000 picoseconds. - Weight::from_parts(11_882_000, 3613) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - } - /// The range of component `n` is `[0, 5]`. - /// The range of component `n` is `[0, 5]`. - /// The range of component `n` is `[0, 5]`. - fn set_reserves(n: u32) -> Weight { - Weight::from_parts(31_972_000, 3675) - // Standard Error: 13_748 - .saturating_add(Weight::from_parts(198_975, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } -} diff --git a/pallets/reversible-transfers/src/tests/test_reversible_transfers.rs b/pallets/reversible-transfers/src/tests/test_reversible_transfers.rs index 10526b37..e0fcce28 100644 --- a/pallets/reversible-transfers/src/tests/test_reversible_transfers.rs +++ b/pallets/reversible-transfers/src/tests/test_reversible_transfers.rs @@ -1235,331 +1235,6 @@ fn schedule_transfer_with_delay_works() { }); } -#[cfg(any())] -#[test] -fn asset_hold_does_not_block_spending() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - let sender: AccountId = alice(); // high-security from genesis - let guardian = bob(); // from genesis config for 1 - let recipient: AccountId = dave(); - let third_party: AccountId = account_id(9); - let asset_id: u32 = 314; - let spend_amount: Balance = 3_000; - - create_asset(asset_id, sender.clone(), None); - let sender_before = asset_balance(asset_id, &sender); - let recipient_before = asset_balance(asset_id, &recipient); - let third_before = asset_balance(asset_id, &third_party); - let hold_amount = sender_before - spend_amount / 2; - - // Calculate tx_id before scheduling - let asset_transfer_call: RuntimeCall = pallet_assets::Call::::transfer_keep_alive { - id: codec::Compact(asset_id), - target: recipient.clone(), - amount: hold_amount, - } - .into(); - let tx_id = calculate_tx_id::(sender.clone(), &asset_transfer_call); - - // Schedule an asset transfer to create a hold on `sender`. - assert_ok!(ReversibleTransfers::schedule_asset_transfer( - RuntimeOrigin::signed(sender.clone()), - asset_id, - recipient.clone(), - hold_amount, - )); - - // Hold exists; free balance reduced by hold amount. - assert_eq!(asset_holds(asset_id, &sender), hold_amount); - let free_after_hold = sender_before - hold_amount; - assert_eq!(asset_balance(asset_id, &sender), free_after_hold); - assert_eq!(asset_balance(asset_id, &recipient), recipient_before); - - // With holds, spending up to free balance is allowed; leave min_balance. - let min = as AssetsInspect<_>>::minimum_balance(asset_id); - let spend = free_after_hold.saturating_sub(min); - assert_ok!(pallet_assets::Pallet::::transfer_keep_alive( - RuntimeOrigin::signed(sender.clone()), - codec::Compact(asset_id), - third_party.clone(), - spend, - )); - - // Verify spend succeeded while hold remains. - assert_eq!(asset_holds(asset_id, &sender), hold_amount); - assert_eq!(asset_balance(asset_id, &sender), min); - assert_eq!(asset_balance(asset_id, &third_party), third_before + spend); - - // Pending remains and will execute later; cancel it now to clean up and credit guardian. - assert_ok!(ReversibleTransfers::cancel(RuntimeOrigin::signed(guardian.clone()), tx_id)); - assert!(ReversibleTransfers::pending_dispatches(tx_id).is_none()); - assert_eq!(asset_holds(asset_id, &sender), 0); - }); -} - -#[cfg(any())] -#[test] -fn asset_hold_blocks_only_held_portion() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - let sender: AccountId = alice(); // high-security from genesis - let recipient: AccountId = dave(); - let third_party: AccountId = account_id(9); - let asset_id: u32 = 777; - - create_asset(asset_id, sender.clone(), None); - let sender_before = asset_balance(asset_id, &sender); - let third_before = asset_balance(asset_id, &third_party); - let recipient_before = asset_balance(asset_id, &recipient); - - // Place a hold smaller than free so some spend is still allowed - let hold_amount: Balance = sender_before / 10; // 10% - assert_ok!(ReversibleTransfers::schedule_asset_transfer( - RuntimeOrigin::signed(sender.clone()), - asset_id, - recipient.clone(), - hold_amount, - )); - assert_eq!(asset_holds(asset_id, &sender), hold_amount); - assert_eq!(asset_balance(asset_id, &sender), sender_before - hold_amount); - assert_eq!(asset_balance(asset_id, &recipient), recipient_before); - - // Attempt to cross the held barrier by 1; must fail - let over = (sender_before - hold_amount).saturating_add(1); - assert_err!( - pallet_assets::Pallet::::transfer_keep_alive( - RuntimeOrigin::signed(sender.clone()), - codec::Compact(asset_id), - third_party.clone(), - over, - ), - pallet_assets::Error::::BalanceLow - ); - - // Spend the free amount but keep account alive with min. - let free_amount = sender_before - hold_amount; - let min = as AssetsInspect<_>>::minimum_balance(asset_id); - let spend = free_amount.saturating_sub(min); - assert_ok!(pallet_assets::Pallet::::transfer_keep_alive( - RuntimeOrigin::signed(sender.clone()), - codec::Compact(asset_id), - third_party.clone(), - spend, - )); - assert_eq!(asset_balance(asset_id, &sender), min); - assert_eq!(asset_balance(asset_id, &third_party), third_before + spend); - }); -} - -#[cfg(any())] -#[test] -fn asset_hold_prevents_spend_over_free() { - // Testing asset hold because it was quite confusing in code - new_test_ext().execute_with(|| { - System::set_block_number(1); - let sender: AccountId = charlie(); // has system account in genesis - let recipient: AccountId = dave(); // has system account in genesis - let asset_id: u32 = 808; - - // Create asset and give sender 20 units - create_asset(asset_id, sender.clone(), Some(20)); - - // Create a 10-unit hold by scheduling an asset transfer with one-time delay (sender is not - // high-security) - assert_ok!(ReversibleTransfers::schedule_asset_transfer_with_delay( - RuntimeOrigin::signed(sender.clone()), - asset_id, - recipient.clone(), - 10, - BlockNumberOrTimestamp::BlockNumber(5), - )); - assert_eq!(asset_holds(asset_id, &sender), 10); - - // Attempt to send 15 (free is only 10 after hold); must fail with BalanceLow - assert_err!( - pallet_assets::Pallet::::transfer_keep_alive( - RuntimeOrigin::signed(sender.clone()), - codec::Compact(asset_id), - recipient.clone(), - 15, - ), - pallet_assets::Error::::BalanceLow - ); - }); -} - -#[cfg(any())] -#[test] -fn recover_funds_is_atomic_when_release_fails() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - - let protected = alice(); // high-security from genesis, guardian = bob - let guardian = bob(); - let asset_operator = charlie(); - let recipient = eve(); - let asset_id: u32 = 4_343; - let amount: Balance = 1_000; - - // Create an asset and block the guardian's asset account so it cannot receive funds. - create_asset(asset_id, asset_operator.clone(), Some(10_000)); - assert_ok!(pallet_assets::Pallet::::mint( - RuntimeOrigin::signed(asset_operator.clone()), - codec::Compact(asset_id), - protected.clone(), - 10_000, - )); - assert_ok!(pallet_assets::Pallet::::mint( - RuntimeOrigin::signed(asset_operator.clone()), - codec::Compact(asset_id), - guardian.clone(), - 1, - )); - assert_ok!(pallet_assets::Pallet::::block( - RuntimeOrigin::signed(asset_operator.clone()), - codec::Compact(asset_id), - guardian.clone(), - )); - - let asset_call: RuntimeCall = pallet_assets::Call::::transfer_keep_alive { - id: codec::Compact(asset_id), - target: recipient.clone(), - amount, - } - .into(); - let tx_id = calculate_tx_id::(protected.clone(), &asset_call); - - assert_ok!(ReversibleTransfers::schedule_asset_transfer( - RuntimeOrigin::signed(protected.clone()), - asset_id, - recipient, - amount, - )); - - let hold_before = asset_holds(asset_id, &protected); - let issuance_before = - as AssetsInspect<_>>::total_issuance(asset_id); - assert_eq!(hold_before, amount); - assert_eq!(ReversibleTransfers::pending_dispatches(tx_id).unwrap().amount, amount); - - // Recovery cannot deliver to the blocked guardian, so the release must roll back fully: - // no fee is burned and the pending transfer is preserved for retry. - assert_ok!(ReversibleTransfers::recover_funds( - RuntimeOrigin::signed(guardian.clone()), - protected.clone(), - )); - System::assert_has_event(Event::TransferRecoveryFailed { tx_id }.into()); - assert_eq!(asset_holds(asset_id, &protected), hold_before); - assert_eq!( - as AssetsInspect<_>>::total_issuance(asset_id), - issuance_before - ); - let pending_after = ReversibleTransfers::pending_dispatches(tx_id).unwrap(); - assert_eq!(pending_after.amount, asset_holds(asset_id, &protected)); - assert!(ReversibleTransfers::pending_transfers_by_sender(&protected).contains(&tx_id)); - - // Repeated recovery stays idempotent: the hold is never partially burned. - assert_ok!(Balances::transfer_keep_alive( - RuntimeOrigin::signed(asset_operator), - protected.clone(), - 100, - )); - assert_ok!(ReversibleTransfers::recover_funds( - RuntimeOrigin::signed(guardian), - protected.clone(), - )); - assert_eq!(asset_holds(asset_id, &protected), hold_before); - assert_eq!( - as AssetsInspect<_>>::total_issuance(asset_id), - issuance_before - ); - }); -} - -#[cfg(any())] -#[test] -fn recover_funds_weight_accounts_for_failed_releases() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - - let protected = alice(); // high-security from genesis, guardian = bob - let guardian = bob(); - let asset_operator = charlie(); - let recipient = eve(); - let asset_id: u32 = 7_777; - let amount: Balance = 1_000; - - // Create an asset and block the guardian's asset account so any asset - // release *to the guardian* during recovery fails. - create_asset(asset_id, asset_operator.clone(), Some(1_000_000)); - assert_ok!(pallet_assets::Pallet::::mint( - RuntimeOrigin::signed(asset_operator.clone()), - codec::Compact(asset_id), - protected.clone(), - 1_000_000, - )); - assert_ok!(pallet_assets::Pallet::::mint( - RuntimeOrigin::signed(asset_operator.clone()), - codec::Compact(asset_id), - guardian.clone(), - 1, - )); - assert_ok!(pallet_assets::Pallet::::block( - RuntimeOrigin::signed(asset_operator.clone()), - codec::Compact(asset_id), - guardian.clone(), - )); - - // Two asset transfers whose recovery release will FAIL (guardian blocked)... - assert_ok!(ReversibleTransfers::schedule_asset_transfer( - RuntimeOrigin::signed(protected.clone()), - asset_id, - recipient.clone(), - amount, - )); - assert_ok!(ReversibleTransfers::schedule_asset_transfer( - RuntimeOrigin::signed(protected.clone()), - asset_id, - recipient, - amount, - )); - // ...and one native transfer whose recovery release SUCCEEDS. - assert_ok!(ReversibleTransfers::schedule_transfer( - RuntimeOrigin::signed(protected.clone()), - guardian.clone(), - amount, - )); - - assert_eq!(ReversibleTransfers::pending_transfers_by_sender(&protected).len(), 3); - - let post = ReversibleTransfers::recover_funds( - RuntimeOrigin::signed(guardian.clone()), - protected.clone(), - ) - .expect("recover_funds should succeed even when some releases fail"); - - // Two asset releases failed (metadata retained), one native release succeeded. - System::assert_has_event( - Event::FundsRecovered { account: protected.clone(), guardian }.into(), - ); - assert_eq!(ReversibleTransfers::pending_transfers_by_sender(&protected).len(), 2); - - // The dispatch inspected 3 pending transfers and attempted a release for - // each, so the charged weight must reflect all 3 processed transfers, not - // just the single successful cancellation. Otherwise a guardian can drive - // unbounded failed-release work while being refunded down to near-zero. - let charged = post.actual_weight.expect("recover_funds returns an explicit post weight"); - let expected_processed = <() as crate::weights::WeightInfo>::recover_funds(3); - let expected_cancelled = <() as crate::weights::WeightInfo>::recover_funds(1); - assert_eq!( - charged, expected_processed, - "recover_funds must charge for every pending transfer it processes (3), \ - not only successful cancellations (was charging as if {expected_cancelled:?})" - ); - }); -} - #[test] fn schedule_transfer_with_error_short_delay() { new_test_ext().execute_with(|| { @@ -2022,49 +1697,6 @@ fn reversible_transfer_records_transfer_proof_on_execution() { }); } -#[cfg(any())] -#[test] -fn reversible_asset_transfer_records_transfer_proof_with_asset_id() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - MockProofRecorder::clear(); - - let user = alice(); // Reversible, delay 10 - let dest = charlie(); - let asset_id = 1u32; - let amount = 100; - - // Create and mint asset to user - create_asset(asset_id, user.clone(), Some(1000)); - - let HighSecurityAccountData { delay, .. } = - ReversibleTransfers::is_high_security(&user).unwrap(); - let start_block = BlockNumberOrTimestamp::BlockNumber(System::block_number()); - let execute_block = start_block.saturating_add(&delay).unwrap(); - - // Schedule asset transfer (no delay parameter - uses account's default) - assert_ok!(ReversibleTransfers::schedule_asset_transfer( - RuntimeOrigin::signed(user.clone()), - asset_id, - dest.clone(), - amount, - )); - - // Run to execution block - run_to_block(execute_block.as_block_number().unwrap()); - - // Transfer proof should be recorded with asset_id - let proofs = MockProofRecorder::get_recorded_proofs(); - assert_eq!(proofs.len(), 1, "Expected exactly one transfer proof to be recorded"); - - let proof = &proofs[0]; - assert_eq!(proof.asset_id, Some(asset_id), "Asset transfer should have Some(asset_id)"); - assert_eq!(proof.from, user, "Transfer proof 'from' should match sender"); - assert_eq!(proof.to, dest, "Transfer proof 'to' should match destination"); - assert_eq!(proof.amount, amount, "Transfer proof amount should match"); - }); -} - #[test] fn cancelled_reversible_transfer_does_not_record_proof() { new_test_ext().execute_with(|| { diff --git a/runtime/src/genesis_config_presets.rs b/runtime/src/genesis_config_presets.rs index 7a91c122..2bc94376 100644 --- a/runtime/src/genesis_config_presets.rs +++ b/runtime/src/genesis_config_presets.rs @@ -148,15 +148,6 @@ fn planck_tech_collective_seed() -> Vec { /// Returns the genesis config populated with given parameters. Treasury is per-profile. /// -/// The treasury account is also the `pallet-assets` owner for **asset id 0**. It is not FRAME -/// `Root`. -/// -/// NOTE: `pallet-assets` asset id 0 is a distinct, unbacked token that merely shares the integer -/// the wormhole uses internally to tag *native* leaves. It is NOT the native token, and minting it -/// does not create or back any native value. The wormhole proof recorder deliberately does not -/// treat asset-0 credits as native deposits (see `record_transfer_proof`); if that ever changes, -/// the asset-0 issuer could mint unbacked native out of the wormhole. -/// /// All endowed addresses automatically get transfer proofs recorded, enabling them to /// spend their funds via ZK proofs. The chain doesn't distinguish between "wormhole /// addresses" and regular addresses - any address can spend via ZK proofs if they From 59ec749b74409eb2f8c324fb17053cdabf08e382 Mon Sep 17 00:00:00 2001 From: illuzen Date: Mon, 3 Aug 2026 15:16:17 +0800 Subject: [PATCH 4/5] docs: sync RUNTIME_SURFACE with assets pallet removal Co-authored-by: Cursor --- docs/RUNTIME_SURFACE.md | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/docs/RUNTIME_SURFACE.md b/docs/RUNTIME_SURFACE.md index 6e58cc13..03ab6bb6 100644 --- a/docs/RUNTIME_SURFACE.md +++ b/docs/RUNTIME_SURFACE.md @@ -77,13 +77,13 @@ The runtime derives `RuntimeCall`, `RuntimeEvent`, `RuntimeError`, `RuntimeOrigi | 14 | `TechReferenda` | `pallet-referenda::Pallet` `45.0.0` | **Inlined** (2nd instance) | yes | | 15 | `TreasuryPallet` | `pallet-treasury` | **Local** (`pallets/treasury`) | yes | | 16 | `Recovery` | `pallet-recovery` `45.0.0` | **Inlined** (`pallets/recovery`) | yes | -| 17 | `Assets` | `pallet-assets` `48.1.0` | **Inlined** (`pallets/assets`) | yes | -| 18 | `AssetsHolder` | `pallet-assets-holder` `0.8.0` | **Inlined** (`pallets/assets-holder`) | (no extrinsics) | +| 17 | — | *(vacant; was `pallet-assets`)* | — | — | +| 18 | — | *(vacant; was `pallet-assets-holder`)* | — | — | | 19 | `Multisig` | `pallet-multisig` | **Local** (`pallets/multisig`) | yes | | 20 | `Wormhole` | `pallet-wormhole` | **Local** (`pallets/wormhole`) | yes | | 21 | `ZkTree` | `pallet-zk-tree` | **Local** (`pallets/zk-tree`) | no | -> Index 4 is intentionally left vacant after `pallet-sudo` removal so downstream indices stay stable. +> Indices 4, 17, and 18 are intentionally left vacant after pallet removals so downstream indices stay stable. --- @@ -137,8 +137,8 @@ All `Config` impls live in `runtime/src/configs/mod.rs` unless noted. - **Calls:** `submit`, `place_decision_deposit`, `refund_decision_deposit`, `cancel`, `kill`, `nudge_referendum`, `one_fewer_deciding`, `refund_submission_deposit`, `set_metadata`. ### Index 11 — `ReversibleTransfers` (`pallet-reversible-transfers`, local) -- `Scheduler = Scheduler`, `DefaultDelay = 1 DAY`, `MinDelayPeriodBlocks = 2`, `MaxGuardianAccounts = 32`, `MaxPendingPerAccount = 16`, `VolumeFee = 1%` (high-security reversals, burned), `ProofRecorder = Wormhole`, `PalletId = "rtpallet"`. -- **Calls:** `set_high_security`(0), `cancel`(1), `execute_transfer`(2), `schedule_transfer`(3), `schedule_transfer_with_delay`(4), `schedule_asset_transfer`(5), `schedule_asset_transfer_with_delay`(6), `recover_funds`(7). +- `AssetId = u32` (retained for wire-format compatibility; asset transfers are rejected), `Scheduler = Scheduler`, `DefaultDelay = 1 DAY`, `MinDelayPeriodBlocks = 2`, `MaxGuardianAccounts = 32`, `MaxPendingPerAccount = 16`, `VolumeFee = 1%` (high-security reversals, burned), `ProofRecorder = Wormhole`, `PalletId = "rtpallet"`. +- **Calls:** `set_high_security`(0), `cancel`(1), `execute_transfer`(2), `schedule_transfer`(3), `schedule_transfer_with_delay`(4), `recover_funds`(7). Call indices 5/6 were `schedule_asset_transfer` / `schedule_asset_transfer_with_delay` (removed with assets); kept vacant so `recover_funds` stays at 7. Pending transfers with `Some(asset_id)` fail with `AssetsNotSupported`. - Backs `HighSecurityConfig` (account whitelist/guardian logic). ### Index 12 — `ConvictionVoting` (`pallet-conviction-voting`) @@ -161,19 +161,12 @@ All `Config` impls live in `runtime/src/configs/mod.rs` unless noted. - `ConfigDepositBase = 10 UNIT`, `FriendDepositFactor = 1 UNIT`, `MaxFriends = 9`, `RecoveryDeposit = 10 UNIT`. - **Calls:** `as_recovered`, `set_recovered`, `create_recovery`, `initiate_recovery`, `vouch_recovery`, `claim_recovery`, `close_recovery`, `remove_recovery`, `cancel_recovered`. -### Index 17 — `Assets` (`pallet-assets`) -- `AssetId = u32`, `CreateOrigin = AsEnsureOriginWithArg`, `ForceOrigin = EnsureRoot`, `AssetDeposit/AccountDeposit/Metadata = MILLI_UNIT`, `StringLimit = 50`, `CallbackHandle = AutoIncAssetId`, `Holder = AssetsHolder`, `RemoveItemsLimit = 1000`. -- **Calls:** full `pallet-assets` surface (`create`, `force_create`, `mint`, `burn`, `transfer`, `transfer_keep_alive`, `force_transfer`, `freeze`/`thaw`, `set_metadata`, `approve_transfer`, `transfer_approved`, etc.). - -### Index 18 — `AssetsHolder` (`pallet-assets-holder`) -- `RuntimeEvent`, `RuntimeHoldReason`. No standalone extrinsics; provides hold support to `Assets`. - ### Index 19 — `Multisig` (`pallet-multisig`, local) - `MaxSigners = 100`, `MaxTotalProposalsInStorage = 200`, `MaxCallSize = 10 KB`, `MultisigFee = 0.6 UNIT` (burned), `ProposalDeposit = 1 UNIT`, `ProposalFee = 1 UNIT`, `MaxExpiryDuration ≈ 2 weeks`, `MaxInnerCallWeight = (10^12, 2.5 MB)`, `HighSecurity = HighSecurityConfig`, `PalletId = "py/mltsg"`. - **Calls:** `create_multisig`(0), `propose`(1), `approve`(2), `cancel`(3), `remove_expired`(4), `claim_deposits`(5), `execute`(6). Exposes `derive_multisig_address`. ### Index 20 — `Wormhole` (`pallet-wormhole`, local) -- `Currency = Balances`, `Assets = Assets`, `VolumeFeeRateBps = 4` (0.04%; circuit ceil-rounds to ≥0.01 QUAN per exit), `VolumeFeesBurnRate = 50%`, `MintingAccount`, `WormholeAccountId = AccountId32`, `ZkTree = ZkTree`. No separate minimum exit amount. +- `Currency = Balances`, `AssetId = u32` (native leaves tagged as asset id 0 internally; non-native exits unsupported), `VolumeFeeRateBps = 4` (0.04%; circuit ceil-rounds to ≥0.01 QUAN per exit), `VolumeFeesBurnRate = 50%`, `MintingAccount`, `WormholeAccountId = AccountId32`, `ZkTree = ZkTree`. No separate minimum exit amount. No `pallet-assets` dependency. - **Calls:** `verify_private_batch`(2) — verifies a private-batch ZK proof and processes batched transfers; `verify_public_batch`(3) — verifies a public-batch proof with per-segment denial and aggregator fee rebate. - Implements `TransferProofRecorder` (`record_transfer`) consumed by mining-rewards, reversible-transfers, and the wormhole tx-extension. `on_initialize` emits genesis endowment proofs at block 1. Loads a static aggregated verifier (`get_aggregated_verifier`). @@ -219,7 +212,7 @@ Signed-extension pipeline applied to every extrinsic, in order: 8. `pallet_transaction_payment::ChargeTransactionPayment` 9. `frame_metadata_hash_extension::CheckMetadataHash` 10. `transaction_extensions::ReversibleTransactionExtension` — **custom**: blocks non-whitelisted calls from high-security accounts. -11. `transaction_extensions::WormholeProofRecorderExtension` — **custom**: in `post_dispatch`, scans emitted `Transfer`/`Transferred`/`Minted`/`Issued` events and records transfer proofs into the ZK tree (event-based, covers direct/batch/multisig/recovery/scheduled transfers). +11. `transaction_extensions::WormholeProofRecorderExtension` — **custom**: in `post_dispatch`, scans emitted native `Balances::Transfer` / `Balances::Minted` events and records transfer proofs into the ZK tree (event-based, covers direct/batch/multisig/recovery/scheduled native transfers). --- @@ -244,7 +237,6 @@ Signed-extension pipeline applied to every extrinsic, in order: - Dilithium well-known accounts: `crystal_alice`, `dilithium_bob`, `crystal_charlie` (public seeds `[0]` / `[1]` / `[2]`). Used by `dev` and **intentionally also by `heisenberg`** so integrators and CI can exercise governance, treasury, and transfer flows without distributing secrets. Those private keys are public by design; do **not** reuse this pattern on a mainnet or any value-bearing chain (Planck already uses distinct live treasury signers). - Treasury = 2-of-3 multisig of the three signers for `dev`/`heisenberg` (distinct nonce per preset); no genesis endowment (funded from mining-reward share only). - Tech-collective seeded via the chain-spec-only `tech_collective_seed_members` JSON field (`prepare_genesis_build_input` + `seed_tech_collective`). -- Reserves asset id 0 for the native-token-in-assets wormhole path. - Endows all genesis balances with wormhole transfer proofs (ZK-spendable). `dev` also endows `TEST_WORMHOLE_SECRET`'s address. --- From 5621a925c2339fe3e53490c06c1753b79ba678b7 Mon Sep 17 00:00:00 2001 From: illuzen Date: Mon, 3 Aug 2026 15:16:43 +0800 Subject: [PATCH 5/5] Bump spec_version to 138 for assets pallet removal Metadata-breaking runtime change; required for governance upgrade path. Co-authored-by: Cursor --- docs/RUNTIME_SURFACE.md | 2 +- runtime/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/RUNTIME_SURFACE.md b/docs/RUNTIME_SURFACE.md index 03ab6bb6..be843649 100644 --- a/docs/RUNTIME_SURFACE.md +++ b/docs/RUNTIME_SURFACE.md @@ -7,7 +7,7 @@ the runtime, their dispatchable calls, the runtime APIs, transaction extensions, genesis logic, and the workspace primitive crates pulled in. - **Crate:** `quantus-runtime` (`runtime/`), version `0.7.1-q-day-2` -- **Spec:** `spec_name = quantus-runtime`, `spec_version = 131`, `transaction_version = 2`, `authoring_version = 1` +- **Spec:** `spec_name = quantus-runtime`, `spec_version = 138`, `transaction_version = 3`, `authoring_version = 1` - **Build:** `no_std` WASM via `substrate-wasm-builder` (`runtime/build.rs`); native `std` build for the node/client - **Block time target:** 12s (`TARGET_BLOCK_TIME_MS = 12_000`) - **Consensus:** QPoW (quantum-resistant Proof of Work, Poseidon2-based) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index d36f8fc4..e5fb1c2a 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -73,7 +73,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 137, + spec_version: 138, impl_version: 1, apis: apis::RUNTIME_API_VERSIONS, transaction_version: 3,