diff --git a/.github/workflows/integration_test_manta.yml b/.github/workflows/integration_test_manta.yml index d03f069b8..1cb44f56d 100644 --- a/.github/workflows/integration_test_manta.yml +++ b/.github/workflows/integration_test_manta.yml @@ -91,7 +91,7 @@ jobs: if: contains(github.event.pull_request.labels.*.name, 'A-manta') needs: [build-node-current] runs-on: runtime-integration-test - timeout-minutes: 240 + timeout-minutes: 420 container: image: ubuntu:20.04 strategy: diff --git a/Cargo.lock b/Cargo.lock index f48c82e40..522a3bee3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1121,7 +1121,7 @@ dependencies = [ "manta-collator-selection", "manta-primitives", "nimbus-primitives", - "orml-traits", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", "orml-xtokens", "pallet-asset-manager", "pallet-assets", @@ -1129,7 +1129,7 @@ dependencies = [ "pallet-aura-style-filter", "pallet-author-inherent", "pallet-authorship", - "pallet-balances", + "pallet-balances 4.4.0", "pallet-collective", "pallet-conviction-voting", "pallet-democracy", @@ -1142,6 +1142,7 @@ dependencies = [ "pallet-membership", "pallet-multisig", "pallet-name-service", + "pallet-native-barrier", "pallet-parachain-staking", "pallet-preimage", "pallet-randomness", @@ -1204,7 +1205,8 @@ dependencies = [ "frame-support", "frame-system", "manta-primitives", - "pallet-balances", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", + "pallet-balances 4.4.0", "pallet-timestamp", "parity-scale-codec", "scale-info", @@ -4180,11 +4182,11 @@ dependencies = [ "manta-primitives", "manta-runtime", "nimbus-primitives", - "orml-traits", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", "orml-xtokens", "pallet-asset-manager", "pallet-assets", - "pallet-balances", + "pallet-balances 4.4.0", "pallet-collective", "pallet-democracy", "pallet-manta-pay", @@ -4489,7 +4491,7 @@ dependencies = [ "pallet-authorship", "pallet-babe", "pallet-bags-list", - "pallet-balances", + "pallet-balances 4.0.0-dev", "pallet-bounties", "pallet-child-bounties", "pallet-collective", @@ -5405,9 +5407,10 @@ dependencies = [ "log", "manta-primitives", "nimbus-primitives", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", "pallet-aura", "pallet-authorship", - "pallet-balances", + "pallet-balances 4.4.0", "pallet-session", "pallet-timestamp", "parity-scale-codec", @@ -5490,7 +5493,7 @@ dependencies = [ "frame-support", "frame-system", "log", - "orml-traits", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", "parity-scale-codec", "scale-info", "smallvec", @@ -5528,7 +5531,7 @@ dependencies = [ "manta-collator-selection", "manta-primitives", "nimbus-primitives", - "orml-traits", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", "orml-xtokens", "pallet-asset-manager", "pallet-assets", @@ -5536,7 +5539,7 @@ dependencies = [ "pallet-aura-style-filter", "pallet-author-inherent", "pallet-authorship", - "pallet-balances", + "pallet-balances 4.4.0", "pallet-collective", "pallet-democracy", "pallet-farming", @@ -5548,6 +5551,7 @@ dependencies = [ "pallet-membership", "pallet-multisig", "pallet-name-service", + "pallet-native-barrier", "pallet-parachain-staking", "pallet-preimage", "pallet-randomness", @@ -6387,12 +6391,30 @@ dependencies = [ [[package]] name = "orml-traits" version = "0.4.1-dev" -source = "git+https://github.com/manta-network/open-runtime-module-library.git?branch=polkadot-v0.9.37#ed87ba5bb12c756b3973b3f59a430bab22fc0cc9" +source = "git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37#00e9590afdefe603dc2ba2757316759622122096" dependencies = [ "frame-support", "impl-trait-for-tuples", "num-traits", - "orml-utilities", + "orml-utilities 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", + "parity-scale-codec", + "scale-info", + "serde", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", +] + +[[package]] +name = "orml-traits" +version = "0.4.1-dev" +source = "git+https://github.com/manta-network/open-runtime-module-library.git?branch=polkadot-v0.9.37#4ff694c4b1e3c4a187410dddf948d86624601a24" +dependencies = [ + "frame-support", + "impl-trait-for-tuples", + "num-traits", + "orml-utilities 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=polkadot-v0.9.37)", "parity-scale-codec", "scale-info", "serde", @@ -6405,7 +6427,21 @@ dependencies = [ [[package]] name = "orml-utilities" version = "0.4.1-dev" -source = "git+https://github.com/manta-network/open-runtime-module-library.git?branch=polkadot-v0.9.37#ed87ba5bb12c756b3973b3f59a430bab22fc0cc9" +source = "git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37#00e9590afdefe603dc2ba2757316759622122096" +dependencies = [ + "frame-support", + "parity-scale-codec", + "scale-info", + "serde", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "orml-utilities" +version = "0.4.1-dev" +source = "git+https://github.com/manta-network/open-runtime-module-library.git?branch=polkadot-v0.9.37#4ff694c4b1e3c4a187410dddf948d86624601a24" dependencies = [ "frame-support", "parity-scale-codec", @@ -6419,10 +6455,10 @@ dependencies = [ [[package]] name = "orml-xcm-support" version = "0.4.1-dev" -source = "git+https://github.com/manta-network/open-runtime-module-library.git?branch=polkadot-v0.9.37#ed87ba5bb12c756b3973b3f59a430bab22fc0cc9" +source = "git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37#00e9590afdefe603dc2ba2757316759622122096" dependencies = [ "frame-support", - "orml-traits", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", "parity-scale-codec", "sp-runtime", "sp-std", @@ -6433,12 +6469,12 @@ dependencies = [ [[package]] name = "orml-xtokens" version = "0.4.1-dev" -source = "git+https://github.com/manta-network/open-runtime-module-library.git?branch=polkadot-v0.9.37#ed87ba5bb12c756b3973b3f59a430bab22fc0cc9" +source = "git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37#00e9590afdefe603dc2ba2757316759622122096" dependencies = [ "cumulus-primitives-core", "frame-support", "frame-system", - "orml-traits", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", "orml-xcm-support", "pallet-xcm", "parity-scale-codec", @@ -6492,9 +6528,9 @@ dependencies = [ "frame-system", "log", "manta-primitives", - "orml-traits", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", "pallet-assets", - "pallet-balances", + "pallet-balances 4.4.0", "parity-scale-codec", "scale-info", "sp-arithmetic", @@ -6637,7 +6673,7 @@ dependencies = [ "frame-support", "frame-system", "log", - "pallet-balances", + "pallet-balances 4.0.0-dev", "parity-scale-codec", "scale-info", "sp-core", @@ -6662,6 +6698,25 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-balances" +version = "4.4.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", + "pallet-native-barrier", + "pallet-transaction-payment", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-beefy" version = "4.0.0-dev" @@ -6854,10 +6909,10 @@ dependencies = [ "hex-literal", "log", "manta-primitives", - "orml-traits", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", "pallet-asset-manager", "pallet-assets", - "pallet-balances", + "pallet-balances 4.4.0", "parity-scale-codec", "scale-info", "sp-arithmetic", @@ -7002,7 +7057,8 @@ dependencies = [ "log", "manta-collator-selection", "manta-primitives", - "pallet-balances", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", + "pallet-balances 4.4.0", "pallet-parachain-staking", "pallet-preimage", "pallet-randomness", @@ -7041,10 +7097,12 @@ dependencies = [ "manta-pay", "manta-primitives", "manta-util", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", "pallet-asset-manager", "pallet-assets", - "pallet-balances", + "pallet-balances 4.4.0", "pallet-manta-support", + "pallet-native-barrier", "pallet-tx-pause", "parity-scale-codec", "rand_chacha 0.3.1", @@ -7078,9 +7136,10 @@ dependencies = [ "manta-primitives", "manta-util", "num_cpus", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", "pallet-asset-manager", "pallet-assets", - "pallet-balances", + "pallet-balances 4.4.0", "pallet-manta-pay", "pallet-manta-support", "pallet-timestamp", @@ -7182,7 +7241,8 @@ dependencies = [ "frame-support", "frame-system", "manta-primitives", - "pallet-balances", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", + "pallet-balances 4.4.0", "pallet-manta-support", "parity-scale-codec", "safe-regex", @@ -7193,6 +7253,24 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-native-barrier" +version = "4.4.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "manta-primitives", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", + "pallet-balances 4.4.0", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-nis" version = "4.0.0-dev" @@ -7264,7 +7342,7 @@ dependencies = [ "frame-support", "frame-system", "log", - "pallet-balances", + "pallet-balances 4.0.0-dev", "parity-scale-codec", "scale-info", "serde", @@ -7283,7 +7361,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-babe", - "pallet-balances", + "pallet-balances 4.0.0-dev", "pallet-grandpa", "pallet-im-online", "pallet-offences", @@ -7306,7 +7384,8 @@ dependencies = [ "log", "manta-collator-selection", "manta-primitives", - "pallet-balances", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", + "pallet-balances 4.4.0", "pallet-session", "parity-scale-codec", "scale-info", @@ -7365,7 +7444,8 @@ dependencies = [ "log", "manta-primitives", "nimbus-primitives", - "pallet-balances", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", + "pallet-balances 4.4.0", "parity-scale-codec", "scale-info", "serde", @@ -7660,7 +7740,7 @@ dependencies = [ "frame-support", "frame-system", "impl-trait-for-tuples", - "pallet-balances", + "pallet-balances 4.0.0-dev", "parity-scale-codec", "scale-info", "serde", @@ -7676,7 +7756,8 @@ dependencies = [ "frame-support", "frame-system", "manta-primitives", - "pallet-balances", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", + "pallet-balances 4.4.0", "parity-scale-codec", "scale-info", "sp-core", @@ -8873,7 +8954,7 @@ dependencies = [ "pallet-authorship", "pallet-babe", "pallet-bags-list", - "pallet-balances", + "pallet-balances 4.0.0-dev", "pallet-bounties", "pallet-child-bounties", "pallet-collective", @@ -8958,7 +9039,7 @@ dependencies = [ "pallet-authorship", "pallet-babe", "pallet-bags-list", - "pallet-balances", + "pallet-balances 4.0.0-dev", "pallet-beefy-mmr", "pallet-election-provider-multi-phase", "pallet-session", @@ -9031,7 +9112,7 @@ dependencies = [ "pallet-authority-discovery", "pallet-authorship", "pallet-babe", - "pallet-balances", + "pallet-balances 4.0.0-dev", "pallet-session", "pallet-staking", "pallet-timestamp", @@ -9897,7 +9978,7 @@ dependencies = [ "pallet-authority-discovery", "pallet-authorship", "pallet-babe", - "pallet-balances", + "pallet-balances 4.0.0-dev", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", @@ -10054,11 +10135,11 @@ dependencies = [ "frame-system", "lazy_static", "manta-primitives", - "orml-traits", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=ghzlatarev/polkadot-v0.9.37)", "orml-xtokens", "pallet-asset-manager", "pallet-assets", - "pallet-balances", + "pallet-balances 4.4.0", "pallet-transaction-payment", "pallet-utility", "pallet-xcm", @@ -14424,7 +14505,7 @@ dependencies = [ "pallet-authorship", "pallet-babe", "pallet-bags-list", - "pallet-balances", + "pallet-balances 4.0.0-dev", "pallet-collective", "pallet-democracy", "pallet-election-provider-multi-phase", @@ -15016,8 +15097,8 @@ dependencies = [ "frame-support", "frame-system", "log", - "orml-traits", - "pallet-balances", + "orml-traits 0.4.1-dev (git+https://github.com/manta-network/open-runtime-module-library.git?branch=polkadot-v0.9.37)", + "pallet-balances 4.0.0-dev", "parity-scale-codec", "scale-info", "serde", diff --git a/pallets/asset-manager/Cargo.toml b/pallets/asset-manager/Cargo.toml index a9be6f607..d99fd432e 100644 --- a/pallets/asset-manager/Cargo.toml +++ b/pallets/asset-manager/Cargo.toml @@ -21,11 +21,11 @@ sp-std = { git = "https://github.com/paritytech/substrate.git", branch = "polkad xcm = { git = "https://github.com/paritytech/polkadot.git", default-features = false, branch = "release-v0.9.37" } # 3rd party dependencies -orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", default-features = false, branch = "polkadot-v0.9.37" } +orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", default-features = false, branch = "ghzlatarev/polkadot-v0.9.37" } [dev-dependencies] pallet-assets = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } -pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } +pallet-balances = { path = "../balances" } sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } diff --git a/pallets/asset-manager/src/mock.rs b/pallets/asset-manager/src/mock.rs index ed2b4ec94..48286e7b6 100644 --- a/pallets/asset-manager/src/mock.rs +++ b/pallets/asset-manager/src/mock.rs @@ -109,6 +109,23 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } +pub struct MockNativeBarrier; +impl orml_traits::native_barrier::NativeBarrier for MockNativeBarrier { + fn update_native_barrier(_account_id: &AccountId, _amount: Balance) {} + fn ensure_limit_not_exceeded( + _account_id: &AccountId, + _amount: Balance, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) + } +} + +impl orml_traits::native_barrier::NativeChecker for MockNativeBarrier { + fn is_native(_currency_id: &u64) -> bool { + true + } +} + parameter_types! { pub ExistentialDeposit: Balance = 1; pub const MaxLocks: u32 = 50; @@ -125,6 +142,7 @@ impl pallet_balances::Config for Runtime { type WeightInfo = (); type MaxReserves = MaxReserves; type ReserveIdentifier = [u8; 8]; + type NativeBarrierType = MockNativeBarrier; } pub struct MantaAssetRegistry; diff --git a/pallets/balances/Cargo.toml b/pallets/balances/Cargo.toml new file mode 100644 index 000000000..bb7d85592 --- /dev/null +++ b/pallets/balances/Cargo.toml @@ -0,0 +1,47 @@ +[package] +authors = ['Manta Network'] +edition = "2021" +homepage = 'https://manta.network' +license = 'GPL-3.0' +name = "pallet-balances" +readme = "README.md" +repository = 'https://github.com/Manta-Network/Manta/' +version = '4.4.0' + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +frame-benchmarking = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37", default-features = false, optional = true } +frame-support = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } +frame-system = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +sp-runtime = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } +sp-std = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } + +orml-traits = { git = 'https://github.com/manta-network/open-runtime-module-library.git', default-features = false, branch = "ghzlatarev/polkadot-v0.9.37" } +pallet-native-barrier = { path = '../native-barrier', default-features = false } + +[dev-dependencies] +pallet-transaction-payment = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } +sp-core = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } +sp-io = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } + +[features] +default = ["std"] +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-runtime/std", + "sp-std/std", + "pallet-native-barrier/std", + "orml-traits/std", +] +try-runtime = ["frame-support/try-runtime", "pallet-native-barrier/try-runtime"] diff --git a/pallets/balances/README.md b/pallets/balances/README.md new file mode 100644 index 000000000..93e424a89 --- /dev/null +++ b/pallets/balances/README.md @@ -0,0 +1,122 @@ +# Balances Module + +The Balances module provides functionality for handling accounts and balances. + +- [`Config`](https://docs.rs/pallet-balances/latest/pallet_balances/pallet/trait.Config.html) +- [`Call`](https://docs.rs/pallet-balances/latest/pallet_balances/pallet/enum.Call.html) +- [`Pallet`](https://docs.rs/pallet-balances/latest/pallet_balances/pallet/struct.Pallet.html) + +## Overview + +The Balances module provides functions for: + +- Getting and setting free balances. +- Retrieving total, reserved and unreserved balances. +- Repatriating a reserved balance to a beneficiary account that exists. +- Transferring a balance between accounts (when not reserved). +- Slashing an account balance. +- Account creation and removal. +- Managing total issuance. +- Setting and managing locks. + +### Terminology + +- **Existential Deposit:** The minimum balance required to create or keep an account open. This prevents +"dust accounts" from filling storage. When the free plus the reserved balance (i.e. the total balance) + fall below this, then the account is said to be dead; and it loses its functionality as well as any + prior history and all information on it is removed from the chain's state. + No account should ever have a total balance that is strictly between 0 and the existential + deposit (exclusive). If this ever happens, it indicates either a bug in this module or an + erroneous raw mutation of storage. + +- **Total Issuance:** The total number of units in existence in a system. + +- **Reaping an account:** The act of removing an account by resetting its nonce. Happens after its +total balance has become zero (or, strictly speaking, less than the Existential Deposit). + +- **Free Balance:** The portion of a balance that is not reserved. The free balance is the only + balance that matters for most operations. + +- **Reserved Balance:** Reserved balance still belongs to the account holder, but is suspended. + Reserved balance can still be slashed, but only after all the free balance has been slashed. + +- **Imbalance:** A condition when some funds were credited or debited without equal and opposite accounting +(i.e. a difference between total issuance and account balances). Functions that result in an imbalance will +return an object of the `Imbalance` trait that can be managed within your runtime logic. (If an imbalance is +simply dropped, it should automatically maintain any book-keeping such as total issuance.) + +- **Lock:** A freeze on a specified amount of an account's free balance until a specified block number. Multiple +locks always operate over the same funds, so they "overlay" rather than "stack". + +### Implementations + +The Balances module provides implementations for the following traits. If these traits provide the functionality +that you need, then you can avoid coupling with the Balances module. + +- [`Currency`](https://docs.rs/frame-support/latest/frame_support/traits/trait.Currency.html): Functions for dealing with a +fungible assets system. +- [`ReservableCurrency`](https://docs.rs/frame-support/latest/frame_support/traits/trait.ReservableCurrency.html): +Functions for dealing with assets that can be reserved from an account. +- [`LockableCurrency`](https://docs.rs/frame-support/latest/frame_support/traits/trait.LockableCurrency.html): Functions for +dealing with accounts that allow liquidity restrictions. +- [`Imbalance`](https://docs.rs/frame-support/latest/frame_support/traits/trait.Imbalance.html): Functions for handling +imbalances between total issuance in the system and account balances. Must be used when a function +creates new funds (e.g. a reward) or destroys some funds (e.g. a system fee). +- [`IsDeadAccount`](https://docs.rs/frame-support/latest/frame_support/traits/trait.IsDeadAccount.html): Determiner to say whether a +given account is unused. + +## Interface + +### Dispatchable Functions + +- `transfer` - Transfer some liquid free balance to another account. +- `set_balance` - Set the balances of a given account. The origin of this call must be root. + +## Usage + +The following examples show how to use the Balances module in your custom module. + +### Examples from the FRAME + +The Contract module uses the `Currency` trait to handle gas payment, and its types inherit from `Currency`: + +```rust +use frame_support::traits::Currency; + +pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +pub type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; + +``` + +The Staking module uses the `LockableCurrency` trait to lock a stash account's funds: + +```rust +use frame_support::traits::{WithdrawReasons, LockableCurrency}; +use sp_runtime::traits::Bounded; +pub trait Config: frame_system::Config { + type Currency: LockableCurrency; +} + +fn update_ledger( + controller: &T::AccountId, + ledger: &StakingLedger +) { + T::Currency::set_lock( + STAKING_ID, + &ledger.stash, + ledger.total, + WithdrawReasons::all() + ); + // >::insert(controller, ledger); // Commented out as we don't have access to Staking's storage here. +} +``` + +## Genesis config + +The Balances module depends on the [`GenesisConfig`](https://docs.rs/pallet-balances/latest/pallet_balances/pallet/struct.GenesisConfig.html). + +## Assumptions + +* Total issued balanced of all accounts should be less than `Config::Balance::max_value()`. + +License: Apache-2.0 diff --git a/pallets/balances/src/benchmarking.rs b/pallets/balances/src/benchmarking.rs new file mode 100644 index 000000000..c2d538cb1 --- /dev/null +++ b/pallets/balances/src/benchmarking.rs @@ -0,0 +1,222 @@ +// Copyright 2020-2023 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . +// +//! # Native Barrier Pallet benchmarking. + +#![cfg(feature = "runtime-benchmarks")] +#![allow(clippy::all)] + +use super::*; + +use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller}; +use frame_system::RawOrigin; +use sp_runtime::traits::Bounded; + +use crate::Pallet as Balances; + +const SEED: u32 = 0; +// existential deposit multiplier +const ED_MULTIPLIER: u32 = 10; + +benchmarks_instance_pallet! { + // Benchmark `transfer` extrinsic with the worst possible conditions: + // * Transfer will kill the sender account. + // * Transfer will create the recipient account. + transfer { + let existential_deposit = T::ExistentialDeposit::get(); + let caller = whitelisted_caller(); + + // Give some multiple of the existential deposit + let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&caller, balance); + + // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, + // and reap this user. + let recipient: T::AccountId = account("recipient", 0, SEED); + let recipient_lookup = T::Lookup::unlookup(recipient.clone()); + let transfer_amount = existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); + }: transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount) + verify { + assert_eq!(Balances::::free_balance(&caller), Zero::zero()); + assert_eq!(Balances::::free_balance(&recipient), transfer_amount); + } + + // Benchmark `transfer` with the best possible condition: + // * Both accounts exist and will continue to exist. + #[extra] + transfer_best_case { + let caller = whitelisted_caller(); + let recipient: T::AccountId = account("recipient", 0, SEED); + let recipient_lookup = T::Lookup::unlookup(recipient.clone()); + + // Give the sender account max funds for transfer (their account will never reasonably be killed). + let _ = as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); + + // Give the recipient account existential deposit (thus their account already exists). + let existential_deposit = T::ExistentialDeposit::get(); + let _ = as Currency<_>>::make_free_balance_be(&recipient, existential_deposit); + let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + }: transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount) + verify { + assert!(!Balances::::free_balance(&caller).is_zero()); + assert!(!Balances::::free_balance(&recipient).is_zero()); + } + + // Benchmark `transfer_keep_alive` with the worst possible condition: + // * The recipient account is created. + transfer_keep_alive { + let caller = whitelisted_caller(); + let recipient: T::AccountId = account("recipient", 0, SEED); + let recipient_lookup = T::Lookup::unlookup(recipient.clone()); + + // Give the sender account max funds, thus a transfer will not kill account. + let _ = as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); + let existential_deposit = T::ExistentialDeposit::get(); + let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + }: _(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount) + verify { + assert!(!Balances::::free_balance(&caller).is_zero()); + assert_eq!(Balances::::free_balance(&recipient), transfer_amount); + } + + // Benchmark `set_balance` coming from ROOT account. This always creates an account. + set_balance_creating { + let user: T::AccountId = account("user", 0, SEED); + let user_lookup = T::Lookup::unlookup(user.clone()); + + // Give the user some initial balance. + let existential_deposit = T::ExistentialDeposit::get(); + let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); + }: set_balance(RawOrigin::Root, user_lookup, balance_amount, balance_amount) + verify { + assert_eq!(Balances::::free_balance(&user), balance_amount); + assert_eq!(Balances::::reserved_balance(&user), balance_amount); + } + + // Benchmark `set_balance` coming from ROOT account. This always kills an account. + set_balance_killing { + let user: T::AccountId = account("user", 0, SEED); + let user_lookup = T::Lookup::unlookup(user.clone()); + + // Give the user some initial balance. + let existential_deposit = T::ExistentialDeposit::get(); + let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); + }: set_balance(RawOrigin::Root, user_lookup, Zero::zero(), Zero::zero()) + verify { + assert!(Balances::::free_balance(&user).is_zero()); + } + + // Benchmark `force_transfer` extrinsic with the worst possible conditions: + // * Transfer will kill the sender account. + // * Transfer will create the recipient account. + force_transfer { + let existential_deposit = T::ExistentialDeposit::get(); + let source: T::AccountId = account("source", 0, SEED); + let source_lookup = T::Lookup::unlookup(source.clone()); + + // Give some multiple of the existential deposit + let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&source, balance); + + // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, and reap this user. + let recipient: T::AccountId = account("recipient", 0, SEED); + let recipient_lookup = T::Lookup::unlookup(recipient.clone()); + let transfer_amount = existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); + }: force_transfer(RawOrigin::Root, source_lookup, recipient_lookup, transfer_amount) + verify { + assert_eq!(Balances::::free_balance(&source), Zero::zero()); + assert_eq!(Balances::::free_balance(&recipient), transfer_amount); + } + + // This benchmark performs the same operation as `transfer` in the worst case scenario, + // but additionally introduces many new users into the storage, increasing the the merkle + // trie and PoV size. + #[extra] + transfer_increasing_users { + // 1_000 is not very much, but this upper bound can be controlled by the CLI. + let u in 0 .. 1_000; + let existential_deposit = T::ExistentialDeposit::get(); + let caller = whitelisted_caller(); + + // Give some multiple of the existential deposit + let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&caller, balance); + + // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, + // and reap this user. + let recipient: T::AccountId = account("recipient", 0, SEED); + let recipient_lookup = T::Lookup::unlookup(recipient.clone()); + let transfer_amount = existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); + + // Create a bunch of users in storage. + for i in 0 .. u { + // The `account` function uses `blake2_256` to generate unique accounts, so these + // should be quite random and evenly distributed in the trie. + let new_user: T::AccountId = account("new_user", i, SEED); + let _ = as Currency<_>>::make_free_balance_be(&new_user, balance); + } + }: transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount) + verify { + assert_eq!(Balances::::free_balance(&caller), Zero::zero()); + assert_eq!(Balances::::free_balance(&recipient), transfer_amount); + } + + // Benchmark `transfer_all` with the worst possible condition: + // * The recipient account is created + // * The sender is killed + transfer_all { + let caller = whitelisted_caller(); + let recipient: T::AccountId = account("recipient", 0, SEED); + let recipient_lookup = T::Lookup::unlookup(recipient.clone()); + + // Give some multiple of the existential deposit + let existential_deposit = T::ExistentialDeposit::get(); + let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&caller, balance); + }: _(RawOrigin::Signed(caller.clone()), recipient_lookup, false) + verify { + assert!(Balances::::free_balance(&caller).is_zero()); + assert_eq!(Balances::::free_balance(&recipient), balance); + } + + force_unreserve { + let user: T::AccountId = account("user", 0, SEED); + let user_lookup = T::Lookup::unlookup(user.clone()); + + // Give some multiple of the existential deposit + let existential_deposit = T::ExistentialDeposit::get(); + let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&user, balance); + + // Reserve the balance + as ReservableCurrency<_>>::reserve(&user, balance)?; + assert_eq!(Balances::::reserved_balance(&user), balance); + assert!(Balances::::free_balance(&user).is_zero()); + + }: _(RawOrigin::Root, user_lookup, balance) + verify { + assert!(Balances::::reserved_balance(&user).is_zero()); + assert_eq!(Balances::::free_balance(&user), balance); + } + + impl_benchmark_test_suite!( + Balances, + crate::tests_composite::ExtBuilder::default().build(), + crate::tests_composite::Test, + ) +} diff --git a/pallets/balances/src/lib.rs b/pallets/balances/src/lib.rs new file mode 100644 index 000000000..720389e1e --- /dev/null +++ b/pallets/balances/src/lib.rs @@ -0,0 +1,2358 @@ +// Copyright 2020-2023 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . + +//! # Balances Pallet +//! +//! The Balances pallet provides functionality for handling accounts and balances. +//! +//! - [`Config`] +//! - [`Call`] +//! - [`Pallet`] +//! +//! ## Overview +//! +//! The Balances pallet provides functions for: +//! +//! - Getting and setting free balances. +//! - Retrieving total, reserved and unreserved balances. +//! - Repatriating a reserved balance to a beneficiary account that exists. +//! - Transferring a balance between accounts (when not reserved). +//! - Slashing an account balance. +//! - Account creation and removal. +//! - Managing total issuance. +//! - Setting and managing locks. +//! +//! ### Terminology +//! +//! - **Existential Deposit:** The minimum balance required to create or keep an account open. This +//! prevents "dust accounts" from filling storage. When the free plus the reserved balance (i.e. +//! the total balance) fall below this, then the account is said to be dead; and it loses its +//! functionality as well as any prior history and all information on it is removed from the +//! chain's state. No account should ever have a total balance that is strictly between 0 and the +//! existential deposit (exclusive). If this ever happens, it indicates either a bug in this +//! pallet or an erroneous raw mutation of storage. +//! +//! - **Total Issuance:** The total number of units in existence in a system. +//! +//! - **Reaping an account:** The act of removing an account by resetting its nonce. Happens after +//! its +//! total balance has become zero (or, strictly speaking, less than the Existential Deposit). +//! +//! - **Free Balance:** The portion of a balance that is not reserved. The free balance is the only +//! balance that matters for most operations. +//! +//! - **Reserved Balance:** Reserved balance still belongs to the account holder, but is suspended. +//! Reserved balance can still be slashed, but only after all the free balance has been slashed. +//! +//! - **Imbalance:** A condition when some funds were credited or debited without equal and opposite +//! accounting +//! (i.e. a difference between total issuance and account balances). Functions that result in an +//! imbalance will return an object of the `Imbalance` trait that can be managed within your runtime +//! logic. (If an imbalance is simply dropped, it should automatically maintain any book-keeping +//! such as total issuance.) +//! +//! - **Lock:** A freeze on a specified amount of an account's free balance until a specified block +//! number. Multiple +//! locks always operate over the same funds, so they "overlay" rather than "stack". +//! +//! ### Implementations +//! +//! The Balances pallet provides implementations for the following traits. If these traits provide +//! the functionality that you need, then you can avoid coupling with the Balances pallet. +//! +//! - [`Currency`](frame_support::traits::Currency): Functions for dealing with a +//! fungible assets system. +//! - [`ReservableCurrency`](frame_support::traits::ReservableCurrency): +//! - [`NamedReservableCurrency`](frame_support::traits::NamedReservableCurrency): +//! Functions for dealing with assets that can be reserved from an account. +//! - [`LockableCurrency`](frame_support::traits::LockableCurrency): Functions for +//! dealing with accounts that allow liquidity restrictions. +//! - [`Imbalance`](frame_support::traits::Imbalance): Functions for handling +//! imbalances between total issuance in the system and account balances. Must be used when a +//! function creates new funds (e.g. a reward) or destroys some funds (e.g. a system fee). +//! +//! ## Interface +//! +//! ### Dispatchable Functions +//! +//! - `transfer` - Transfer some liquid free balance to another account. +//! - `set_balance` - Set the balances of a given account. The origin of this call must be root. +//! +//! ## Usage +//! +//! The following examples show how to use the Balances pallet in your custom pallet. +//! +//! ### Examples from the FRAME +//! +//! The Contract pallet uses the `Currency` trait to handle gas payment, and its types inherit from +//! `Currency`: +//! +//! ``` +//! use frame_support::traits::Currency; +//! # pub trait Config: frame_system::Config { +//! # type Currency: Currency; +//! # } +//! +//! pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +//! pub type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; +//! +//! # fn main() {} +//! ``` +//! +//! The Staking pallet uses the `LockableCurrency` trait to lock a stash account's funds: +//! +//! ``` +//! use frame_support::traits::{WithdrawReasons, LockableCurrency}; +//! use sp_runtime::traits::Bounded; +//! pub trait Config: frame_system::Config { +//! type Currency: LockableCurrency; +//! } +//! # struct StakingLedger { +//! # stash: ::AccountId, +//! # total: <::Currency as frame_support::traits::Currency<::AccountId>>::Balance, +//! # phantom: std::marker::PhantomData, +//! # } +//! # const STAKING_ID: [u8; 8] = *b"staking "; +//! +//! fn update_ledger( +//! controller: &T::AccountId, +//! ledger: &StakingLedger +//! ) { +//! T::Currency::set_lock( +//! STAKING_ID, +//! &ledger.stash, +//! ledger.total, +//! WithdrawReasons::all() +//! ); +//! // >::insert(controller, ledger); // Commented out as we don't have access to Staking's storage here. +//! } +//! # fn main() {} +//! ``` +//! +//! ## Genesis config +//! +//! The Balances pallet depends on the [`GenesisConfig`]. +//! +//! ## Assumptions +//! +//! * Total issued balanced of all accounts should be less than `Config::Balance::max_value()`. + +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::all)] + +#[macro_use] +mod tests; +mod benchmarking; +pub mod migration; +mod tests_composite; +mod tests_local; +#[cfg(test)] +mod tests_reentrancy; +pub mod weights; + +pub use self::imbalances::{NegativeImbalance, PositiveImbalance}; +use codec::{Codec, Decode, Encode, MaxEncodedLen}; +#[cfg(feature = "std")] +use frame_support::traits::GenesisBuild; +use frame_support::{ + ensure, + pallet_prelude::DispatchResult, + traits::{ + tokens::{fungible, BalanceStatus as Status, DepositConsequence, WithdrawConsequence}, + Currency, DefensiveSaturating, ExistenceRequirement, + ExistenceRequirement::{AllowDeath, KeepAlive}, + Get, Imbalance, LockIdentifier, LockableCurrency, NamedReservableCurrency, OnUnbalanced, + ReservableCurrency, SignedImbalance, StoredMap, TryDrop, WithdrawReasons, + }, + WeakBoundedVec, +}; +use frame_system as system; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{ + AtLeast32BitUnsigned, Bounded, CheckedAdd, CheckedSub, MaybeSerializeDeserialize, + Saturating, StaticLookup, Zero, + }, + ArithmeticError, DispatchError, FixedPointOperand, RuntimeDebug, +}; +use sp_std::{cmp, fmt::Debug, mem, ops::BitOr, prelude::*, result}; +pub use weights::WeightInfo; + +pub use pallet::*; + +const LOG_TARGET: &str = "runtime::balances"; + +type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use orml_traits::native_barrier::NativeBarrier; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The balance of an account. + type Balance: Parameter + + Member + + AtLeast32BitUnsigned + + Codec + + Default + + Copy + + MaybeSerializeDeserialize + + Debug + + MaxEncodedLen + + TypeInfo + + FixedPointOperand; + + /// Handler for the unbalanced reduction when removing a dust account. + type DustRemoval: OnUnbalanced>; + + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + + /// The minimum amount required to keep an account open. + #[pallet::constant] + type ExistentialDeposit: Get; + + /// The means of storing the balances of an account. + type AccountStore: StoredMap>; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + + /// The maximum number of locks that should exist on an account. + /// Not strictly enforced, but used for weight estimation. + #[pallet::constant] + type MaxLocks: Get; + + /// The maximum number of named reserves that can exist on an account. + #[pallet::constant] + type MaxReserves: Get; + + /// The id type for named reserves. + type ReserveIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy; + + /// Barrier for native transfers of select accounts + type NativeBarrierType: NativeBarrier; + } + + /// The current storage version. + const STORAGE_VERSION: frame_support::traits::StorageVersion = + frame_support::traits::StorageVersion::new(1); + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(PhantomData<(T, I)>); + + #[pallet::call] + impl, I: 'static> Pallet { + /// Transfer some liquid free balance to another account. + /// + /// `transfer` will set the `FreeBalance` of the sender and receiver. + /// If the sender's account is below the existential deposit as a result + /// of the transfer, the account will be reaped. + /// + /// The dispatch origin for this call must be `Signed` by the transactor. + /// + /// # + /// - Dependent on arguments but not critical, given proper implementations for input config + /// types. See related functions below. + /// - It contains a limited number of reads and writes internally and no complex + /// computation. + /// + /// Related functions: + /// + /// - `ensure_can_withdraw` is always called internally but has a bounded complexity. + /// - Transferring balances to accounts that did not exist before will cause + /// `T::OnNewAccount::on_new_account` to be called. + /// - Removing enough funds from an account will trigger `T::DustRemoval::on_unbalanced`. + /// - `transfer_keep_alive` works the same way as `transfer`, but has an additional check + /// that the transfer will not kill the origin account. + /// --------------------------------- + /// - Origin account is already in memory, so no DB operations for them. + /// # + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::transfer())] + pub fn transfer( + origin: OriginFor, + dest: AccountIdLookupOf, + #[pallet::compact] value: T::Balance, + ) -> DispatchResultWithPostInfo { + let transactor = ensure_signed(origin)?; + let dest = T::Lookup::lookup(dest)?; + ::ensure_limit_not_exceeded(&transactor, value)?; + >::transfer( + &transactor, + &dest, + value, + ExistenceRequirement::AllowDeath, + )?; + Ok(().into()) + } + + /// Set the balances of a given account. + /// + /// This will alter `FreeBalance` and `ReservedBalance` in storage. it will + /// also alter the total issuance of the system (`TotalIssuance`) appropriately. + /// If the new free or reserved balance is below the existential deposit, + /// it will reset the account nonce (`frame_system::AccountNonce`). + /// + /// The dispatch origin for this call is `root`. + #[pallet::call_index(1)] + #[pallet::weight( + T::WeightInfo::set_balance_creating() // Creates a new account. + .max(T::WeightInfo::set_balance_killing()) // Kills an existing account. + )] + pub fn set_balance( + origin: OriginFor, + who: AccountIdLookupOf, + #[pallet::compact] new_free: T::Balance, + #[pallet::compact] new_reserved: T::Balance, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + let who = T::Lookup::lookup(who)?; + let existential_deposit = T::ExistentialDeposit::get(); + + let wipeout = new_free + new_reserved < existential_deposit; + let new_free = if wipeout { Zero::zero() } else { new_free }; + let new_reserved = if wipeout { Zero::zero() } else { new_reserved }; + + // First we try to modify the account's balance to the forced balance. + let (old_free, old_reserved) = Self::mutate_account(&who, |account| { + let old_free = account.free; + let old_reserved = account.reserved; + + account.free = new_free; + account.reserved = new_reserved; + + (old_free, old_reserved) + })?; + + // This will adjust the total issuance, which was not done by the `mutate_account` + // above. + if new_free > old_free { + mem::drop(PositiveImbalance::::new(new_free - old_free)); + } else if new_free < old_free { + mem::drop(NegativeImbalance::::new(old_free - new_free)); + } + + if new_reserved > old_reserved { + mem::drop(PositiveImbalance::::new(new_reserved - old_reserved)); + } else if new_reserved < old_reserved { + mem::drop(NegativeImbalance::::new(old_reserved - new_reserved)); + } + + Self::deposit_event(Event::BalanceSet { + who, + free: new_free, + reserved: new_reserved, + }); + Ok(().into()) + } + + /// Exactly as `transfer`, except the origin must be root and the source account may be + /// specified. + /// # + /// - Same as transfer, but additional read and write because the source account is not + /// assumed to be in the overlay. + /// # + #[pallet::call_index(2)] + #[pallet::weight(T::WeightInfo::force_transfer())] + pub fn force_transfer( + origin: OriginFor, + source: AccountIdLookupOf, + dest: AccountIdLookupOf, + #[pallet::compact] value: T::Balance, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + let source = T::Lookup::lookup(source)?; + let dest = T::Lookup::lookup(dest)?; + >::transfer( + &source, + &dest, + value, + ExistenceRequirement::AllowDeath, + )?; + Ok(().into()) + } + + /// Same as the [`transfer`] call, but with a check that the transfer will not kill the + /// origin account. + /// + /// 99% of the time you want [`transfer`] instead. + /// + /// [`transfer`]: struct.Pallet.html#method.transfer + #[pallet::call_index(3)] + #[pallet::weight(T::WeightInfo::transfer_keep_alive())] + pub fn transfer_keep_alive( + origin: OriginFor, + dest: AccountIdLookupOf, + #[pallet::compact] value: T::Balance, + ) -> DispatchResultWithPostInfo { + let transactor = ensure_signed(origin)?; + let dest = T::Lookup::lookup(dest)?; + ::ensure_limit_not_exceeded(&transactor, value)?; + >::transfer(&transactor, &dest, value, KeepAlive)?; + Ok(().into()) + } + + /// Transfer the entire transferable balance from the caller account. + /// + /// NOTE: This function only attempts to transfer _transferable_ balances. This means that + /// any locked, reserved, or existential deposits (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. + /// + /// - `dest`: The recipient of the transfer. + /// - `keep_alive`: A boolean to determine if the `transfer_all` operation should send all + /// of the funds the account has, causing the sender account to be killed (false), or + /// transfer everything except at least the existential deposit, which will guarantee to + /// keep the sender account alive (true). # + /// - O(1). Just like transfer, but reading the user's transferable balance first. + /// # + #[pallet::call_index(4)] + #[pallet::weight(T::WeightInfo::transfer_all())] + pub fn transfer_all( + origin: OriginFor, + dest: AccountIdLookupOf, + keep_alive: bool, + ) -> DispatchResult { + use fungible::Inspect; + let transactor = ensure_signed(origin)?; + let reducible_balance = Self::reducible_balance(&transactor, keep_alive); + let dest = T::Lookup::lookup(dest)?; + let keep_alive = if keep_alive { KeepAlive } else { AllowDeath }; + ::ensure_limit_not_exceeded(&transactor, reducible_balance)?; + >::transfer(&transactor, &dest, reducible_balance, keep_alive)?; + Ok(()) + } + + /// Unreserve some balance from a user by force. + /// + /// Can only be called by ROOT. + #[pallet::call_index(5)] + #[pallet::weight(T::WeightInfo::force_unreserve())] + pub fn force_unreserve( + origin: OriginFor, + who: AccountIdLookupOf, + amount: T::Balance, + ) -> DispatchResult { + ensure_root(origin)?; + let who = T::Lookup::lookup(who)?; + let _leftover = >::unreserve(&who, amount); + Ok(()) + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// An account was created with some free balance. + Endowed { + account: T::AccountId, + free_balance: T::Balance, + }, + /// An account was removed whose balance was non-zero but below ExistentialDeposit, + /// resulting in an outright loss. + DustLost { + account: T::AccountId, + amount: T::Balance, + }, + /// Transfer succeeded. + Transfer { + from: T::AccountId, + to: T::AccountId, + amount: T::Balance, + }, + /// A balance was set by root. + BalanceSet { + who: T::AccountId, + free: T::Balance, + reserved: T::Balance, + }, + /// Some balance was reserved (moved from free to reserved). + Reserved { + who: T::AccountId, + amount: T::Balance, + }, + /// Some balance was unreserved (moved from reserved to free). + Unreserved { + who: T::AccountId, + amount: T::Balance, + }, + /// Some balance was moved from the reserve of the first account to the second account. + /// Final argument indicates the destination balance type. + ReserveRepatriated { + from: T::AccountId, + to: T::AccountId, + amount: T::Balance, + destination_status: Status, + }, + /// Some amount was deposited (e.g. for transaction fees). + Deposit { + who: T::AccountId, + amount: T::Balance, + }, + /// Some amount was withdrawn from the account (e.g. for transaction fees). + Withdraw { + who: T::AccountId, + amount: T::Balance, + }, + /// Some amount was removed from the account (e.g. for misbehavior). + Slashed { + who: T::AccountId, + amount: T::Balance, + }, + } + + #[pallet::error] + pub enum Error { + /// Vesting balance too high to send value + VestingBalance, + /// Account liquidity restrictions prevent withdrawal + LiquidityRestrictions, + /// Balance too low to send value. + InsufficientBalance, + /// Value too low to create account due to existential deposit + ExistentialDeposit, + /// Transfer/payment would kill account + KeepAlive, + /// A vesting schedule already exists for this account + ExistingVestingSchedule, + /// Beneficiary account must pre-exist + DeadAccount, + /// Number of named reserves exceed MaxReserves + TooManyReserves, + /// + XcmTransfersLimitExceeded, + /// + XcmTransfersNotAllowedForAccount, + } + + /// The total units issued in the system. + #[pallet::storage] + #[pallet::getter(fn total_issuance)] + #[pallet::whitelist_storage] + pub type TotalIssuance, I: 'static = ()> = StorageValue<_, T::Balance, ValueQuery>; + + /// The total units of outstanding deactivated balance in the system. + #[pallet::storage] + #[pallet::getter(fn inactive_issuance)] + #[pallet::whitelist_storage] + pub type InactiveIssuance, I: 'static = ()> = + StorageValue<_, T::Balance, ValueQuery>; + + /// The Balances pallet example of storing the balance of an account. + /// + /// # Example + /// + /// ```nocompile + /// impl pallet_balances::Config for Runtime { + /// type AccountStore = StorageMapShim, frame_system::Provider, AccountId, Self::AccountData> + /// } + /// ``` + /// + /// You can also store the balance of an account in the `System` pallet. + /// + /// # Example + /// + /// ```nocompile + /// impl pallet_balances::Config for Runtime { + /// type AccountStore = System + /// } + /// ``` + /// + /// But this comes with tradeoffs, storing account balances in the system pallet stores + /// `frame_system` data alongside the account data contrary to storing account balances in the + /// `Balances` pallet, which uses a `StorageMap` to store balances data only. + /// NOTE: This is only used in the case that this pallet is used to store balances. + #[pallet::storage] + pub type Account, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, T::AccountId, AccountData, ValueQuery>; + + /// Any liquidity locks on some account balances. + /// NOTE: Should only be accessed when setting, changing and freeing a lock. + #[pallet::storage] + #[pallet::getter(fn locks)] + pub type Locks, I: 'static = ()> = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + WeakBoundedVec, T::MaxLocks>, + ValueQuery, + >; + + /// Named reserves on some account balances. + #[pallet::storage] + #[pallet::getter(fn reserves)] + pub type Reserves, I: 'static = ()> = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + BoundedVec, T::MaxReserves>, + ValueQuery, + >; + + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + pub balances: Vec<(T::AccountId, T::Balance)>, + } + + #[cfg(feature = "std")] + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { + balances: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig { + fn build(&self) { + let total = self + .balances + .iter() + .fold(Zero::zero(), |acc: T::Balance, &(_, n)| acc + n); + >::put(total); + + for (_, balance) in &self.balances { + assert!( + *balance >= >::ExistentialDeposit::get(), + "the balance of any account should always be at least the existential deposit.", + ) + } + + // ensure no duplicates exist. + let endowed_accounts = self + .balances + .iter() + .map(|(x, _)| x) + .cloned() + .collect::>(); + + assert!( + endowed_accounts.len() == self.balances.len(), + "duplicate balances in genesis." + ); + + for &(ref who, free) in self.balances.iter() { + assert!(T::AccountStore::insert( + who, + AccountData { + free, + ..Default::default() + } + ) + .is_ok()); + } + } + } +} + +#[cfg(feature = "std")] +impl, I: 'static> GenesisConfig { + /// Direct implementation of `GenesisBuild::build_storage`. + /// + /// Kept in order not to break dependency. + pub fn build_storage(&self) -> Result { + >::build_storage(self) + } + + /// Direct implementation of `GenesisBuild::assimilate_storage`. + /// + /// Kept in order not to break dependency. + pub fn assimilate_storage(&self, storage: &mut sp_runtime::Storage) -> Result<(), String> { + >::assimilate_storage(self, storage) + } +} + +/// Simplified reasons for withdrawing balance. +#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub enum Reasons { + /// Paying system transaction fees. + Fee = 0, + /// Any reason other than paying system transaction fees. + Misc = 1, + /// Any reason at all. + All = 2, +} + +impl From for Reasons { + fn from(r: WithdrawReasons) -> Reasons { + if r == WithdrawReasons::TRANSACTION_PAYMENT { + Reasons::Fee + } else if r.contains(WithdrawReasons::TRANSACTION_PAYMENT) { + Reasons::All + } else { + Reasons::Misc + } + } +} + +impl BitOr for Reasons { + type Output = Reasons; + fn bitor(self, other: Reasons) -> Reasons { + if self == other { + return self; + } + Reasons::All + } +} + +/// A single lock on a balance. There can be many of these on an account and they "overlap", so the +/// same balance is frozen by multiple locks. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub struct BalanceLock { + /// An identifier for this lock. Only one lock may be in existence for each identifier. + pub id: LockIdentifier, + /// The amount which the free balance may not drop below when this lock is in effect. + pub amount: Balance, + /// If true, then the lock remains in effect even for payment of transaction fees. + pub reasons: Reasons, +} + +/// Store named reserved balance. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub struct ReserveData { + /// The identifier for the named reserve. + pub id: ReserveIdentifier, + /// The amount of the named reserve. + pub amount: Balance, +} + +/// All balance information for an account. +#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub struct AccountData { + /// Non-reserved part of the balance. There may still be restrictions on this, but it is the + /// total pool what may in principle be transferred, reserved and used for tipping. + /// + /// This is the only balance that matters in terms of most operations on tokens. It + /// alone is used to determine the balance when in the contract execution environment. + pub free: Balance, + /// Balance which is reserved and may not be used at all. + /// + /// This can still get slashed, but gets slashed last of all. + /// + /// This balance is a 'reserve' balance that other subsystems use in order to set aside tokens + /// that are still 'owned' by the account holder, but which are suspendable. + /// This includes named reserve and unnamed reserve. + pub reserved: Balance, + /// The amount that `free` may not drop below when withdrawing for *anything except transaction + /// fee payment*. + pub misc_frozen: Balance, + /// The amount that `free` may not drop below when withdrawing specifically for transaction + /// fee payment. + pub fee_frozen: Balance, +} + +impl AccountData { + /// How much this account's balance can be reduced for the given `reasons`. + fn usable(&self, reasons: Reasons) -> Balance { + self.free.saturating_sub(self.frozen(reasons)) + } + /// The amount that this account's free balance may not be reduced beyond for the given + /// `reasons`. + fn frozen(&self, reasons: Reasons) -> Balance { + match reasons { + Reasons::All => self.misc_frozen.max(self.fee_frozen), + Reasons::Misc => self.misc_frozen, + Reasons::Fee => self.fee_frozen, + } + } + /// The total balance in this account including any that is reserved and ignoring any frozen. + fn total(&self) -> Balance { + self.free.saturating_add(self.reserved) + } +} + +pub struct DustCleaner, I: 'static = ()>( + Option<(T::AccountId, NegativeImbalance)>, +); + +impl, I: 'static> Drop for DustCleaner { + fn drop(&mut self) { + if let Some((who, dust)) = self.0.take() { + Pallet::::deposit_event(Event::DustLost { + account: who, + amount: dust.peek(), + }); + T::DustRemoval::on_unbalanced(dust); + } + } +} + +impl, I: 'static> Pallet { + /// Get the free balance of an account. + pub fn free_balance(who: impl sp_std::borrow::Borrow) -> T::Balance { + Self::account(who.borrow()).free + } + + /// Get the balance of an account that can be used for transfers, reservations, or any other + /// non-locking, non-transaction-fee activity. Will be at most `free_balance`. + pub fn usable_balance(who: impl sp_std::borrow::Borrow) -> T::Balance { + Self::account(who.borrow()).usable(Reasons::Misc) + } + + /// Get the balance of an account that can be used for paying transaction fees (not tipping, + /// or any other kind of fees, though). Will be at most `free_balance`. + pub fn usable_balance_for_fees(who: impl sp_std::borrow::Borrow) -> T::Balance { + Self::account(who.borrow()).usable(Reasons::Fee) + } + + /// Get the reserved balance of an account. + pub fn reserved_balance(who: impl sp_std::borrow::Borrow) -> T::Balance { + Self::account(who.borrow()).reserved + } + + /// Get both the free and reserved balances of an account. + fn account(who: &T::AccountId) -> AccountData { + T::AccountStore::get(who) + } + + /// Handles any steps needed after mutating an account. + /// + /// This includes DustRemoval unbalancing, in the case than the `new` account's total balance + /// is non-zero but below ED. + /// + /// Returns two values: + /// - `Some` containing the the `new` account, iff the account has sufficient balance. + /// - `Some` containing the dust to be dropped, iff some dust should be dropped. + fn post_mutation( + _who: &T::AccountId, + new: AccountData, + ) -> ( + Option>, + Option>, + ) { + let total = new.total(); + if total < T::ExistentialDeposit::get() { + if total.is_zero() { + (None, None) + } else { + (None, Some(NegativeImbalance::new(total))) + } + } else { + (Some(new), None) + } + } + + fn deposit_consequence( + _who: &T::AccountId, + amount: T::Balance, + account: &AccountData, + mint: bool, + ) -> DepositConsequence { + if amount.is_zero() { + return DepositConsequence::Success; + } + + if mint && TotalIssuance::::get().checked_add(&amount).is_none() { + return DepositConsequence::Overflow; + } + + let new_total_balance = match account.total().checked_add(&amount) { + Some(x) => x, + None => return DepositConsequence::Overflow, + }; + + if new_total_balance < T::ExistentialDeposit::get() { + return DepositConsequence::BelowMinimum; + } + + // NOTE: We assume that we are a provider, so don't need to do any checks in the + // case of account creation. + + DepositConsequence::Success + } + + fn withdraw_consequence( + who: &T::AccountId, + amount: T::Balance, + account: &AccountData, + ) -> WithdrawConsequence { + if amount.is_zero() { + return WithdrawConsequence::Success; + } + + if TotalIssuance::::get().checked_sub(&amount).is_none() { + return WithdrawConsequence::Underflow; + } + + let new_total_balance = match account.total().checked_sub(&amount) { + Some(x) => x, + None => return WithdrawConsequence::NoFunds, + }; + + // Provider restriction - total account balance cannot be reduced to zero if it cannot + // sustain the loss of a provider reference. + // NOTE: This assumes that the pallet is a provider (which is true). Is this ever changes, + // then this will need to adapt accordingly. + let ed = T::ExistentialDeposit::get(); + let success = if new_total_balance < ed { + if frame_system::Pallet::::can_dec_provider(who) { + WithdrawConsequence::ReducedToZero(new_total_balance) + } else { + return WithdrawConsequence::WouldDie; + } + } else { + WithdrawConsequence::Success + }; + + // Enough free funds to have them be reduced. + let new_free_balance = match account.free.checked_sub(&amount) { + Some(b) => b, + None => return WithdrawConsequence::NoFunds, + }; + + // Eventual free funds must be no less than the frozen balance. + let min_balance = account.frozen(Reasons::All); + if new_free_balance < min_balance { + return WithdrawConsequence::Frozen; + } + + success + } + + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce + /// `ExistentialDeposit` law, annulling the account as needed. + /// + /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used + /// when it is known that the account already exists. + /// + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + pub fn mutate_account( + who: &T::AccountId, + f: impl FnOnce(&mut AccountData) -> R, + ) -> Result { + Self::try_mutate_account(who, |a, _| -> Result { Ok(f(a)) }) + } + + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce + /// `ExistentialDeposit` law, annulling the account as needed. This will do nothing if the + /// result of `f` is an `Err`. + /// + /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used + /// when it is known that the account already exists. + /// + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + fn try_mutate_account>( + who: &T::AccountId, + f: impl FnOnce(&mut AccountData, bool) -> Result, + ) -> Result { + Self::try_mutate_account_with_dust(who, f).map(|(result, dust_cleaner)| { + drop(dust_cleaner); + result + }) + } + + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce + /// `ExistentialDeposit` law, annulling the account as needed. This will do nothing if the + /// result of `f` is an `Err`. + /// + /// It returns both the result from the closure, and an optional `DustCleaner` instance which + /// should be dropped once it is known that all nested mutates that could affect storage items + /// what the dust handler touches have completed. + /// + /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used + /// when it is known that the account already exists. + /// + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + fn try_mutate_account_with_dust>( + who: &T::AccountId, + f: impl FnOnce(&mut AccountData, bool) -> Result, + ) -> Result<(R, DustCleaner), E> { + let result = T::AccountStore::try_mutate_exists(who, |maybe_account| { + let is_new = maybe_account.is_none(); + let mut account = maybe_account.take().unwrap_or_default(); + f(&mut account, is_new).map(move |result| { + let maybe_endowed = if is_new { Some(account.free) } else { None }; + let maybe_account_maybe_dust = Self::post_mutation(who, account); + *maybe_account = maybe_account_maybe_dust.0; + (maybe_endowed, maybe_account_maybe_dust.1, result) + }) + }); + result.map(|(maybe_endowed, maybe_dust, result)| { + if let Some(endowed) = maybe_endowed { + Self::deposit_event(Event::Endowed { + account: who.clone(), + free_balance: endowed, + }); + } + let dust_cleaner = DustCleaner(maybe_dust.map(|dust| (who.clone(), dust))); + (result, dust_cleaner) + }) + } + + /// Update the account entry for `who`, given the locks. + fn update_locks(who: &T::AccountId, locks: &[BalanceLock]) { + let bounded_locks = WeakBoundedVec::<_, T::MaxLocks>::force_from( + locks.to_vec(), + Some("Balances Update Locks"), + ); + + if locks.len() as u32 > T::MaxLocks::get() { + log::warn!( + target: LOG_TARGET, + "Warning: A user has more currency locks than expected. \ + A runtime configuration adjustment may be needed." + ); + } + // No way this can fail since we do not alter the existential balances. + let res = Self::mutate_account(who, |b| { + b.misc_frozen = Zero::zero(); + b.fee_frozen = Zero::zero(); + for l in locks.iter() { + if l.reasons == Reasons::All || l.reasons == Reasons::Misc { + b.misc_frozen = b.misc_frozen.max(l.amount); + } + if l.reasons == Reasons::All || l.reasons == Reasons::Fee { + b.fee_frozen = b.fee_frozen.max(l.amount); + } + } + }); + debug_assert!(res.is_ok()); + + let existed = Locks::::contains_key(who); + if locks.is_empty() { + Locks::::remove(who); + if existed { + // TODO: use Locks::::hashed_key + // https://github.com/paritytech/substrate/issues/4969 + system::Pallet::::dec_consumers(who); + } + } else { + Locks::::insert(who, bounded_locks); + if !existed && system::Pallet::::inc_consumers_without_limit(who).is_err() { + // No providers for the locks. This is impossible under normal circumstances + // since the funds that are under the lock will themselves be stored in the + // account and therefore will need a reference. + log::warn!( + target: LOG_TARGET, + "Warning: Attempt to introduce lock consumer reference, yet no providers. \ + This is unexpected but should be safe." + ); + } + } + } + + /// Move the reserved balance of one account into the balance of another, according to `status`. + /// + /// Is a no-op if: + /// - the value to be moved is zero; or + /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. + /// + /// NOTE: returns actual amount of transferred value in `Ok` case. + fn do_transfer_reserved( + slashed: &T::AccountId, + beneficiary: &T::AccountId, + value: T::Balance, + best_effort: bool, + status: Status, + ) -> Result { + if value.is_zero() { + return Ok(Zero::zero()); + } + + if slashed == beneficiary { + return match status { + Status::Free => Ok(value.saturating_sub(Self::unreserve(slashed, value))), + Status::Reserved => Ok(value.saturating_sub(Self::reserved_balance(slashed))), + }; + } + + let ((actual, _maybe_one_dust), _maybe_other_dust) = Self::try_mutate_account_with_dust( + beneficiary, + |to_account, is_new| -> Result<(T::Balance, DustCleaner), DispatchError> { + ensure!(!is_new, Error::::DeadAccount); + Self::try_mutate_account_with_dust( + slashed, + |from_account, _| -> Result { + let actual = cmp::min(from_account.reserved, value); + ensure!( + best_effort || actual == value, + Error::::InsufficientBalance + ); + match status { + Status::Free => { + to_account.free = to_account + .free + .checked_add(&actual) + .ok_or(ArithmeticError::Overflow)? + } + Status::Reserved => { + to_account.reserved = to_account + .reserved + .checked_add(&actual) + .ok_or(ArithmeticError::Overflow)? + } + } + from_account.reserved -= actual; + Ok(actual) + }, + ) + }, + )?; + + Self::deposit_event(Event::ReserveRepatriated { + from: slashed.clone(), + to: beneficiary.clone(), + amount: actual, + destination_status: status, + }); + Ok(actual) + } +} + +impl, I: 'static> fungible::Inspect for Pallet { + type Balance = T::Balance; + + fn total_issuance() -> Self::Balance { + TotalIssuance::::get() + } + fn active_issuance() -> Self::Balance { + TotalIssuance::::get().saturating_sub(InactiveIssuance::::get()) + } + fn minimum_balance() -> Self::Balance { + T::ExistentialDeposit::get() + } + fn balance(who: &T::AccountId) -> Self::Balance { + Self::account(who).total() + } + fn reducible_balance(who: &T::AccountId, keep_alive: bool) -> Self::Balance { + let a = Self::account(who); + // Liquid balance is what is neither reserved nor locked/frozen. + let liquid = a.free.saturating_sub(a.fee_frozen.max(a.misc_frozen)); + if frame_system::Pallet::::can_dec_provider(who) && !keep_alive { + liquid + } else { + // `must_remain_to_exist` is the part of liquid balance which must remain to keep total + // over ED. + let must_remain_to_exist = + T::ExistentialDeposit::get().saturating_sub(a.total() - liquid); + liquid.saturating_sub(must_remain_to_exist) + } + } + fn can_deposit(who: &T::AccountId, amount: Self::Balance, mint: bool) -> DepositConsequence { + Self::deposit_consequence(who, amount, &Self::account(who), mint) + } + fn can_withdraw( + who: &T::AccountId, + amount: Self::Balance, + ) -> WithdrawConsequence { + Self::withdraw_consequence(who, amount, &Self::account(who)) + } +} + +impl, I: 'static> fungible::Mutate for Pallet { + fn mint_into(who: &T::AccountId, amount: Self::Balance) -> DispatchResult { + if amount.is_zero() { + return Ok(()); + } + Self::try_mutate_account(who, |account, _is_new| -> DispatchResult { + Self::deposit_consequence(who, amount, account, true).into_result()?; + account.free += amount; + Ok(()) + })?; + TotalIssuance::::mutate(|t| *t += amount); + Self::deposit_event(Event::Deposit { + who: who.clone(), + amount, + }); + Ok(()) + } + + fn burn_from( + who: &T::AccountId, + amount: Self::Balance, + ) -> Result { + if amount.is_zero() { + return Ok(Self::Balance::zero()); + } + let actual = Self::try_mutate_account( + who, + |account, _is_new| -> Result { + let extra = Self::withdraw_consequence(who, amount, account).into_result()?; + let actual = amount + extra; + account.free -= actual; + Ok(actual) + }, + )?; + TotalIssuance::::mutate(|t| *t -= actual); + Self::deposit_event(Event::Withdraw { + who: who.clone(), + amount, + }); + Ok(actual) + } +} + +impl, I: 'static> fungible::Transfer for Pallet { + fn transfer( + source: &T::AccountId, + dest: &T::AccountId, + amount: T::Balance, + keep_alive: bool, + ) -> Result { + let er = if keep_alive { KeepAlive } else { AllowDeath }; + >::transfer(source, dest, amount, er).map(|_| amount) + } + + fn deactivate(amount: Self::Balance) { + InactiveIssuance::::mutate(|b| b.saturating_accrue(amount)); + } + + fn reactivate(amount: Self::Balance) { + InactiveIssuance::::mutate(|b| b.saturating_reduce(amount)); + } +} + +impl, I: 'static> fungible::Unbalanced for Pallet { + fn set_balance(who: &T::AccountId, amount: Self::Balance) -> DispatchResult { + Self::mutate_account(who, |account| -> DispatchResult { + // fungibles::Unbalanced::decrease_balance didn't check account.reserved + // free = new_balance - reserved + account.free = amount + .checked_sub(&account.reserved) + .ok_or(ArithmeticError::Underflow)?; + Self::deposit_event(Event::BalanceSet { + who: who.clone(), + free: account.free, + reserved: account.reserved, + }); + + Ok(()) + })? + } + + fn set_total_issuance(amount: Self::Balance) { + TotalIssuance::::mutate(|t| *t = amount); + } +} + +impl, I: 'static> fungible::InspectHold for Pallet { + fn balance_on_hold(who: &T::AccountId) -> T::Balance { + Self::account(who).reserved + } + fn can_hold(who: &T::AccountId, amount: T::Balance) -> bool { + let a = Self::account(who); + let min_balance = T::ExistentialDeposit::get().max(a.frozen(Reasons::All)); + if a.reserved.checked_add(&amount).is_none() { + return false; + } + // We require it to be min_balance + amount to ensure that the full reserved funds may be + // slashed without compromising locked funds or destroying the account. + let required_free = match min_balance.checked_add(&amount) { + Some(x) => x, + None => return false, + }; + a.free >= required_free + } +} +impl, I: 'static> fungible::MutateHold for Pallet { + fn hold(who: &T::AccountId, amount: Self::Balance) -> DispatchResult { + if amount.is_zero() { + return Ok(()); + } + ensure!( + Self::can_reserve(who, amount), + Error::::InsufficientBalance + ); + Self::mutate_account(who, |a| { + a.free -= amount; + a.reserved += amount; + })?; + Ok(()) + } + fn release( + who: &T::AccountId, + amount: Self::Balance, + best_effort: bool, + ) -> Result { + if amount.is_zero() { + return Ok(amount); + } + // Done on a best-effort basis. + Self::try_mutate_account(who, |a, _| { + let new_free = a.free.saturating_add(amount.min(a.reserved)); + let actual = new_free - a.free; + ensure!( + best_effort || actual == amount, + Error::::InsufficientBalance + ); + // ^^^ Guaranteed to be <= amount and <= a.reserved + a.free = new_free; + a.reserved = a.reserved.saturating_sub(actual); + Ok(actual) + }) + } + fn transfer_held( + source: &T::AccountId, + dest: &T::AccountId, + amount: Self::Balance, + best_effort: bool, + on_hold: bool, + ) -> Result { + let status = if on_hold { + Status::Reserved + } else { + Status::Free + }; + Self::do_transfer_reserved(source, dest, amount, best_effort, status) + } +} + +// wrapping these imbalances in a private module is necessary to ensure absolute privacy +// of the inner member. +mod imbalances { + use super::{result, Config, Imbalance, RuntimeDebug, Saturating, TryDrop, Zero}; + use frame_support::traits::SameOrOther; + use sp_std::mem; + + /// Opaque, move-only struct with private fields that serves as a token denoting that + /// funds have been created without any equal and opposite accounting. + #[must_use] + #[derive(RuntimeDebug, PartialEq, Eq)] + pub struct PositiveImbalance, I: 'static = ()>(T::Balance); + + impl, I: 'static> PositiveImbalance { + /// Create a new positive imbalance from a balance. + pub fn new(amount: T::Balance) -> Self { + PositiveImbalance(amount) + } + } + + /// Opaque, move-only struct with private fields that serves as a token denoting that + /// funds have been destroyed without any equal and opposite accounting. + #[must_use] + #[derive(RuntimeDebug, PartialEq, Eq)] + pub struct NegativeImbalance, I: 'static = ()>(T::Balance); + + impl, I: 'static> NegativeImbalance { + /// Create a new negative imbalance from a balance. + pub fn new(amount: T::Balance) -> Self { + NegativeImbalance(amount) + } + } + + impl, I: 'static> TryDrop for PositiveImbalance { + fn try_drop(self) -> result::Result<(), Self> { + self.drop_zero() + } + } + + impl, I: 'static> Default for PositiveImbalance { + fn default() -> Self { + Self::zero() + } + } + + impl, I: 'static> Imbalance for PositiveImbalance { + type Opposite = NegativeImbalance; + + fn zero() -> Self { + Self(Zero::zero()) + } + fn drop_zero(self) -> result::Result<(), Self> { + if self.0.is_zero() { + Ok(()) + } else { + Err(self) + } + } + fn split(self, amount: T::Balance) -> (Self, Self) { + let first = self.0.min(amount); + let second = self.0 - first; + + mem::forget(self); + (Self(first), Self(second)) + } + fn merge(mut self, other: Self) -> Self { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + + self + } + fn subsume(&mut self, other: Self) { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + } + fn offset(self, other: Self::Opposite) -> SameOrOther { + let (a, b) = (self.0, other.0); + mem::forget((self, other)); + + if a > b { + SameOrOther::Same(Self(a - b)) + } else if b > a { + SameOrOther::Other(NegativeImbalance::new(b - a)) + } else { + SameOrOther::None + } + } + fn peek(&self) -> T::Balance { + self.0 + } + } + + impl, I: 'static> TryDrop for NegativeImbalance { + fn try_drop(self) -> result::Result<(), Self> { + self.drop_zero() + } + } + + impl, I: 'static> Default for NegativeImbalance { + fn default() -> Self { + Self::zero() + } + } + + impl, I: 'static> Imbalance for NegativeImbalance { + type Opposite = PositiveImbalance; + + fn zero() -> Self { + Self(Zero::zero()) + } + fn drop_zero(self) -> result::Result<(), Self> { + if self.0.is_zero() { + Ok(()) + } else { + Err(self) + } + } + fn split(self, amount: T::Balance) -> (Self, Self) { + let first = self.0.min(amount); + let second = self.0 - first; + + mem::forget(self); + (Self(first), Self(second)) + } + fn merge(mut self, other: Self) -> Self { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + + self + } + fn subsume(&mut self, other: Self) { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + } + fn offset(self, other: Self::Opposite) -> SameOrOther { + let (a, b) = (self.0, other.0); + mem::forget((self, other)); + + if a > b { + SameOrOther::Same(Self(a - b)) + } else if b > a { + SameOrOther::Other(PositiveImbalance::new(b - a)) + } else { + SameOrOther::None + } + } + fn peek(&self) -> T::Balance { + self.0 + } + } + + impl, I: 'static> Drop for PositiveImbalance { + /// Basic drop handler will just square up the total issuance. + fn drop(&mut self) { + >::mutate(|v| *v = v.saturating_add(self.0)); + } + } + + impl, I: 'static> Drop for NegativeImbalance { + /// Basic drop handler will just square up the total issuance. + fn drop(&mut self) { + >::mutate(|v| *v = v.saturating_sub(self.0)); + } + } +} + +impl, I: 'static> Currency for Pallet +where + T::Balance: MaybeSerializeDeserialize + Debug, +{ + type Balance = T::Balance; + type PositiveImbalance = PositiveImbalance; + type NegativeImbalance = NegativeImbalance; + + fn total_balance(who: &T::AccountId) -> Self::Balance { + Self::account(who).total() + } + + // Check if `value` amount of free balance can be slashed from `who`. + fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool { + if value.is_zero() { + return true; + } + Self::free_balance(who) >= value + } + + fn total_issuance() -> Self::Balance { + TotalIssuance::::get() + } + + fn active_issuance() -> Self::Balance { + >::active_issuance() + } + + fn deactivate(amount: Self::Balance) { + >::deactivate(amount); + } + + fn reactivate(amount: Self::Balance) { + >::reactivate(amount); + } + + fn minimum_balance() -> Self::Balance { + T::ExistentialDeposit::get() + } + + // Burn funds from the total issuance, returning a positive imbalance for the amount burned. + // Is a no-op if amount to be burned is zero. + fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance { + if amount.is_zero() { + return PositiveImbalance::zero(); + } + >::mutate(|issued| { + *issued = issued.checked_sub(&amount).unwrap_or_else(|| { + amount = *issued; + Zero::zero() + }); + }); + PositiveImbalance::new(amount) + } + + // Create new funds into the total issuance, returning a negative imbalance + // for the amount issued. + // Is a no-op if amount to be issued it zero. + fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance { + if amount.is_zero() { + return NegativeImbalance::zero(); + } + >::mutate(|issued| { + *issued = issued.checked_add(&amount).unwrap_or_else(|| { + amount = Self::Balance::max_value() - *issued; + Self::Balance::max_value() + }) + }); + NegativeImbalance::new(amount) + } + + fn free_balance(who: &T::AccountId) -> Self::Balance { + Self::account(who).free + } + + // Ensure that an account can withdraw from their free balance given any existing withdrawal + // restrictions like locks and vesting balance. + // Is a no-op if amount to be withdrawn is zero. + // + // # + // Despite iterating over a list of locks, they are limited by the number of + // lock IDs, which means the number of runtime pallets that intend to use and create locks. + // # + fn ensure_can_withdraw( + who: &T::AccountId, + amount: T::Balance, + reasons: WithdrawReasons, + new_balance: T::Balance, + ) -> DispatchResult { + if amount.is_zero() { + return Ok(()); + } + let min_balance = Self::account(who).frozen(reasons.into()); + ensure!( + new_balance >= min_balance, + Error::::LiquidityRestrictions + ); + Ok(()) + } + + // Transfer some free balance from `transactor` to `dest`, respecting existence requirements. + // Is a no-op if value to be transferred is zero or the `transactor` is the same as `dest`. + fn transfer( + transactor: &T::AccountId, + dest: &T::AccountId, + value: Self::Balance, + existence_requirement: ExistenceRequirement, + ) -> DispatchResult { + if value.is_zero() || transactor == dest { + return Ok(()); + } + + Self::try_mutate_account_with_dust( + dest, + |to_account, _| -> Result, DispatchError> { + Self::try_mutate_account_with_dust( + transactor, + |from_account, _| -> DispatchResult { + from_account.free = from_account + .free + .checked_sub(&value) + .ok_or(Error::::InsufficientBalance)?; + + // NOTE: total stake being stored in the same type means that this could + // never overflow but better to be safe than sorry. + to_account.free = to_account + .free + .checked_add(&value) + .ok_or(ArithmeticError::Overflow)?; + + let ed = T::ExistentialDeposit::get(); + ensure!(to_account.total() >= ed, Error::::ExistentialDeposit); + + Self::ensure_can_withdraw( + transactor, + value, + WithdrawReasons::TRANSFER, + from_account.free, + ) + .map_err(|_| Error::::LiquidityRestrictions)?; + + // TODO: This is over-conservative. There may now be other providers, and + // this pallet may not even be a provider. + let allow_death = existence_requirement == ExistenceRequirement::AllowDeath; + let allow_death = + allow_death && system::Pallet::::can_dec_provider(transactor); + ensure!( + allow_death || from_account.total() >= ed, + Error::::KeepAlive + ); + + Ok(()) + }, + ) + .map(|(_, maybe_dust_cleaner)| maybe_dust_cleaner) + }, + )?; + + // Emit transfer event. + Self::deposit_event(Event::Transfer { + from: transactor.clone(), + to: dest.clone(), + amount: value, + }); + + Ok(()) + } + + /// Slash a target account `who`, returning the negative imbalance created and any left over + /// amount that could not be slashed. + /// + /// Is a no-op if `value` to be slashed is zero or the account does not exist. + /// + /// NOTE: `slash()` prefers free balance, but assumes that reserve balance can be drawn + /// from in extreme circumstances. `can_slash()` should be used prior to `slash()` to avoid + /// having to draw from reserved funds, however we err on the side of punishment if things are + /// inconsistent or `can_slash` wasn't used appropriately. + fn slash(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) { + if value.is_zero() { + return (NegativeImbalance::zero(), Zero::zero()); + } + if Self::total_balance(who).is_zero() { + return (NegativeImbalance::zero(), value); + } + + for attempt in 0..2 { + match Self::try_mutate_account( + who, + |account, + _is_new| + -> Result<(Self::NegativeImbalance, Self::Balance), DispatchError> { + // Best value is the most amount we can slash following liveness rules. + let best_value = match attempt { + // First attempt we try to slash the full amount, and see if liveness issues + // happen. + 0 => value, + // If acting as a critical provider (i.e. first attempt failed), then slash + // as much as possible while leaving at least at ED. + _ => value.min( + (account.free + account.reserved) + .saturating_sub(T::ExistentialDeposit::get()), + ), + }; + + let free_slash = cmp::min(account.free, best_value); + account.free -= free_slash; // Safe because of above check + let remaining_slash = best_value - free_slash; // Safe because of above check + + if !remaining_slash.is_zero() { + // If we have remaining slash, take it from reserved balance. + let reserved_slash = cmp::min(account.reserved, remaining_slash); + account.reserved -= reserved_slash; // Safe because of above check + Ok(( + NegativeImbalance::new(free_slash + reserved_slash), + value - free_slash - reserved_slash, /* Safe because value is gt or + * eq total slashed */ + )) + } else { + // Else we are done! + Ok(( + NegativeImbalance::new(free_slash), + value - free_slash, // Safe because value is gt or eq to total slashed + )) + } + }, + ) { + Ok((imbalance, not_slashed)) => { + Self::deposit_event(Event::Slashed { + who: who.clone(), + amount: value.saturating_sub(not_slashed), + }); + return (imbalance, not_slashed) + }, + Err(_) => (), + } + } + + // Should never get here. But we'll be defensive anyway. + (Self::NegativeImbalance::zero(), value) + } + + /// Deposit some `value` into the free balance of an existing target account `who`. + /// + /// Is a no-op if the `value` to be deposited is zero. + fn deposit_into_existing( + who: &T::AccountId, + value: Self::Balance, + ) -> Result { + if value.is_zero() { + return Ok(PositiveImbalance::zero()); + } + + Self::try_mutate_account( + who, + |account, is_new| -> Result { + ensure!(!is_new, Error::::DeadAccount); + account.free = account + .free + .checked_add(&value) + .ok_or(ArithmeticError::Overflow)?; + Self::deposit_event(Event::Deposit { + who: who.clone(), + amount: value, + }); + Ok(PositiveImbalance::new(value)) + }, + ) + } + + /// Deposit some `value` into the free balance of `who`, possibly creating a new account. + /// + /// This function is a no-op if: + /// - the `value` to be deposited is zero; or + /// - the `value` to be deposited is less than the required ED and the account does not yet + /// exist; or + /// - the deposit would necessitate the account to exist and there are no provider references; + /// or + /// - `value` is so large it would cause the balance of `who` to overflow. + fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance { + if value.is_zero() { + return Self::PositiveImbalance::zero(); + } + + Self::try_mutate_account( + who, + |account, is_new| -> Result { + let ed = T::ExistentialDeposit::get(); + ensure!(value >= ed || !is_new, Error::::ExistentialDeposit); + + // defensive only: overflow should never happen, however in case it does, then this + // operation is a no-op. + account.free = match account.free.checked_add(&value) { + Some(x) => x, + None => return Ok(Self::PositiveImbalance::zero()), + }; + + Self::deposit_event(Event::Deposit { + who: who.clone(), + amount: value, + }); + Ok(PositiveImbalance::new(value)) + }, + ) + .unwrap_or_else(|_| Self::PositiveImbalance::zero()) + } + + /// Withdraw some free balance from an account, respecting existence requirements. + /// + /// Is a no-op if value to be withdrawn is zero. + fn withdraw( + who: &T::AccountId, + value: Self::Balance, + reasons: WithdrawReasons, + liveness: ExistenceRequirement, + ) -> result::Result { + if value.is_zero() { + return Ok(NegativeImbalance::zero()); + } + + Self::try_mutate_account( + who, + |account, _| -> Result { + let new_free_account = account + .free + .checked_sub(&value) + .ok_or(Error::::InsufficientBalance)?; + + // bail if we need to keep the account alive and this would kill it. + let ed = T::ExistentialDeposit::get(); + let would_be_dead = new_free_account + account.reserved < ed; + let would_kill = would_be_dead && account.free + account.reserved >= ed; + ensure!( + liveness == AllowDeath || !would_kill, + Error::::KeepAlive + ); + + Self::ensure_can_withdraw(who, value, reasons, new_free_account)?; + + account.free = new_free_account; + + Self::deposit_event(Event::Withdraw { + who: who.clone(), + amount: value, + }); + Ok(NegativeImbalance::new(value)) + }, + ) + } + + /// Force the new free balance of a target account `who` to some new value `balance`. + fn make_free_balance_be( + who: &T::AccountId, + value: Self::Balance, + ) -> SignedImbalance { + Self::try_mutate_account( + who, + |account, + is_new| + -> Result, DispatchError> { + let ed = T::ExistentialDeposit::get(); + let total = value.saturating_add(account.reserved); + // If we're attempting to set an existing account to less than ED, then + // bypass the entire operation. It's a no-op if you follow it through, but + // since this is an instance where we might account for a negative imbalance + // (in the dust cleaner of set_account) before we account for its actual + // equal and opposite cause (returned as an Imbalance), then in the + // instance that there's no other accounts on the system at all, we might + // underflow the issuance and our arithmetic will be off. + ensure!(total >= ed || !is_new, Error::::ExistentialDeposit); + + let imbalance = if account.free <= value { + SignedImbalance::Positive(PositiveImbalance::new(value - account.free)) + } else { + SignedImbalance::Negative(NegativeImbalance::new(account.free - value)) + }; + account.free = value; + Self::deposit_event(Event::BalanceSet { + who: who.clone(), + free: account.free, + reserved: account.reserved, + }); + Ok(imbalance) + }, + ) + .unwrap_or_else(|_| SignedImbalance::Positive(Self::PositiveImbalance::zero())) + } +} + +impl, I: 'static> ReservableCurrency for Pallet +where + T::Balance: MaybeSerializeDeserialize + Debug, +{ + /// Check if `who` can reserve `value` from their free balance. + /// + /// Always `true` if value to be reserved is zero. + fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool { + if value.is_zero() { + return true; + } + Self::account(who) + .free + .checked_sub(&value) + .map_or(false, |new_balance| { + Self::ensure_can_withdraw(who, value, WithdrawReasons::RESERVE, new_balance).is_ok() + }) + } + + fn reserved_balance(who: &T::AccountId) -> Self::Balance { + Self::account(who).reserved + } + + /// Move `value` from the free balance from `who` to their reserved balance. + /// + /// Is a no-op if value to be reserved is zero. + fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult { + if value.is_zero() { + return Ok(()); + } + + Self::try_mutate_account(who, |account, _| -> DispatchResult { + account.free = account + .free + .checked_sub(&value) + .ok_or(Error::::InsufficientBalance)?; + account.reserved = account + .reserved + .checked_add(&value) + .ok_or(ArithmeticError::Overflow)?; + Self::ensure_can_withdraw(&who, value, WithdrawReasons::RESERVE, account.free) + })?; + + Self::deposit_event(Event::Reserved { + who: who.clone(), + amount: value, + }); + Ok(()) + } + + /// Unreserve some funds, returning any amount that was unable to be unreserved. + /// + /// Is a no-op if the value to be unreserved is zero or the account does not exist. + /// + /// NOTE: returns amount value which wasn't successfully unreserved. + fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance { + if value.is_zero() { + return Zero::zero(); + } + if Self::total_balance(who).is_zero() { + return value; + } + + let actual = match Self::mutate_account(who, |account| { + let actual = cmp::min(account.reserved, value); + account.reserved -= actual; + // defensive only: this can never fail since total issuance which is at least + // free+reserved fits into the same data type. + account.free = account.free.defensive_saturating_add(actual); + actual + }) { + Ok(x) => x, + Err(_) => { + // This should never happen since we don't alter the total amount in the account. + // If it ever does, then we should fail gracefully though, indicating that nothing + // could be done. + return value; + } + }; + + Self::deposit_event(Event::Unreserved { + who: who.clone(), + amount: actual, + }); + value - actual + } + + /// Slash from reserved balance, returning the negative imbalance created, + /// and any amount that was unable to be slashed. + /// + /// Is a no-op if the value to be slashed is zero or the account does not exist. + fn slash_reserved( + who: &T::AccountId, + value: Self::Balance, + ) -> (Self::NegativeImbalance, Self::Balance) { + if value.is_zero() { + return (NegativeImbalance::zero(), Zero::zero()); + } + if Self::total_balance(who).is_zero() { + return (NegativeImbalance::zero(), value); + } + + // NOTE: `mutate_account` may fail if it attempts to reduce the balance to the point that an + // account is attempted to be illegally destroyed. + + for attempt in 0..2 { + match Self::mutate_account(who, |account| { + let best_value = match attempt { + 0 => value, + // If acting as a critical provider (i.e. first attempt failed), then ensure + // slash leaves at least the ED. + _ => value.min( + (account.free + account.reserved) + .saturating_sub(T::ExistentialDeposit::get()), + ), + }; + + let actual = cmp::min(account.reserved, best_value); + account.reserved -= actual; + + // underflow should never happen, but it if does, there's nothing to be done here. + (NegativeImbalance::new(actual), value - actual) + }) { + Ok((imbalance, not_slashed)) => { + Self::deposit_event(Event::Slashed { + who: who.clone(), + amount: value.saturating_sub(not_slashed), + }); + return (imbalance, not_slashed); + } + Err(_) => (), + } + } + // Should never get here as we ensure that ED is left in the second attempt. + // In case we do, though, then we fail gracefully. + (Self::NegativeImbalance::zero(), value) + } + + /// Move the reserved balance of one account into the balance of another, according to `status`. + /// + /// Is a no-op if: + /// - the value to be moved is zero; or + /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. + fn repatriate_reserved( + slashed: &T::AccountId, + beneficiary: &T::AccountId, + value: Self::Balance, + status: Status, + ) -> Result { + let actual = Self::do_transfer_reserved(slashed, beneficiary, value, true, status)?; + Ok(value.saturating_sub(actual)) + } +} + +impl, I: 'static> NamedReservableCurrency for Pallet +where + T::Balance: MaybeSerializeDeserialize + Debug, +{ + type ReserveIdentifier = T::ReserveIdentifier; + + fn reserved_balance_named(id: &Self::ReserveIdentifier, who: &T::AccountId) -> Self::Balance { + let reserves = Self::reserves(who); + reserves + .binary_search_by_key(id, |data| data.id) + .map(|index| reserves[index].amount) + .unwrap_or_default() + } + + /// Move `value` from the free balance from `who` to a named reserve balance. + /// + /// Is a no-op if value to be reserved is zero. + fn reserve_named( + id: &Self::ReserveIdentifier, + who: &T::AccountId, + value: Self::Balance, + ) -> DispatchResult { + if value.is_zero() { + return Ok(()); + } + + Reserves::::try_mutate(who, |reserves| -> DispatchResult { + match reserves.binary_search_by_key(id, |data| data.id) { + Ok(index) => { + // this add can't overflow but just to be defensive. + reserves[index].amount = reserves[index].amount.defensive_saturating_add(value); + } + Err(index) => { + reserves + .try_insert( + index, + ReserveData { + id: *id, + amount: value, + }, + ) + .map_err(|_| Error::::TooManyReserves)?; + } + }; + >::reserve(who, value)?; + Ok(()) + }) + } + + /// Unreserve some funds, returning any amount that was unable to be unreserved. + /// + /// Is a no-op if the value to be unreserved is zero. + fn unreserve_named( + id: &Self::ReserveIdentifier, + who: &T::AccountId, + value: Self::Balance, + ) -> Self::Balance { + if value.is_zero() { + return Zero::zero(); + } + + Reserves::::mutate_exists(who, |maybe_reserves| -> Self::Balance { + if let Some(reserves) = maybe_reserves.as_mut() { + match reserves.binary_search_by_key(id, |data| data.id) { + Ok(index) => { + let to_change = cmp::min(reserves[index].amount, value); + + let remain = >::unreserve(who, to_change); + + // remain should always be zero but just to be defensive here. + let actual = to_change.defensive_saturating_sub(remain); + + // `actual <= to_change` and `to_change <= amount`; qed; + reserves[index].amount -= actual; + + if reserves[index].amount.is_zero() { + if reserves.len() == 1 { + // no more named reserves + *maybe_reserves = None; + } else { + // remove this named reserve + reserves.remove(index); + } + } + + value - actual + } + Err(_) => value, + } + } else { + value + } + }) + } + + /// Slash from reserved balance, returning the negative imbalance created, + /// and any amount that was unable to be slashed. + /// + /// Is a no-op if the value to be slashed is zero. + fn slash_reserved_named( + id: &Self::ReserveIdentifier, + who: &T::AccountId, + value: Self::Balance, + ) -> (Self::NegativeImbalance, Self::Balance) { + if value.is_zero() { + return (NegativeImbalance::zero(), Zero::zero()); + } + + Reserves::::mutate( + who, + |reserves| -> (Self::NegativeImbalance, Self::Balance) { + match reserves.binary_search_by_key(id, |data| data.id) { + Ok(index) => { + let to_change = cmp::min(reserves[index].amount, value); + + let (imb, remain) = + >::slash_reserved(who, to_change); + + // remain should always be zero but just to be defensive here. + let actual = to_change.defensive_saturating_sub(remain); + + // `actual <= to_change` and `to_change <= amount`; qed; + reserves[index].amount -= actual; + + Self::deposit_event(Event::Slashed { + who: who.clone(), + amount: actual, + }); + (imb, value - actual) + } + Err(_) => (NegativeImbalance::zero(), value), + } + }, + ) + } + + /// Move the reserved balance of one account into the balance of another, according to `status`. + /// If `status` is `Reserved`, the balance will be reserved with given `id`. + /// + /// Is a no-op if: + /// - the value to be moved is zero; or + /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. + fn repatriate_reserved_named( + id: &Self::ReserveIdentifier, + slashed: &T::AccountId, + beneficiary: &T::AccountId, + value: Self::Balance, + status: Status, + ) -> Result { + if value.is_zero() { + return Ok(Zero::zero()); + } + + if slashed == beneficiary { + return match status { + Status::Free => Ok(Self::unreserve_named(id, slashed, value)), + Status::Reserved => { + Ok(value.saturating_sub(Self::reserved_balance_named(id, slashed))) + } + }; + } + + Reserves::::try_mutate( + slashed, + |reserves| -> Result { + match reserves.binary_search_by_key(id, |data| data.id) { + Ok(index) => { + let to_change = cmp::min(reserves[index].amount, value); + + let actual = if status == Status::Reserved { + // make it the reserved under same identifier + Reserves::::try_mutate( + beneficiary, + |reserves| -> Result { + match reserves.binary_search_by_key(id, |data| data.id) { + Ok(index) => { + let remain = + >::repatriate_reserved( + slashed, + beneficiary, + to_change, + status, + )?; + + // remain should always be zero but just to be defensive + // here. + let actual = to_change.defensive_saturating_sub(remain); + + // this add can't overflow but just to be defensive. + reserves[index].amount = reserves[index] + .amount + .defensive_saturating_add(actual); + + Ok(actual) + } + Err(index) => { + let remain = + >::repatriate_reserved( + slashed, + beneficiary, + to_change, + status, + )?; + + // remain should always be zero but just to be defensive + // here + let actual = to_change.defensive_saturating_sub(remain); + + reserves + .try_insert( + index, + ReserveData { + id: *id, + amount: actual, + }, + ) + .map_err(|_| Error::::TooManyReserves)?; + + Ok(actual) + } + } + }, + )? + } else { + let remain = >::repatriate_reserved( + slashed, + beneficiary, + to_change, + status, + )?; + + // remain should always be zero but just to be defensive here + to_change.defensive_saturating_sub(remain) + }; + + // `actual <= to_change` and `to_change <= amount`; qed; + reserves[index].amount -= actual; + + Ok(value - actual) + } + Err(_) => Ok(value), + } + }, + ) + } +} + +impl, I: 'static> LockableCurrency for Pallet +where + T::Balance: MaybeSerializeDeserialize + Debug, +{ + type Moment = T::BlockNumber; + + type MaxLocks = T::MaxLocks; + + // Set a lock on the balance of `who`. + // Is a no-op if lock amount is zero or `reasons` `is_none()`. + fn set_lock( + id: LockIdentifier, + who: &T::AccountId, + amount: T::Balance, + reasons: WithdrawReasons, + ) { + if amount.is_zero() || reasons.is_empty() { + return; + } + let mut new_lock = Some(BalanceLock { + id, + amount, + reasons: reasons.into(), + }); + let mut locks = Self::locks(who) + .into_iter() + .filter_map(|l| if l.id == id { new_lock.take() } else { Some(l) }) + .collect::>(); + if let Some(lock) = new_lock { + locks.push(lock) + } + Self::update_locks(who, &locks[..]); + } + + // Extend a lock on the balance of `who`. + // Is a no-op if lock amount is zero or `reasons` `is_none()`. + fn extend_lock( + id: LockIdentifier, + who: &T::AccountId, + amount: T::Balance, + reasons: WithdrawReasons, + ) { + if amount.is_zero() || reasons.is_empty() { + return; + } + let mut new_lock = Some(BalanceLock { + id, + amount, + reasons: reasons.into(), + }); + let mut locks = Self::locks(who) + .into_iter() + .filter_map(|l| { + if l.id == id { + new_lock.take().map(|nl| BalanceLock { + id: l.id, + amount: l.amount.max(nl.amount), + reasons: l.reasons | nl.reasons, + }) + } else { + Some(l) + } + }) + .collect::>(); + if let Some(lock) = new_lock { + locks.push(lock) + } + Self::update_locks(who, &locks[..]); + } + + fn remove_lock(id: LockIdentifier, who: &T::AccountId) { + let mut locks = Self::locks(who); + locks.retain(|l| l.id != id); + Self::update_locks(who, &locks[..]); + } +} diff --git a/pallets/balances/src/migration.rs b/pallets/balances/src/migration.rs new file mode 100644 index 000000000..6ac29567e --- /dev/null +++ b/pallets/balances/src/migration.rs @@ -0,0 +1,104 @@ +// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . +#![allow(clippy::all)] + +use super::*; +use frame_support::{ + pallet_prelude::*, + traits::{OnRuntimeUpgrade, PalletInfoAccess}, + weights::Weight, +}; + +fn migrate_v0_to_v1, I: 'static>(accounts: &[T::AccountId]) -> Weight { + let onchain_version = Pallet::::on_chain_storage_version(); + + if onchain_version == 0 { + let total = accounts + .iter() + .map(|a| Pallet::::total_balance(a)) + .fold(T::Balance::zero(), |a, e| a.saturating_add(e)); + Pallet::::deactivate(total); + + // Remove the old `StorageVersion` type. + frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix( + Pallet::::name().as_bytes(), + "StorageVersion".as_bytes(), + )); + + // Set storage version to `1`. + StorageVersion::new(1).put::>(); + + log::info!(target: LOG_TARGET, "Storage to version 1"); + T::DbWeight::get().reads_writes(2 + accounts.len() as u64, 3) + } else { + log::info!( + target: LOG_TARGET, + "Migration did not execute. This probably should be removed" + ); + T::DbWeight::get().reads(1) + } +} + +// NOTE: This must be used alongside the account whose balance is expected to be inactive. +// Generally this will be used for the XCM teleport checking account. +pub struct MigrateToTrackInactive(PhantomData<(T, A, I)>); +impl, A: Get, I: 'static> OnRuntimeUpgrade + for MigrateToTrackInactive +{ + fn on_runtime_upgrade() -> Weight { + migrate_v0_to_v1::(&[A::get()]) + } +} + +// NOTE: This must be used alongside the accounts whose balance is expected to be inactive. +// Generally this will be used for the XCM teleport checking accounts. +pub struct MigrateManyToTrackInactive(PhantomData<(T, A, I)>); +impl, A: Get>, I: 'static> OnRuntimeUpgrade + for MigrateManyToTrackInactive +{ + fn on_runtime_upgrade() -> Weight { + migrate_v0_to_v1::(&A::get()) + } +} + +pub struct ResetInactive(PhantomData<(T, I)>); +impl, I: 'static> OnRuntimeUpgrade for ResetInactive { + fn on_runtime_upgrade() -> Weight { + let onchain_version = Pallet::::on_chain_storage_version(); + + if onchain_version == 1 { + // Remove the old `StorageVersion` type. + frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix( + Pallet::::name().as_bytes(), + "StorageVersion".as_bytes(), + )); + + InactiveIssuance::::kill(); + + // Set storage version to `0`. + StorageVersion::new(0).put::>(); + + log::info!(target: LOG_TARGET, "Storage to version 0"); + T::DbWeight::get().reads_writes(1, 2) + } else { + log::info!( + target: LOG_TARGET, + "Migration did not execute. This probably should be removed" + ); + T::DbWeight::get().reads(1) + } + } +} diff --git a/pallets/balances/src/tests.rs b/pallets/balances/src/tests.rs new file mode 100644 index 000000000..4b97404d2 --- /dev/null +++ b/pallets/balances/src/tests.rs @@ -0,0 +1,1456 @@ +// Copyright 2020-2023 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . + +//! Macro for creating the tests for the module. + +#![cfg(test)] +#![allow(clippy::all)] + +#[macro_export] +macro_rules! decl_tests { + ($test:ty, $ext_builder:ty, $existential_deposit:expr) => { + + use crate::*; + use sp_runtime::{ArithmeticError, TokenError, FixedPointNumber, traits::{SignedExtension, BadOrigin}}; + use frame_support::{ + assert_noop, assert_storage_noop, assert_ok, assert_err, + traits::{ + LockableCurrency, LockIdentifier, WithdrawReasons, + Currency, ReservableCurrency, ExistenceRequirement::AllowDeath + } + }; + use pallet_transaction_payment::{ChargeTransactionPayment, Multiplier}; + use frame_system::RawOrigin; + + const ID_1: LockIdentifier = *b"1 "; + const ID_2: LockIdentifier = *b"2 "; + + pub const CALL: &<$test as frame_system::Config>::RuntimeCall = + &RuntimeCall::Balances(pallet_balances::Call::transfer { dest: 0, value: 0 }); + + /// create a transaction info struct from weight. Handy to avoid building the whole struct. + pub fn info_from_weight(w: Weight) -> DispatchInfo { + DispatchInfo { weight: w, ..Default::default() } + } + + fn events() -> Vec { + let evt = System::events().into_iter().map(|evt| evt.event).collect::>(); + + System::reset_events(); + + evt + } + + #[test] + fn basic_locking_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + assert_eq!(Balances::free_balance(1), 10); + Balances::set_lock(ID_1, &1, 9, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 5, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + }); + } + + #[test] + fn account_should_be_reaped() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + assert_eq!(Balances::free_balance(1), 10); + assert_ok!(>::transfer(&1, &2, 10, AllowDeath)); + // Check that the account is dead. + assert!(!frame_system::Account::::contains_key(&1)); + }); + } + + #[test] + fn reap_failed_due_to_provider_and_consumer() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + // SCENARIO: only one provider and there are remaining consumers. + assert_ok!(System::inc_consumers(&1)); + assert!(!System::can_dec_provider(&1)); + assert_noop!( + >::transfer(&1, &2, 10, AllowDeath), + Error::<$test, _>::KeepAlive + ); + assert!(System::account_exists(&1)); + assert_eq!(Balances::free_balance(1), 10); + + // SCENARIO: more than one provider, but will not kill account due to other provider. + assert_eq!(System::inc_providers(&1), frame_system::IncRefStatus::Existed); + assert_eq!(System::providers(&1), 2); + assert!(System::can_dec_provider(&1)); + assert_ok!(>::transfer(&1, &2, 10, AllowDeath)); + assert_eq!(System::providers(&1), 1); + assert!(System::account_exists(&1)); + assert_eq!(Balances::free_balance(1), 0); + }); + } + + #[test] + fn partial_locking_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); + } + + #[test] + fn lock_removal_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, u64::MAX, WithdrawReasons::all()); + Balances::remove_lock(ID_1, &1); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); + } + + #[test] + fn lock_replacement_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, u64::MAX, WithdrawReasons::all()); + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); + } + + #[test] + fn double_locking_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + Balances::set_lock(ID_2, &1, 5, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); + } + + #[test] + fn combination_locking_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, u64::MAX, WithdrawReasons::empty()); + Balances::set_lock(ID_2, &1, 0, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); + } + + #[test] + fn lock_value_extension_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + Balances::extend_lock(ID_1, &1, 2, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + Balances::extend_lock(ID_1, &1, 8, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 3, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + }); + } + + #[test] + fn lock_reasons_should_work() { + <$ext_builder>::default() + .existential_deposit(1) + .monied(true) + .build() + .execute_with(|| { + pallet_transaction_payment::NextFeeMultiplier::<$test>::put( + Multiplier::saturating_from_integer(1) + ); + Balances::set_lock(ID_1, &1, 10, WithdrawReasons::RESERVE); + assert_noop!( + >::transfer(&1, &2, 1, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + assert_noop!( + >::reserve(&1, 1), + Error::<$test, _>::LiquidityRestrictions, + ); + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(1), + &1, + CALL, + &info_from_weight(Weight::from_ref_time(1)), + 1, + ).is_err()); + assert_ok!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(0), + &1, + CALL, + &info_from_weight(Weight::from_ref_time(1)), + 1, + )); + + Balances::set_lock(ID_1, &1, 10, WithdrawReasons::TRANSACTION_PAYMENT); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + assert_ok!(>::reserve(&1, 1)); + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(1), + &1, + CALL, + &info_from_weight(Weight::from_ref_time(1)), + 1, + ).is_err()); + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(0), + &1, + CALL, + &info_from_weight(Weight::from_ref_time(1)), + 1, + ).is_err()); + }); + } + + #[test] + fn lock_block_number_extension_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, 10, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + System::set_block_number(2); + Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 3, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + }); + } + + #[test] + fn lock_reasons_extension_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, 10, WithdrawReasons::TRANSFER); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::empty()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::RESERVE); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + }); + } + + #[test] + fn default_indexing_on_new_accounts_should_not_work2() { + <$ext_builder>::default() + .existential_deposit(10) + .monied(true) + .build() + .execute_with(|| { + // account 5 should not exist + // ext_deposit is 10, value is 9, not satisfies for ext_deposit + assert_noop!( + Balances::transfer(Some(1).into(), 5, 9), + Error::<$test, _>::ExistentialDeposit, + ); + assert_eq!(Balances::free_balance(1), 100); + }); + } + + #[test] + fn reserved_balance_should_prevent_reclaim_count() { + <$ext_builder>::default() + .existential_deposit(256 * 1) + .monied(true) + .build() + .execute_with(|| { + System::inc_account_nonce(&2); + assert_eq!(Balances::total_balance(&2), 256 * 20); + + assert_ok!(Balances::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved + assert_eq!(Balances::free_balance(2), 255); // "free" account deleted." + assert_eq!(Balances::total_balance(&2), 256 * 20); // reserve still exists. + assert_eq!(System::account_nonce(&2), 1); + + // account 4 tries to take index 1 for account 5. + assert_ok!(Balances::transfer(Some(4).into(), 5, 256 * 1 + 0x69)); + assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69); + + assert!(Balances::slash(&2, 256 * 19 + 2).1.is_zero()); // account 2 gets slashed + // "reserve" account reduced to 255 (below ED) so account deleted + assert_eq!(Balances::total_balance(&2), 0); + assert_eq!(System::account_nonce(&2), 0); // nonce zero + + // account 4 tries to take index 1 again for account 6. + assert_ok!(Balances::transfer(Some(4).into(), 6, 256 * 1 + 0x69)); + assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69); + }); + } + + #[test] + fn reward_should_work() { + <$ext_builder>::default().monied(true).build().execute_with(|| { + assert_eq!(Balances::total_balance(&1), 10); + assert_ok!(Balances::deposit_into_existing(&1, 10).map(drop)); + System::assert_last_event(RuntimeEvent::Balances(crate::Event::Deposit { who: 1, amount: 10 })); + assert_eq!(Balances::total_balance(&1), 20); + assert_eq!(>::get(), 120); + }); + } + + #[test] + fn dust_account_removal_should_work() { + <$ext_builder>::default() + .existential_deposit(100) + .monied(true) + .build() + .execute_with(|| { + System::inc_account_nonce(&2); + assert_eq!(System::account_nonce(&2), 1); + assert_eq!(Balances::total_balance(&2), 2000); + // index 1 (account 2) becomes zombie + assert_ok!(Balances::transfer(Some(2).into(), 5, 1901)); + assert_eq!(Balances::total_balance(&2), 0); + assert_eq!(Balances::total_balance(&5), 1901); + assert_eq!(System::account_nonce(&2), 0); + }); + } + + #[test] + fn balance_works() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 42); + System::assert_has_event(RuntimeEvent::Balances(crate::Event::Deposit { who: 1, amount: 42 })); + assert_eq!(Balances::free_balance(1), 42); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::free_balance(2), 0); + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::total_balance(&2), 0); + }); + } + + #[test] + fn balance_transfer_works() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::transfer(Some(1).into(), 2, 69)); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::total_balance(&2), 69); + }); + } + + #[test] + fn force_transfer_works() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_noop!( + Balances::force_transfer(Some(2).into(), 1, 2, 69), + BadOrigin, + ); + assert_ok!(Balances::force_transfer(RawOrigin::Root.into(), 1, 2, 69)); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::total_balance(&2), 69); + }); + } + + #[test] + fn reserving_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(1), 111); + assert_eq!(Balances::reserved_balance(1), 0); + + assert_ok!(Balances::reserve(&1, 69)); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(1), 42); + assert_eq!(Balances::reserved_balance(1), 69); + }); + } + + #[test] + fn balance_transfer_when_reserved_should_not_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert_noop!( + Balances::transfer(Some(1).into(), 2, 69), + Error::<$test, _>::InsufficientBalance, + ); + }); + } + + #[test] + fn deducting_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert_eq!(Balances::free_balance(1), 42); + }); + } + + #[test] + fn refunding_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 42); + assert_ok!(Balances::mutate_account(&1, |a| a.reserved = 69)); + Balances::unreserve(&1, 69); + assert_eq!(Balances::free_balance(1), 111); + assert_eq!(Balances::reserved_balance(1), 0); + }); + } + + #[test] + fn slashing_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert!(Balances::slash(&1, 69).1.is_zero()); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(1), 42); + assert_eq!(>::get(), 42); + }); + } + + #[test] + fn withdrawing_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&2, 111); + let _ = Balances::withdraw( + &2, 11, WithdrawReasons::TRANSFER, ExistenceRequirement::KeepAlive + ); + System::assert_last_event(RuntimeEvent::Balances(crate::Event::Withdraw { who: 2, amount: 11 })); + assert_eq!(Balances::free_balance(2), 100); + assert_eq!(>::get(), 100); + }); + } + + #[test] + fn slashing_incomplete_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 42); + assert_ok!(Balances::reserve(&1, 21)); + assert_eq!(Balances::slash(&1, 69).1, 27); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(>::get(), 0); + }); + } + + #[test] + fn unreserving_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 111)); + Balances::unreserve(&1, 42); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::free_balance(1), 42); + }); + } + + #[test] + fn slashing_reserved_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 111)); + assert_eq!(Balances::slash_reserved(&1, 42).1, 0); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(>::get(), 69); + }); + } + + #[test] + fn slashing_incomplete_reserved_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 42)); + assert_eq!(Balances::slash_reserved(&1, 69).1, 27); + assert_eq!(Balances::free_balance(1), 69); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(>::get(), 69); + }); + } + + #[test] + fn repatriating_reserved_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 1); + assert_ok!(Balances::reserve(&1, 110)); + assert_ok!(Balances::repatriate_reserved(&1, &2, 41, Status::Free), 0); + System::assert_last_event( + RuntimeEvent::Balances(crate::Event::ReserveRepatriated { from: 1, to: 2, amount: 41, destination_status: Status::Free }) + ); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::free_balance(2), 42); + }); + } + + #[test] + fn transferring_reserved_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 1); + assert_ok!(Balances::reserve(&1, 110)); + assert_ok!(Balances::repatriate_reserved(&1, &2, 41, Status::Reserved), 0); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(2), 41); + assert_eq!(Balances::free_balance(2), 1); + }); + } + + #[test] + fn transferring_reserved_balance_to_yourself_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 110); + assert_ok!(Balances::reserve(&1, 50)); + assert_ok!(Balances::repatriate_reserved(&1, &1, 50, Status::Free), 0); + assert_eq!(Balances::free_balance(1), 110); + assert_eq!(Balances::reserved_balance(1), 0); + + assert_ok!(Balances::reserve(&1, 50)); + assert_ok!(Balances::repatriate_reserved(&1, &1, 60, Status::Free), 10); + assert_eq!(Balances::free_balance(1), 110); + assert_eq!(Balances::reserved_balance(1), 0); + }); + } + + #[test] + fn transferring_reserved_balance_to_nonexistent_should_fail() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 111)); + assert_noop!(Balances::repatriate_reserved(&1, &2, 42, Status::Free), Error::<$test, _>::DeadAccount); + }); + } + + #[test] + fn transferring_incomplete_reserved_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 1); + assert_ok!(Balances::reserve(&1, 41)); + assert_ok!(Balances::repatriate_reserved(&1, &2, 69, Status::Free), 28); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::free_balance(1), 69); + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::free_balance(2), 42); + }); + } + + #[test] + fn transferring_too_high_value_should_not_panic() { + <$ext_builder>::default().build().execute_with(|| { + Balances::make_free_balance_be(&1, u64::MAX); + Balances::make_free_balance_be(&2, 1); + + assert_err!( + Balances::transfer(Some(1).into(), 2, u64::MAX), + ArithmeticError::Overflow, + ); + + assert_eq!(Balances::free_balance(1), u64::MAX); + assert_eq!(Balances::free_balance(2), 1); + }); + } + + #[test] + fn account_create_on_free_too_low_with_other() { + <$ext_builder>::default().existential_deposit(100).build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 100); + assert_eq!(>::get(), 100); + + // No-op. + let _ = Balances::deposit_creating(&2, 50); + assert_eq!(Balances::free_balance(2), 0); + assert_eq!(>::get(), 100); + }) + } + + #[test] + fn account_create_on_free_too_low() { + <$ext_builder>::default().existential_deposit(100).build().execute_with(|| { + // No-op. + let _ = Balances::deposit_creating(&2, 50); + assert_eq!(Balances::free_balance(2), 0); + assert_eq!(>::get(), 0); + }) + } + + #[test] + fn account_removal_on_free_too_low() { + <$ext_builder>::default().existential_deposit(100).build().execute_with(|| { + assert_eq!(>::get(), 0); + + // Setup two accounts with free balance above the existential threshold. + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 110); + + assert_eq!(Balances::free_balance(1), 110); + assert_eq!(Balances::free_balance(2), 110); + assert_eq!(>::get(), 220); + + // Transfer funds from account 1 of such amount that after this transfer + // the balance of account 1 will be below the existential threshold. + // This should lead to the removal of all balance of this account. + assert_ok!(Balances::transfer(Some(1).into(), 2, 20)); + + // Verify free balance removal of account 1. + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::free_balance(2), 130); + + // Verify that TotalIssuance tracks balance removal when free balance is too low. + assert_eq!(>::get(), 130); + }); + } + + #[test] + fn burn_must_work() { + <$ext_builder>::default().monied(true).build().execute_with(|| { + let init_total_issuance = Balances::total_issuance(); + let imbalance = Balances::burn(10); + assert_eq!(Balances::total_issuance(), init_total_issuance - 10); + drop(imbalance); + assert_eq!(Balances::total_issuance(), init_total_issuance); + }); + } + + #[test] + fn transfer_keep_alive_works() { + <$ext_builder>::default().existential_deposit(1).build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 100); + assert_noop!( + Balances::transfer_keep_alive(Some(1).into(), 2, 100), + Error::<$test, _>::KeepAlive + ); + assert_eq!(Balances::total_balance(&1), 100); + assert_eq!(Balances::total_balance(&2), 0); + }); + } + + #[test] + #[should_panic = "the balance of any account should always be at least the existential deposit."] + fn cannot_set_genesis_value_below_ed() { + ($existential_deposit).with(|v| *v.borrow_mut() = 11); + let mut t = frame_system::GenesisConfig::default().build_storage::<$test>().unwrap(); + let _ = pallet_balances::GenesisConfig::<$test> { + balances: vec![(1, 10)], + }.assimilate_storage(&mut t).unwrap(); + } + + #[test] + #[should_panic = "duplicate balances in genesis."] + fn cannot_set_genesis_value_twice() { + let mut t = frame_system::GenesisConfig::default().build_storage::<$test>().unwrap(); + let _ = pallet_balances::GenesisConfig::<$test> { + balances: vec![(1, 10), (2, 20), (1, 15)], + }.assimilate_storage(&mut t).unwrap(); + } + + #[test] + fn dust_moves_between_free_and_reserved() { + <$ext_builder>::default() + .existential_deposit(100) + .build() + .execute_with(|| { + // Set balance to free and reserved at the existential deposit + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 100, 0)); + // Check balance + assert_eq!(Balances::free_balance(1), 100); + assert_eq!(Balances::reserved_balance(1), 0); + + // Reserve some free balance + assert_ok!(Balances::reserve(&1, 50)); + // Check balance, the account should be ok. + assert_eq!(Balances::free_balance(1), 50); + assert_eq!(Balances::reserved_balance(1), 50); + + // Reserve the rest of the free balance + assert_ok!(Balances::reserve(&1, 50)); + // Check balance, the account should be ok. + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(1), 100); + + // Unreserve everything + Balances::unreserve(&1, 100); + // Check balance, all 100 should move to free_balance + assert_eq!(Balances::free_balance(1), 100); + assert_eq!(Balances::reserved_balance(1), 0); + }); + } + + #[test] + fn account_deleted_when_just_dust() { + <$ext_builder>::default() + .existential_deposit(100) + .build() + .execute_with(|| { + // Set balance to free and reserved at the existential deposit + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 50, 50)); + // Check balance + assert_eq!(Balances::free_balance(1), 50); + assert_eq!(Balances::reserved_balance(1), 50); + + // Reserve some free balance + let res = Balances::slash(&1, 1); + assert_eq!(res, (NegativeImbalance::new(1), 0)); + + // The account should be dead. + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(1), 0); + }); + } + + #[test] + fn emit_events_with_reserve_and_unreserve() { + <$ext_builder>::default() + .build() + .execute_with(|| { + let _ = Balances::deposit_creating(&1, 100); + + System::set_block_number(2); + assert_ok!(Balances::reserve(&1, 10)); + + System::assert_last_event(RuntimeEvent::Balances(crate::Event::Reserved { who: 1, amount: 10 })); + + System::set_block_number(3); + assert!(Balances::unreserve(&1, 5).is_zero()); + + System::assert_last_event(RuntimeEvent::Balances(crate::Event::Unreserved { who: 1, amount: 5 })); + + System::set_block_number(4); + assert_eq!(Balances::unreserve(&1, 6), 1); + + // should only unreserve 5 + System::assert_last_event(RuntimeEvent::Balances(crate::Event::Unreserved { who: 1, amount: 5 })); + }); + } + + #[test] + fn emit_events_with_existential_deposit() { + <$ext_builder>::default() + .existential_deposit(100) + .build() + .execute_with(|| { + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 100, 0)); + + assert_eq!( + events(), + [ + RuntimeEvent::System(system::Event::NewAccount { account: 1 }), + RuntimeEvent::Balances(crate::Event::Endowed { account: 1, free_balance: 100 }), + RuntimeEvent::Balances(crate::Event::BalanceSet { who: 1, free: 100, reserved: 0 }), + ] + ); + + let res = Balances::slash(&1, 1); + assert_eq!(res, (NegativeImbalance::new(1), 0)); + + assert_eq!( + events(), + [ + RuntimeEvent::System(system::Event::KilledAccount { account: 1 }), + RuntimeEvent::Balances(crate::Event::DustLost { account: 1, amount: 99 }), + RuntimeEvent::Balances(crate::Event::Slashed { who: 1, amount: 1 }), + ] + ); + }); + } + + #[test] + fn emit_events_with_no_existential_deposit_suicide() { + <$ext_builder>::default() + .existential_deposit(1) + .build() + .execute_with(|| { + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 100, 0)); + + assert_eq!( + events(), + [ + RuntimeEvent::System(system::Event::NewAccount { account: 1 }), + RuntimeEvent::Balances(crate::Event::Endowed { account: 1, free_balance: 100 }), + RuntimeEvent::Balances(crate::Event::BalanceSet { who: 1, free: 100, reserved: 0 }), + ] + ); + + let res = Balances::slash(&1, 100); + assert_eq!(res, (NegativeImbalance::new(100), 0)); + + assert_eq!( + events(), + [ + RuntimeEvent::System(system::Event::KilledAccount { account: 1 }), + RuntimeEvent::Balances(crate::Event::Slashed { who: 1, amount: 100 }), + ] + ); + }); + } + + #[test] + fn slash_loop_works() { + <$ext_builder>::default() + .existential_deposit(100) + .build() + .execute_with(|| { + /* User has no reference counter, so they can die in these scenarios */ + // SCENARIO: Slash would not kill account. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 0)); + // Slashed completed in full + assert_eq!(Balances::slash(&1, 900), (NegativeImbalance::new(900), 0)); + // Account is still alive + assert!(System::account_exists(&1)); + System::assert_last_event(RuntimeEvent::Balances(crate::Event::Slashed { who: 1, amount: 900 })); + + // SCENARIO: Slash will kill account because not enough balance left. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 0)); + // Slashed completed in full + assert_eq!(Balances::slash(&1, 950), (NegativeImbalance::new(950), 0)); + // Account is killed + assert!(!System::account_exists(&1)); + + // SCENARIO: Over-slash will kill account, and report missing slash amount. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 0)); + // Slashed full free_balance, and reports 300 not slashed + assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(1000), 300)); + // Account is dead + assert!(!System::account_exists(&1)); + + // SCENARIO: Over-slash can take from reserved, but keep alive. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 400)); + // Slashed full free_balance and 300 of reserved balance + assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(1300), 0)); + // Account is still alive + assert!(System::account_exists(&1)); + + // SCENARIO: Over-slash can take from reserved, and kill. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 350)); + // Slashed full free_balance and 300 of reserved balance + assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(1300), 0)); + // Account is dead because 50 reserved balance is not enough to keep alive + assert!(!System::account_exists(&1)); + + // SCENARIO: Over-slash can take as much as possible from reserved, kill, and report missing amount. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 250)); + // Slashed full free_balance and 300 of reserved balance + assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(1250), 50)); + // Account is super dead + assert!(!System::account_exists(&1)); + + /* User will now have a reference counter on them, keeping them alive in these scenarios */ + // SCENARIO: Slash would not kill account. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 0)); + assert_ok!(System::inc_consumers(&1)); // <-- Reference counter added here is enough for all tests + // Slashed completed in full + assert_eq!(Balances::slash(&1, 900), (NegativeImbalance::new(900), 0)); + // Account is still alive + assert!(System::account_exists(&1)); + + // SCENARIO: Slash will take as much as possible without killing account. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 0)); + // Slashed completed in full + assert_eq!(Balances::slash(&1, 950), (NegativeImbalance::new(900), 50)); + // Account is still alive + assert!(System::account_exists(&1)); + + // SCENARIO: Over-slash will not kill account, and report missing slash amount. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 0)); + // Slashed full free_balance minus ED, and reports 400 not slashed + assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(900), 400)); + // Account is still alive + assert!(System::account_exists(&1)); + + // SCENARIO: Over-slash can take from reserved, but keep alive. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 400)); + // Slashed full free_balance and 300 of reserved balance + assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(1300), 0)); + // Account is still alive + assert!(System::account_exists(&1)); + + // SCENARIO: Over-slash can take from reserved, but keep alive. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 350)); + // Slashed full free_balance and 250 of reserved balance to leave ED + assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(1250), 50)); + // Account is still alive + assert!(System::account_exists(&1)); + + // SCENARIO: Over-slash can take as much as possible from reserved and report missing amount. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 1_000, 250)); + // Slashed full free_balance and 300 of reserved balance + assert_eq!(Balances::slash(&1, 1_300), (NegativeImbalance::new(1150), 150)); + // Account is still alive + assert!(System::account_exists(&1)); + + // Slash on non-existent account is okay. + assert_eq!(Balances::slash(&12345, 1_300), (NegativeImbalance::new(0), 1300)); + }); + } + + #[test] + fn slash_reserved_loop_works() { + <$ext_builder>::default() + .existential_deposit(100) + .build() + .execute_with(|| { + /* User has no reference counter, so they can die in these scenarios */ + // SCENARIO: Slash would not kill account. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 50, 1_000)); + // Slashed completed in full + assert_eq!(Balances::slash_reserved(&1, 900), (NegativeImbalance::new(900), 0)); + // Account is still alive + assert!(System::account_exists(&1)); + + // SCENARIO: Slash would kill account. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 50, 1_000)); + // Slashed completed in full + assert_eq!(Balances::slash_reserved(&1, 1_000), (NegativeImbalance::new(1_000), 0)); + // Account is dead + assert!(!System::account_exists(&1)); + + // SCENARIO: Over-slash would kill account, and reports left over slash. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 50, 1_000)); + // Slashed completed in full + assert_eq!(Balances::slash_reserved(&1, 1_300), (NegativeImbalance::new(1_000), 300)); + // Account is dead + assert!(!System::account_exists(&1)); + + // SCENARIO: Over-slash does not take from free balance. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 300, 1_000)); + // Slashed completed in full + assert_eq!(Balances::slash_reserved(&1, 1_300), (NegativeImbalance::new(1_000), 300)); + // Account is alive because of free balance + assert!(System::account_exists(&1)); + + /* User has a reference counter, so they cannot die */ + // SCENARIO: Slash would not kill account. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 50, 1_000)); + assert_ok!(System::inc_consumers(&1)); // <-- Reference counter added here is enough for all tests + // Slashed completed in full + assert_eq!(Balances::slash_reserved(&1, 900), (NegativeImbalance::new(900), 0)); + // Account is still alive + assert!(System::account_exists(&1)); + + // SCENARIO: Slash as much as possible without killing. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 50, 1_000)); + // Slashed as much as possible + assert_eq!(Balances::slash_reserved(&1, 1_000), (NegativeImbalance::new(950), 50)); + // Account is still alive + assert!(System::account_exists(&1)); + + // SCENARIO: Over-slash reports correctly, where reserved is needed to keep alive. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 50, 1_000)); + // Slashed as much as possible + assert_eq!(Balances::slash_reserved(&1, 1_300), (NegativeImbalance::new(950), 350)); + // Account is still alive + assert!(System::account_exists(&1)); + + // SCENARIO: Over-slash reports correctly, where full reserved is removed. + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 200, 1_000)); + // Slashed as much as possible + assert_eq!(Balances::slash_reserved(&1, 1_300), (NegativeImbalance::new(1_000), 300)); + // Account is still alive + assert!(System::account_exists(&1)); + + // Slash on non-existent account is okay. + assert_eq!(Balances::slash_reserved(&12345, 1_300), (NegativeImbalance::new(0), 1300)); + }); + } + + #[test] + fn operations_on_dead_account_should_not_change_state() { + // These functions all use `mutate_account` which may introduce a storage change when + // the account never existed to begin with, and shouldn't exist in the end. + <$ext_builder>::default() + .existential_deposit(0) + .build() + .execute_with(|| { + assert!(!frame_system::Account::::contains_key(&1337)); + + // Unreserve + assert_storage_noop!(assert_eq!(Balances::unreserve(&1337, 42), 42)); + // Reserve + assert_noop!(Balances::reserve(&1337, 42), Error::::InsufficientBalance); + // Slash Reserve + assert_storage_noop!(assert_eq!(Balances::slash_reserved(&1337, 42).1, 42)); + // Repatriate Reserve + assert_noop!(Balances::repatriate_reserved(&1337, &1338, 42, Status::Free), Error::::DeadAccount); + // Slash + assert_storage_noop!(assert_eq!(Balances::slash(&1337, 42).1, 42)); + }); + } + + #[test] + fn transfer_keep_alive_all_free_succeed() { + <$ext_builder>::default() + .existential_deposit(100) + .build() + .execute_with(|| { + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 100, 100)); + assert_ok!(Balances::transfer_keep_alive(Some(1).into(), 2, 100)); + assert_eq!(Balances::total_balance(&1), 100); + assert_eq!(Balances::total_balance(&2), 100); + }); + } + + #[test] + fn transfer_all_works() { + <$ext_builder>::default() + .existential_deposit(100) + .build() + .execute_with(|| { + // setup + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 200, 0)); + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 2, 0, 0)); + // transfer all and allow death + assert_ok!(Balances::transfer_all(Some(1).into(), 2, false)); + assert_eq!(Balances::total_balance(&1), 0); + assert_eq!(Balances::total_balance(&2), 200); + + // setup + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 200, 0)); + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 2, 0, 0)); + // transfer all and keep alive + assert_ok!(Balances::transfer_all(Some(1).into(), 2, true)); + assert_eq!(Balances::total_balance(&1), 100); + assert_eq!(Balances::total_balance(&2), 100); + + // setup + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 200, 10)); + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 2, 0, 0)); + // transfer all and allow death w/ reserved + assert_ok!(Balances::transfer_all(Some(1).into(), 2, false)); + assert_eq!(Balances::total_balance(&1), 0); + assert_eq!(Balances::total_balance(&2), 200); + + // setup + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1, 200, 10)); + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 2, 0, 0)); + // transfer all and keep alive w/ reserved + assert_ok!(Balances::transfer_all(Some(1).into(), 2, true)); + assert_eq!(Balances::total_balance(&1), 100); + assert_eq!(Balances::total_balance(&2), 110); + }); + } + + #[test] + fn named_reserve_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + + let id_1 = [1u8; 8]; + let id_2 = [2u8; 8]; + let id_3 = [3u8; 8]; + + // reserve + + assert_noop!(Balances::reserve_named(&id_1, &1, 112), Error::::InsufficientBalance); + + assert_ok!(Balances::reserve_named(&id_1, &1, 12)); + + assert_eq!(Balances::reserved_balance(1), 12); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 12); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 0); + + assert_ok!(Balances::reserve_named(&id_1, &1, 2)); + + assert_eq!(Balances::reserved_balance(1), 14); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 14); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 0); + + assert_ok!(Balances::reserve_named(&id_2, &1, 23)); + + assert_eq!(Balances::reserved_balance(1), 37); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 14); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 23); + + assert_ok!(Balances::reserve(&1, 34)); + + assert_eq!(Balances::reserved_balance(1), 71); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 14); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 23); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(1), 40); + + assert_noop!(Balances::reserve_named(&id_3, &1, 2), Error::::TooManyReserves); + + // unreserve + + assert_eq!(Balances::unreserve_named(&id_1, &1, 10), 0); + + assert_eq!(Balances::reserved_balance(1), 61); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 4); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 23); + + assert_eq!(Balances::unreserve_named(&id_1, &1, 5), 1); + + assert_eq!(Balances::reserved_balance(1), 57); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 0); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 23); + + assert_eq!(Balances::unreserve_named(&id_2, &1, 3), 0); + + assert_eq!(Balances::reserved_balance(1), 54); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 0); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 20); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(1), 57); + + // slash_reserved_named + + assert_ok!(Balances::reserve_named(&id_1, &1, 10)); + + assert_eq!(Balances::slash_reserved_named(&id_1, &1, 25).1, 15); + + assert_eq!(Balances::reserved_balance(1), 54); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 0); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 20); + assert_eq!(Balances::total_balance(&1), 101); + + assert_eq!(Balances::slash_reserved_named(&id_2, &1, 5).1, 0); + + assert_eq!(Balances::reserved_balance(1), 49); + assert_eq!(Balances::reserved_balance_named(&id_1, &1), 0); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 15); + assert_eq!(Balances::total_balance(&1), 96); + + // repatriate_reserved_named + + let _ = Balances::deposit_creating(&2, 100); + + assert_eq!(Balances::repatriate_reserved_named(&id_2, &1, &2, 10, Status::Reserved).unwrap(), 0); + + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 5); + assert_eq!(Balances::reserved_balance_named(&id_2, &2), 10); + assert_eq!(Balances::reserved_balance(&2), 10); + + assert_eq!(Balances::repatriate_reserved_named(&id_2, &2, &1, 11, Status::Reserved).unwrap(), 1); + + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 15); + assert_eq!(Balances::reserved_balance_named(&id_2, &2), 0); + assert_eq!(Balances::reserved_balance(&2), 0); + + assert_eq!(Balances::repatriate_reserved_named(&id_2, &1, &2, 10, Status::Free).unwrap(), 0); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 5); + assert_eq!(Balances::reserved_balance_named(&id_2, &2), 0); + assert_eq!(Balances::free_balance(&2), 110); + + // repatriate_reserved_named to self + + assert_eq!(Balances::repatriate_reserved_named(&id_2, &1, &1, 10, Status::Reserved).unwrap(), 5); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 5); + + assert_eq!(Balances::free_balance(&1), 47); + + assert_eq!(Balances::repatriate_reserved_named(&id_2, &1, &1, 15, Status::Free).unwrap(), 10); + assert_eq!(Balances::reserved_balance_named(&id_2, &1), 0); + + assert_eq!(Balances::free_balance(&1), 52); + }); + } + + #[test] + fn reserved_named_to_yourself_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 110); + + let id = [1u8; 8]; + + assert_ok!(Balances::reserve_named(&id, &1, 50)); + assert_ok!(Balances::repatriate_reserved_named(&id, &1, &1, 50, Status::Free), 0); + assert_eq!(Balances::free_balance(1), 110); + assert_eq!(Balances::reserved_balance_named(&id, &1), 0); + + assert_ok!(Balances::reserve_named(&id, &1, 50)); + assert_ok!(Balances::repatriate_reserved_named(&id, &1, &1, 60, Status::Free), 10); + assert_eq!(Balances::free_balance(1), 110); + assert_eq!(Balances::reserved_balance_named(&id, &1), 0); + }); + } + + #[test] + fn ensure_reserved_named_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + + let id = [1u8; 8]; + + assert_ok!(Balances::ensure_reserved_named(&id, &1, 15)); + assert_eq!(Balances::reserved_balance_named(&id, &1), 15); + + assert_ok!(Balances::ensure_reserved_named(&id, &1, 10)); + assert_eq!(Balances::reserved_balance_named(&id, &1), 10); + + assert_ok!(Balances::ensure_reserved_named(&id, &1, 20)); + assert_eq!(Balances::reserved_balance_named(&id, &1), 20); + }); + } + + #[test] + fn unreserve_all_named_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + + let id = [1u8; 8]; + + assert_ok!(Balances::reserve_named(&id, &1, 15)); + + assert_eq!(Balances::unreserve_all_named(&id, &1), 15); + assert_eq!(Balances::reserved_balance_named(&id, &1), 0); + assert_eq!(Balances::free_balance(&1), 111); + + assert_eq!(Balances::unreserve_all_named(&id, &1), 0); + }); + } + + #[test] + fn slash_all_reserved_named_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + + let id = [1u8; 8]; + + assert_ok!(Balances::reserve_named(&id, &1, 15)); + + assert_eq!(Balances::slash_all_reserved_named(&id, &1).peek(), 15); + assert_eq!(Balances::reserved_balance_named(&id, &1), 0); + assert_eq!(Balances::free_balance(&1), 96); + + assert_eq!(Balances::slash_all_reserved_named(&id, &1).peek(), 0); + }); + } + + #[test] + fn repatriate_all_reserved_named_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + let _ = Balances::deposit_creating(&2, 10); + let _ = Balances::deposit_creating(&3, 10); + + let id = [1u8; 8]; + + assert_ok!(Balances::reserve_named(&id, &1, 15)); + + assert_ok!(Balances::repatriate_all_reserved_named(&id, &1, &2, Status::Reserved)); + assert_eq!(Balances::reserved_balance_named(&id, &1), 0); + assert_eq!(Balances::reserved_balance_named(&id, &2), 15); + + assert_ok!(Balances::repatriate_all_reserved_named(&id, &2, &3, Status::Free)); + assert_eq!(Balances::reserved_balance_named(&id, &2), 0); + assert_eq!(Balances::free_balance(&3), 25); + }); + } + + #[test] + fn set_balance_handles_killing_account() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(frame_system::Pallet::::inc_consumers(&1)); + assert_noop!( + Balances::set_balance(RuntimeOrigin::root(), 1, 0, 0), + DispatchError::ConsumerRemaining, + ); + }); + } + + #[test] + fn set_balance_handles_total_issuance() { + <$ext_builder>::default().build().execute_with(|| { + let old_total_issuance = Balances::total_issuance(); + assert_ok!(Balances::set_balance(RuntimeOrigin::root(), 1337, 69, 42)); + assert_eq!(Balances::total_issuance(), old_total_issuance + 69 + 42); + assert_eq!(Balances::total_balance(&1337), 69 + 42); + assert_eq!(Balances::free_balance(&1337), 69); + assert_eq!(Balances::reserved_balance(&1337), 42); + }); + } + + #[test] + fn fungible_unbalanced_trait_set_balance_works() { + <$ext_builder>::default().build().execute_with(|| { + assert_eq!(>::balance(&1337), 0); + assert_ok!(>::set_balance(&1337, 100)); + assert_eq!(>::balance(&1337), 100); + + assert_ok!(Balances::reserve(&1337, 60)); + assert_eq!(Balances::free_balance(1337) , 40); + assert_eq!(Balances::reserved_balance(1337), 60); + + assert_noop!(>::set_balance(&1337, 0), ArithmeticError::Underflow); + + assert_ok!(>::set_balance(&1337, 60)); + assert_eq!(Balances::free_balance(1337) , 0); + assert_eq!(Balances::reserved_balance(1337), 60); + }); + } + + #[test] + fn fungible_unbalanced_trait_set_total_issuance_works() { + <$ext_builder>::default().build().execute_with(|| { + assert_eq!(>::total_issuance(), 0); + >::set_total_issuance(100); + assert_eq!(>::total_issuance(), 100); + }); + } + + #[test] + fn fungible_unbalanced_trait_decrease_balance_simple_works() { + <$ext_builder>::default().build().execute_with(|| { + // An Account that starts at 100 + assert_ok!(>::set_balance(&1337, 100)); + // and reserves 50 + assert_ok!(Balances::reserve(&1337, 50)); + // and is decreased by 20 + assert_ok!(>::decrease_balance(&1337, 20)); + // should end up at 80. + assert_eq!(>::balance(&1337), 80); + }); + } + + #[test] + fn fungible_unbalanced_trait_decrease_balance_works() { + <$ext_builder>::default().build().execute_with(|| { + assert_ok!(>::set_balance(&1337, 100)); + assert_eq!(>::balance(&1337), 100); + + assert_noop!( + >::decrease_balance(&1337, 101), + TokenError::NoFunds + ); + assert_eq!( + >::decrease_balance(&1337, 100), + Ok(100) + ); + assert_eq!(>::balance(&1337), 0); + + // free: 40, reserved: 60 + assert_ok!(>::set_balance(&1337, 100)); + assert_ok!(Balances::reserve(&1337, 60)); + assert_eq!(Balances::free_balance(1337) , 40); + assert_eq!(Balances::reserved_balance(1337), 60); + assert_noop!( + >::decrease_balance(&1337, 41), + TokenError::NoFunds + ); + assert_eq!( + >::decrease_balance(&1337, 40), + Ok(40) + ); + assert_eq!(>::balance(&1337), 60); + assert_eq!(Balances::free_balance(1337), 0); + assert_eq!(Balances::reserved_balance(1337), 60); + }); + } + + #[test] + fn fungible_unbalanced_trait_decrease_balance_at_most_works() { + <$ext_builder>::default().build().execute_with(|| { + assert_ok!(>::set_balance(&1337, 100)); + assert_eq!(>::balance(&1337), 100); + + assert_eq!( + >::decrease_balance_at_most(&1337, 101), + 100 + ); + assert_eq!(>::balance(&1337), 0); + + assert_ok!(>::set_balance(&1337, 100)); + assert_eq!( + >::decrease_balance_at_most(&1337, 100), + 100 + ); + assert_eq!(>::balance(&1337), 0); + + // free: 40, reserved: 60 + assert_ok!(>::set_balance(&1337, 100)); + assert_ok!(Balances::reserve(&1337, 60)); + assert_eq!(Balances::free_balance(1337) , 40); + assert_eq!(Balances::reserved_balance(1337), 60); + assert_eq!( + >::decrease_balance_at_most(&1337, 0), + 0 + ); + assert_eq!(Balances::free_balance(1337) , 40); + assert_eq!(Balances::reserved_balance(1337), 60); + assert_eq!( + >::decrease_balance_at_most(&1337, 10), + 10 + ); + assert_eq!(Balances::free_balance(1337), 30); + assert_eq!( + >::decrease_balance_at_most(&1337, 200), + 30 + ); + assert_eq!(>::balance(&1337), 60); + assert_eq!(Balances::free_balance(1337), 0); + assert_eq!(Balances::reserved_balance(1337), 60); + }); + } + + #[test] + fn fungible_unbalanced_trait_increase_balance_works() { + <$ext_builder>::default().build().execute_with(|| { + assert_noop!( + >::increase_balance(&1337, 0), + TokenError::BelowMinimum + ); + assert_eq!( + >::increase_balance(&1337, 1), + Ok(1) + ); + assert_noop!( + >::increase_balance(&1337, u64::MAX), + ArithmeticError::Overflow + ); + }); + } + + #[test] + fn fungible_unbalanced_trait_increase_balance_at_most_works() { + <$ext_builder>::default().build().execute_with(|| { + assert_eq!( + >::increase_balance_at_most(&1337, 0), + 0 + ); + assert_eq!( + >::increase_balance_at_most(&1337, 1), + 1 + ); + assert_eq!( + >::increase_balance_at_most(&1337, u64::MAX), + u64::MAX - 1 + ); + }); + } + } +} diff --git a/pallets/balances/src/tests_composite.rs b/pallets/balances/src/tests_composite.rs new file mode 100644 index 000000000..0a37c8cce --- /dev/null +++ b/pallets/balances/src/tests_composite.rs @@ -0,0 +1,155 @@ +// Copyright 2020-2023 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . + +//! Test utilities + +#![cfg(test)] +#![allow(clippy::all)] + +use crate::{self as pallet_balances, decl_tests, Config, Pallet}; +use frame_support::{ + dispatch::DispatchInfo, + parameter_types, + traits::{ConstU32, ConstU64, ConstU8}, + weights::{IdentityFee, Weight}, +}; +use pallet_transaction_payment::CurrencyAdapter; +use sp_core::H256; +use sp_io; +use sp_runtime::{testing::Header, traits::IdentityLookup}; +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + } +); + +parameter_types! { + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max( + frame_support::weights::Weight::from_ref_time(1024).set_proof_size(u64::MAX), + ); + pub static ExistentialDeposit: u64 = 0; +} +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = BlockWeights; + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u64; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = super::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_transaction_payment::Config for Test { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = CurrencyAdapter, ()>; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = IdentityFee; + type LengthToFee = IdentityFee; + type FeeMultiplierUpdate = (); +} + +impl Config for Test { + type Balance = u64; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = frame_system::Pallet; + type MaxLocks = (); + type MaxReserves = ConstU32<2>; + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); + type NativeBarrierType = crate::tests_local::MockNativeBarrier; +} + +pub struct ExtBuilder { + existential_deposit: u64, + monied: bool, +} +impl Default for ExtBuilder { + fn default() -> Self { + Self { + existential_deposit: 1, + monied: false, + } + } +} +impl ExtBuilder { + pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { + self.existential_deposit = existential_deposit; + self + } + pub fn monied(mut self, monied: bool) -> Self { + self.monied = monied; + self + } + pub fn set_associated_consts(&self) { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + } + pub fn build(self) -> sp_io::TestExternalities { + self.set_associated_consts(); + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + pallet_balances::GenesisConfig:: { + balances: if self.monied { + vec![ + (1, 10 * self.existential_deposit), + (2, 20 * self.existential_deposit), + (3, 30 * self.existential_deposit), + (4, 40 * self.existential_deposit), + (12, 10 * self.existential_deposit), + ] + } else { + vec![] + }, + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext + } +} + +decl_tests! { Test, ExtBuilder, EXISTENTIAL_DEPOSIT } diff --git a/pallets/balances/src/tests_local.rs b/pallets/balances/src/tests_local.rs new file mode 100644 index 000000000..d9b0584ab --- /dev/null +++ b/pallets/balances/src/tests_local.rs @@ -0,0 +1,230 @@ +// Copyright 2020-2023 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . + +//! Test utilities + +#![cfg(test)] +#![allow(clippy::all)] + +use crate::{self as pallet_balances, decl_tests, Config, Pallet}; +use frame_support::{ + dispatch::DispatchInfo, + parameter_types, + traits::{ConstU32, ConstU64, ConstU8, StorageMapShim}, + weights::{IdentityFee, Weight}, +}; +use pallet_transaction_payment::CurrencyAdapter; +use sp_core::H256; +use sp_io; +use sp_runtime::{testing::Header, traits::IdentityLookup}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub struct Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + } +); + +parameter_types! { + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max( + frame_support::weights::Weight::from_ref_time(1024).set_proof_size(u64::MAX), + ); + pub static ExistentialDeposit: u64 = 0; +} +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = BlockWeights; + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u64; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_transaction_payment::Config for Test { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = CurrencyAdapter, ()>; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = IdentityFee; + type LengthToFee = IdentityFee; + type FeeMultiplierUpdate = (); +} + +pub struct MockNativeBarrier; +impl orml_traits::native_barrier::NativeBarrier for MockNativeBarrier { + fn update_native_barrier(_account_id: &u64, _amount: u64) {} + fn ensure_limit_not_exceeded( + _account_id: &u64, + _amount: u64, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) + } +} + +impl orml_traits::native_barrier::NativeChecker for MockNativeBarrier { + fn is_native(_currency_id: &u64) -> bool { + true + } +} + +impl Config for Test { + type Balance = u64; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = + StorageMapShim, system::Provider, u64, super::AccountData>; + type MaxLocks = ConstU32<50>; + type MaxReserves = ConstU32<2>; + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); + type NativeBarrierType = MockNativeBarrier; +} + +pub struct ExtBuilder { + existential_deposit: u64, + monied: bool, +} +impl Default for ExtBuilder { + fn default() -> Self { + Self { + existential_deposit: 1, + monied: false, + } + } +} +impl ExtBuilder { + pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { + self.existential_deposit = existential_deposit; + self + } + pub fn monied(mut self, monied: bool) -> Self { + self.monied = monied; + if self.existential_deposit == 0 { + self.existential_deposit = 1; + } + self + } + pub fn set_associated_consts(&self) { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + } + pub fn build(self) -> sp_io::TestExternalities { + self.set_associated_consts(); + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + pallet_balances::GenesisConfig:: { + balances: if self.monied { + vec![ + (1, 10 * self.existential_deposit), + (2, 20 * self.existential_deposit), + (3, 30 * self.existential_deposit), + (4, 40 * self.existential_deposit), + (12, 10 * self.existential_deposit), + ] + } else { + vec![] + }, + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext + } +} + +decl_tests! { Test, ExtBuilder, EXISTENTIAL_DEPOSIT } + +#[test] +fn emit_events_with_no_existential_deposit_suicide_with_dust() { + ::default() + .existential_deposit(2) + .build() + .execute_with(|| { + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 100, 0)); + + assert_eq!( + events(), + [ + RuntimeEvent::System(system::Event::NewAccount { account: 1 }), + RuntimeEvent::Balances(crate::Event::Endowed { + account: 1, + free_balance: 100 + }), + RuntimeEvent::Balances(crate::Event::BalanceSet { + who: 1, + free: 100, + reserved: 0 + }), + ] + ); + + let res = Balances::slash(&1, 98); + assert_eq!(res, (NegativeImbalance::new(98), 0)); + + // no events + assert_eq!( + events(), + [RuntimeEvent::Balances(crate::Event::Slashed { + who: 1, + amount: 98 + })] + ); + + let res = Balances::slash(&1, 1); + assert_eq!(res, (NegativeImbalance::new(1), 0)); + + assert_eq!( + events(), + [ + RuntimeEvent::System(system::Event::KilledAccount { account: 1 }), + RuntimeEvent::Balances(crate::Event::DustLost { + account: 1, + amount: 1 + }), + RuntimeEvent::Balances(crate::Event::Slashed { who: 1, amount: 1 }) + ] + ); + }); +} diff --git a/pallets/balances/src/tests_reentrancy.rs b/pallets/balances/src/tests_reentrancy.rs new file mode 100644 index 000000000..35d4fbadf --- /dev/null +++ b/pallets/balances/src/tests_reentrancy.rs @@ -0,0 +1,278 @@ +// Copyright 2020-2023 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . + +//! Test setup for potential reentracy and lost updates of nested mutations. + +#![cfg(test)] +#![allow(clippy::all)] + +use crate::{self as pallet_balances, Config}; +use frame_support::{ + parameter_types, + traits::{ConstU32, ConstU64, StorageMapShim}, +}; +use sp_core::H256; +use sp_io; +use sp_runtime::{testing::Header, traits::IdentityLookup}; + +use crate::*; +use frame_support::{ + assert_ok, + traits::{Currency, ReservableCurrency}, +}; +use frame_system::RawOrigin; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + } +); + +parameter_types! { + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max( + frame_support::weights::Weight::from_ref_time(1024).set_proof_size(u64::MAX), + ); + pub static ExistentialDeposit: u64 = 0; +} +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = BlockWeights; + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u64; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +pub struct OnDustRemoval; +impl OnUnbalanced> for OnDustRemoval { + fn on_nonzero_unbalanced(amount: NegativeImbalance) { + assert_ok!(Balances::resolve_into_existing(&1, amount)); + } +} + +impl Config for Test { + type Balance = u64; + type DustRemoval = OnDustRemoval; + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = + StorageMapShim, system::Provider, u64, super::AccountData>; + type MaxLocks = ConstU32<50>; + type MaxReserves = ConstU32<2>; + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); + type NativeBarrierType = crate::tests_local::MockNativeBarrier; +} + +pub struct ExtBuilder { + existential_deposit: u64, +} +impl Default for ExtBuilder { + fn default() -> Self { + Self { + existential_deposit: 1, + } + } +} +impl ExtBuilder { + pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { + self.existential_deposit = existential_deposit; + self + } + + pub fn set_associated_consts(&self) { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + } + + pub fn build(self) -> sp_io::TestExternalities { + self.set_associated_consts(); + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + pallet_balances::GenesisConfig:: { balances: vec![] } + .assimilate_storage(&mut t) + .unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext + } +} + +#[test] +fn transfer_dust_removal_tst1_should_work() { + ExtBuilder::default() + .existential_deposit(100) + .build() + .execute_with(|| { + // Verification of reentrancy in dust removal + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 1000, 0)); + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 2, 500, 0)); + + // In this transaction, account 2 free balance + // drops below existential balance + // and dust balance is removed from account 2 + assert_ok!(Balances::transfer(RawOrigin::Signed(2).into(), 3, 450)); + + // As expected dust balance is removed. + assert_eq!(Balances::free_balance(&2), 0); + + // As expected beneficiary account 3 + // received the transfered fund. + assert_eq!(Balances::free_balance(&3), 450); + + // Dust balance is deposited to account 1 + // during the process of dust removal. + assert_eq!(Balances::free_balance(&1), 1050); + + // Verify the events + assert_eq!(System::events().len(), 12); + + System::assert_has_event(RuntimeEvent::Balances(crate::Event::Transfer { + from: 2, + to: 3, + amount: 450, + })); + System::assert_has_event(RuntimeEvent::Balances(crate::Event::DustLost { + account: 2, + amount: 50, + })); + System::assert_has_event(RuntimeEvent::Balances(crate::Event::Deposit { + who: 1, + amount: 50, + })); + }); +} + +#[test] +fn transfer_dust_removal_tst2_should_work() { + ExtBuilder::default() + .existential_deposit(100) + .build() + .execute_with(|| { + // Verification of reentrancy in dust removal + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 1000, 0)); + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 2, 500, 0)); + + // In this transaction, account 2 free balance + // drops below existential balance + // and dust balance is removed from account 2 + assert_ok!(Balances::transfer(RawOrigin::Signed(2).into(), 1, 450)); + + // As expected dust balance is removed. + assert_eq!(Balances::free_balance(&2), 0); + + // Dust balance is deposited to account 1 + // during the process of dust removal. + assert_eq!(Balances::free_balance(&1), 1500); + + // Verify the events + assert_eq!(System::events().len(), 10); + + System::assert_has_event(RuntimeEvent::Balances(crate::Event::Transfer { + from: 2, + to: 1, + amount: 450, + })); + System::assert_has_event(RuntimeEvent::Balances(crate::Event::DustLost { + account: 2, + amount: 50, + })); + System::assert_has_event(RuntimeEvent::Balances(crate::Event::Deposit { + who: 1, + amount: 50, + })); + }); +} + +#[test] +fn repatriating_reserved_balance_dust_removal_should_work() { + ExtBuilder::default() + .existential_deposit(100) + .build() + .execute_with(|| { + // Verification of reentrancy in dust removal + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 1000, 0)); + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 2, 500, 0)); + + // Reserve a value on account 2, + // Such that free balance is lower than + // Exestintial deposit. + assert_ok!(Balances::reserve(&2, 450)); + + // Transfer of reserved fund from slashed account 2 to + // beneficiary account 1 + assert_ok!(Balances::repatriate_reserved(&2, &1, 450, Status::Free), 0); + + // Since free balance of account 2 is lower than + // existential deposit, dust amount is + // removed from the account 2 + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::free_balance(2), 0); + + // account 1 is credited with reserved amount + // together with dust balance during dust + // removal. + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::free_balance(1), 1500); + + // Verify the events + assert_eq!(System::events().len(), 11); + + System::assert_has_event(RuntimeEvent::Balances(crate::Event::ReserveRepatriated { + from: 2, + to: 1, + amount: 450, + destination_status: Status::Free, + })); + + System::assert_has_event(RuntimeEvent::Balances(crate::Event::DustLost { + account: 2, + amount: 50, + })); + + System::assert_last_event(RuntimeEvent::Balances(crate::Event::Deposit { + who: 1, + amount: 50, + })); + }); +} diff --git a/pallets/balances/src/weights.rs b/pallets/balances/src/weights.rs new file mode 100644 index 000000000..b5579e3c0 --- /dev/null +++ b/pallets/balances/src/weights.rs @@ -0,0 +1,163 @@ +// Copyright 2020-2023 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . + +//! Autogenerated weights for pallet_balances +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/substrate +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_balances +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./frame/balances/src/weights.rs +// --header=./HEADER-APACHE2 +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_balances. +pub trait WeightInfo { + fn transfer() -> Weight; + fn transfer_keep_alive() -> Weight; + fn set_balance_creating() -> Weight; + fn set_balance_killing() -> Weight; + fn force_transfer() -> Weight; + fn transfer_all() -> Weight; + fn force_unreserve() -> Weight; +} + +/// Weights for pallet_balances using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: System Account (r:1 w:1) + fn transfer() -> Weight { + // Minimum execution time: 48_134 nanoseconds. + Weight::from_ref_time(48_811_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:1 w:1) + fn transfer_keep_alive() -> Weight { + // Minimum execution time: 36_586 nanoseconds. + Weight::from_ref_time(36_966_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:1 w:1) + fn set_balance_creating() -> Weight { + // Minimum execution time: 28_486 nanoseconds. + Weight::from_ref_time(28_940_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:1 w:1) + fn set_balance_killing() -> Weight { + // Minimum execution time: 31_225 nanoseconds. + Weight::from_ref_time(31_946_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:2 w:2) + fn force_transfer() -> Weight { + // Minimum execution time: 47_347 nanoseconds. + Weight::from_ref_time(48_005_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + // Storage: System Account (r:1 w:1) + fn transfer_all() -> Weight { + // Minimum execution time: 41_668 nanoseconds. + Weight::from_ref_time(42_232_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:1 w:1) + fn force_unreserve() -> Weight { + // Minimum execution time: 23_741 nanoseconds. + Weight::from_ref_time(24_073_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: System Account (r:1 w:1) + fn transfer() -> Weight { + // Minimum execution time: 48_134 nanoseconds. + Weight::from_ref_time(48_811_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:1 w:1) + fn transfer_keep_alive() -> Weight { + // Minimum execution time: 36_586 nanoseconds. + Weight::from_ref_time(36_966_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:1 w:1) + fn set_balance_creating() -> Weight { + // Minimum execution time: 28_486 nanoseconds. + Weight::from_ref_time(28_940_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:1 w:1) + fn set_balance_killing() -> Weight { + // Minimum execution time: 31_225 nanoseconds. + Weight::from_ref_time(31_946_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:2 w:2) + fn force_transfer() -> Weight { + // Minimum execution time: 47_347 nanoseconds. + Weight::from_ref_time(48_005_000 as u64) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + // Storage: System Account (r:1 w:1) + fn transfer_all() -> Weight { + // Minimum execution time: 41_668 nanoseconds. + Weight::from_ref_time(42_232_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:1 w:1) + fn force_unreserve() -> Weight { + // Minimum execution time: 23_741 nanoseconds. + Weight::from_ref_time(24_073_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } +} diff --git a/pallets/collator-selection/Cargo.toml b/pallets/collator-selection/Cargo.toml index 9b1fafb47..b6778057e 100644 --- a/pallets/collator-selection/Cargo.toml +++ b/pallets/collator-selection/Cargo.toml @@ -32,8 +32,9 @@ sp-std = { git = 'https://github.com/paritytech/substrate.git', default-features [dev-dependencies] manta-primitives = { path = "../../primitives/manta" } +orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "ghzlatarev/polkadot-v0.9.37" } pallet-aura = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } -pallet-balances = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } +pallet-balances = { path = "../balances" } pallet-timestamp = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } sp-consensus-aura = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } sp-core = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } diff --git a/pallets/collator-selection/src/mock.rs b/pallets/collator-selection/src/mock.rs index 20ab39a8d..45f959407 100644 --- a/pallets/collator-selection/src/mock.rs +++ b/pallets/collator-selection/src/mock.rs @@ -80,6 +80,23 @@ impl frame_system::Config for Test { type MaxConsumers = frame_support::traits::ConstU32<16>; } +pub struct MockNativeBarrier; +impl orml_traits::native_barrier::NativeBarrier for MockNativeBarrier { + fn update_native_barrier(_account_id: &u64, _amount: u64) {} + fn ensure_limit_not_exceeded( + _account_id: &u64, + _amount: u64, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) + } +} + +impl orml_traits::native_barrier::NativeChecker for MockNativeBarrier { + fn is_native(_currency_id: &u64) -> bool { + true + } +} + impl pallet_balances::Config for Test { type Balance = u64; type RuntimeEvent = RuntimeEvent; @@ -90,6 +107,7 @@ impl pallet_balances::Config for Test { type MaxLocks = (); type MaxReserves = ConstU32<50>; type ReserveIdentifier = [u8; 8]; + type NativeBarrierType = MockNativeBarrier; } pub struct Author4; diff --git a/pallets/farming/Cargo.toml b/pallets/farming/Cargo.toml index ae8d1e369..9c8d19d24 100644 --- a/pallets/farming/Cargo.toml +++ b/pallets/farming/Cargo.toml @@ -17,21 +17,21 @@ scale-info = { version = "2.3.1", default-features = false, features = ["derive" frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false, optional = true } frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } -pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } +pallet-balances = { path = "../balances", default-features = false } sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } manta-primitives = { path = '../../primitives/manta', default-features = false } -orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "polkadot-v0.9.37", default-features = false } +orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "ghzlatarev/polkadot-v0.9.37", default-features = false } pallet-asset-manager = { path = "../asset-manager", default-features = false, optional = true } pallet-assets = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37", default-features = false, optional = true } xcm = { git = "https://github.com/paritytech/polkadot.git", branch = "release-v0.9.37", default-features = false, optional = true } [dev-dependencies] pallet-assets = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } -pallet-balances = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } +pallet-balances = { path = "../balances" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37" } sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37" } diff --git a/pallets/farming/src/mock.rs b/pallets/farming/src/mock.rs index db6562232..29c55d8ed 100644 --- a/pallets/farming/src/mock.rs +++ b/pallets/farming/src/mock.rs @@ -107,6 +107,23 @@ impl frame_system::Config for Runtime { type MaxConsumers = frame_support::traits::ConstU32<16>; } +pub struct MockNativeBarrier; +impl orml_traits::native_barrier::NativeBarrier for MockNativeBarrier { + fn update_native_barrier(_account_id: &AccountId, _amount: Balance) {} + fn ensure_limit_not_exceeded( + _account_id: &AccountId, + _amount: Balance, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) + } +} + +impl orml_traits::native_barrier::NativeChecker for MockNativeBarrier { + fn is_native(_currency_id: &u64) -> bool { + true + } +} + parameter_types! { pub const ExistentialDeposit: Balance = 1; } @@ -120,6 +137,7 @@ impl pallet_balances::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type WeightInfo = (); + type NativeBarrierType = MockNativeBarrier; } parameter_types! { diff --git a/pallets/manta-pay/Cargo.toml b/pallets/manta-pay/Cargo.toml index 078f748f1..12198cba2 100644 --- a/pallets/manta-pay/Cargo.toml +++ b/pallets/manta-pay/Cargo.toml @@ -48,6 +48,7 @@ runtime-benchmarks = [ try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", + "pallet-native-barrier/try-runtime", ] # Serde Serialization @@ -72,6 +73,8 @@ std = [ "manta-util/std", "manta-pay/std", "manta-support/std", + "pallet-native-barrier/std", + "orml-traits/std", ] # Precompute Benchmark Transactions @@ -119,6 +122,8 @@ manta-pay = { git = "https://github.com/manta-network/manta-rs.git", tag = "v0.5 manta-primitives = { path = "../../primitives/manta", default-features = false } manta-support = { package = "pallet-manta-support", path = "../manta-support", default-features = false } manta-util = { git = "https://github.com/manta-network/manta-rs.git", tag = "v0.5.15", default-features = false } +orml-traits = { git = 'https://github.com/manta-network/open-runtime-module-library.git', default-features = false, branch = "ghzlatarev/polkadot-v0.9.37" } +pallet-native-barrier = { path = '../native-barrier', default-features = false } [dev-dependencies] lazy_static = "1.4.0" @@ -126,7 +131,7 @@ manta-crypto = { git = "https://github.com/manta-network/manta-rs.git", tag = "v manta-pay = { git = "https://github.com/manta-network/manta-rs.git", tag = "v0.5.15", features = ["groth16", "parameters", "scale", "download", "test"] } pallet-asset-manager = { path = "../asset-manager" } pallet-assets = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } -pallet-balances = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } +pallet-balances = { path = "../balances" } pallet-tx-pause = { path = '../tx-pause' } sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } diff --git a/pallets/manta-pay/src/lib.rs b/pallets/manta-pay/src/lib.rs index 3d748ef04..709da42d6 100644 --- a/pallets/manta-pay/src/lib.rs +++ b/pallets/manta-pay/src/lib.rs @@ -118,6 +118,7 @@ pub type FungibleLedgerError = assets::FungibleLedgerError; + + type NativeBarrierType: NativeBarrier; } /// Fungible Ledger Implementation for [`Config`] @@ -225,6 +228,17 @@ pub mod pallet { Error::::ZeroTransfer ); } + + let asset_id = id_from_field(post.receiver_posts[0].utxo.public_asset.id) + .ok_or(Error::::InvalidAssetId)?; + + if asset_id == >::NativeAssetId::get() { + ::ensure_limit_not_exceeded( + &origin, + asset_value_decode(post.sources[0]), + )?; + } + Self::post_transaction(None, vec![origin], vec![], post) } diff --git a/pallets/manta-pay/src/mock.rs b/pallets/manta-pay/src/mock.rs index e7738302b..7e56f576a 100644 --- a/pallets/manta-pay/src/mock.rs +++ b/pallets/manta-pay/src/mock.rs @@ -108,6 +108,7 @@ impl pallet_balances::Config for Test { type WeightInfo = (); type MaxReserves = MaxReserves; type ReserveIdentifier = [u8; 8]; + type NativeBarrierType = MockNativeBarrier; } parameter_types! { @@ -260,6 +261,23 @@ impl pallet_asset_manager::Config for Test { type PermissionlessAssetRegistryCost = ConstU128<1000>; } +pub struct MockNativeBarrier; +impl orml_traits::native_barrier::NativeBarrier for MockNativeBarrier { + fn update_native_barrier(_account_id: &AccountId32, _amount: Balance) {} + fn ensure_limit_not_exceeded( + _account_id: &AccountId32, + _amount: Balance, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) + } +} + +impl orml_traits::native_barrier::NativeChecker for MockNativeBarrier { + fn is_native(_currency_id: &u64) -> bool { + true + } +} + parameter_types! { pub const MantaPayPalletId: PalletId = MANTA_PAY_PALLET_ID; } @@ -269,6 +287,7 @@ impl crate::Config for Test { type WeightInfo = crate::weights::SubstrateWeight; type PalletId = MantaPayPalletId; type AssetConfig = MantaAssetConfig; + type NativeBarrierType = MockNativeBarrier; } parameter_types! { diff --git a/pallets/manta-sbt/Cargo.toml b/pallets/manta-sbt/Cargo.toml index bf256233b..05108cc02 100644 --- a/pallets/manta-sbt/Cargo.toml +++ b/pallets/manta-sbt/Cargo.toml @@ -129,9 +129,10 @@ manta-util = { git = "https://github.com/manta-network/manta-rs.git", tag = "v0. lazy_static = "1.4.0" manta-crypto = { git = "https://github.com/manta-network/manta-rs.git", tag = "v0.5.15", features = ["getrandom"] } manta-pay = { git = "https://github.com/manta-network/manta-rs.git", tag = "v0.5.15", features = ["groth16", "parameters", "scale", "download", "test"] } +orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "ghzlatarev/polkadot-v0.9.37" } pallet-asset-manager = { path = "../asset-manager" } pallet-assets = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } -pallet-balances = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } +pallet-balances = { path = "../balances" } pallet-manta-pay = { path = "../manta-pay" } pallet-timestamp = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } pallet-tx-pause = { path = "../tx-pause" } diff --git a/pallets/manta-sbt/src/mock.rs b/pallets/manta-sbt/src/mock.rs index ff59fbab8..d3b6deae6 100644 --- a/pallets/manta-sbt/src/mock.rs +++ b/pallets/manta-sbt/src/mock.rs @@ -106,6 +106,23 @@ impl frame_system::Config for Test { type MaxConsumers = ConstU32<16>; } +pub struct MockNativeBarrier; +impl orml_traits::native_barrier::NativeBarrier for MockNativeBarrier { + fn update_native_barrier(_account_id: &AccountId32, _amount: Balance) {} + fn ensure_limit_not_exceeded( + _account_id: &AccountId32, + _amount: Balance, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) + } +} + +impl orml_traits::native_barrier::NativeChecker for MockNativeBarrier { + fn is_native(_currency_id: &u64) -> bool { + true + } +} + parameter_types! { pub ExistentialDeposit: Balance = 1; pub const MaxLocks: u32 = 50; @@ -122,6 +139,7 @@ impl pallet_balances::Config for Test { type WeightInfo = (); type MaxReserves = MaxReserves; type ReserveIdentifier = [u8; 8]; + type NativeBarrierType = MockNativeBarrier; } parameter_types! { @@ -298,6 +316,7 @@ impl pallet_manta_pay::Config for Test { type WeightInfo = pallet_manta_pay::weights::SubstrateWeight; type PalletId = MantaPayPalletId; type AssetConfig = MantaAssetConfig; + type NativeBarrierType = MockNativeBarrier; } parameter_types! { diff --git a/pallets/name-service/Cargo.toml b/pallets/name-service/Cargo.toml index edffe4cf3..d45eea146 100644 --- a/pallets/name-service/Cargo.toml +++ b/pallets/name-service/Cargo.toml @@ -22,7 +22,8 @@ sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "po sp-std = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37", default-features = false } [dev-dependencies] -pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } +orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "ghzlatarev/polkadot-v0.9.37" } +pallet-balances = { path = "../balances" } sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } diff --git a/pallets/name-service/src/mock.rs b/pallets/name-service/src/mock.rs index c870ec8af..e669bda6b 100644 --- a/pallets/name-service/src/mock.rs +++ b/pallets/name-service/src/mock.rs @@ -65,6 +65,22 @@ impl frame_system::Config for Runtime { type OnSetCode = (); type MaxConsumers = frame_support::traits::ConstU32<16>; } +pub struct MockNativeBarrier; +impl orml_traits::native_barrier::NativeBarrier for MockNativeBarrier { + fn update_native_barrier(_account_id: &AccountId32, _amount: Balance) {} + fn ensure_limit_not_exceeded( + _account_id: &AccountId32, + _amount: Balance, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) + } +} + +impl orml_traits::native_barrier::NativeChecker for MockNativeBarrier { + fn is_native(_currency_id: &u64) -> bool { + true + } +} parameter_types! { pub ExistentialDeposit: Balance = 1; @@ -82,6 +98,7 @@ impl pallet_balances::Config for Runtime { type WeightInfo = (); type MaxReserves = MaxReserves; type ReserveIdentifier = [u8; 8]; + type NativeBarrierType = MockNativeBarrier; } parameter_types! { diff --git a/pallets/native-barrier/Cargo.toml b/pallets/native-barrier/Cargo.toml new file mode 100644 index 000000000..92341fdfc --- /dev/null +++ b/pallets/native-barrier/Cargo.toml @@ -0,0 +1,46 @@ +[package] +authors = ['Manta Network'] +edition = "2021" +homepage = 'https://manta.network' +license = 'GPL-3.0' +name = "pallet-native-barrier" +repository = 'https://github.com/Manta-Network/Manta/' +version = '4.4.0' + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +frame-benchmarking = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37", default-features = false, optional = true } +frame-support = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } +frame-system = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +sp-runtime = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } +sp-std = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } + +manta-primitives = { path = '../../primitives/manta', default-features = false } +orml-traits = { git = 'https://github.com/manta-network/open-runtime-module-library.git', default-features = false, branch = "ghzlatarev/polkadot-v0.9.37" } + +[dev-dependencies] +pallet-balances = { path = "../balances" } +sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } +sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } + +[features] +default = ["std"] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", +] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-runtime/std", + "sp-std/std", + "orml-traits/std", + "manta-primitives/std", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/native-barrier/src/benchmarking.rs b/pallets/native-barrier/src/benchmarking.rs new file mode 100644 index 000000000..cfb66701e --- /dev/null +++ b/pallets/native-barrier/src/benchmarking.rs @@ -0,0 +1,137 @@ +// Copyright 2020-2023 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . +// +//! # Native Barrier Pallet benchmarking. + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; + +use crate::Pallet as NativeBarrier; +use core::time::Duration; +use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_support::traits::Hooks; +use frame_system::RawOrigin; +use sp_runtime::traits::Zero; + +const SEED: u32 = 0; + +benchmarks! { + + // Worst case scenario is once per day when the RemainingLimit is updated + // we set the start time as the beginning of the epoch so the on-initialize will + // add ((now - epoch_start) * daily_limit) + daily_amount for each account + on_initialize { + let daily_limit = T::Balance::zero(); + let start_unix_time = Duration::default(); + let _ = NativeBarrier::::initialize_native_barrier( + RawOrigin::Root.into(), + Some((daily_limit, start_unix_time)), + )?; + let barrier_addresses: Vec = vec![ + account("address_0", 0, SEED), + account("address_1", 0, SEED), + account("address_2", 0, SEED), + account("address_3", 0, SEED), + account("address_4", 0, SEED) + ]; + let _ = NativeBarrier::::add_accounts_to_native_barrier(RawOrigin::Root.into(), barrier_addresses)?; + }:{NativeBarrier::::on_initialize(T::BlockNumber::from(1_000_000u32));} + verify { + let now = ::now().as_secs(); + let expected_days = now / (24 * 60 * 60); + let mut total_limit = daily_limit; + for _ in 0..expected_days { + total_limit += daily_limit; + } + for (account_id, balance) in RemainingLimit::::iter() { + assert_eq!(balance, total_limit); + } + } + + // Worst case scenario would be actually overwriting this simple storage item + initialize_native_barrier { + let caller: T::AccountId = whitelisted_caller(); + let daily_limit = T::Balance::zero(); + let start_unix_time = Duration::default(); + }: initialize_native_barrier(RawOrigin::Root, Some((daily_limit, start_unix_time))) + verify { + assert_eq!(NativeBarrier::::get_configurations().unwrap(), (daily_limit, start_unix_time)); + } + + // Worst case scenario would be actually writing the accounts into the barrier storage item + // Make sure the accounts have at least one daily limit once they are added + add_accounts_to_native_barrier { + let caller: T::AccountId = whitelisted_caller(); + let daily_limit = T::Balance::zero(); + let start_unix_time = Duration::default(); + let _ = NativeBarrier::::initialize_native_barrier( + RawOrigin::Root.into(), + Some((daily_limit, start_unix_time)), + )?; + let barrier_addresses: Vec = vec![ + account("address_0", 0, SEED), + account("address_1", 0, SEED), + account("address_2", 0, SEED), + account("address_3", 0, SEED), + account("address_4", 0, SEED) + ]; + let daily_limit = T::Balance::zero(); + }: add_accounts_to_native_barrier(RawOrigin::Root, barrier_addresses) + verify { + for (account_id, balance) in RemainingLimit::::iter() { + assert_eq!(balance, daily_limit); + } + } + + // worst case scenario is to remove all the account from the barrier + remove_accounts_from_native_barrier { + let caller: T::AccountId = whitelisted_caller(); + let daily_limit = T::Balance::zero(); + let _ = NativeBarrier::::initialize_native_barrier( + RawOrigin::Root.into(), + Some((daily_limit, Default::default())), + )?; + let barrier_addresses: Vec = vec![ + account("address_0", 0, SEED), + account("address_1", 0, SEED), + account("address_2", 0, SEED), + account("address_3", 0, SEED), + account("address_4", 0, SEED) + ]; + let _ = NativeBarrier::::add_accounts_to_native_barrier(RawOrigin::Root.into(), barrier_addresses.clone())?; + let remove_addresses = vec![ + barrier_addresses[0].clone(), + barrier_addresses[1].clone(), + barrier_addresses[2].clone(), + barrier_addresses[3].clone(), + barrier_addresses[4].clone() + ]; + }: remove_accounts_from_native_barrier(RawOrigin::Root, remove_addresses) + verify { + assert_eq!(None, RemainingLimit::::get(account::("address_0", 0, SEED))); + assert_eq!(None, RemainingLimit::::get(account::("address_1", 0, SEED))); + assert_eq!(None, RemainingLimit::::get(account::("address_2", 0, SEED))); + assert_eq!(None, RemainingLimit::::get(account::("address_3", 0, SEED))); + assert_eq!(None, RemainingLimit::::get(account::("address_4", 0, SEED))); + } + + impl_benchmark_test_suite!( + NativeBarrier, + crate::mock::ExtBuilder::default().build(), + crate::mock::Runtime, + ); +} diff --git a/pallets/native-barrier/src/lib.rs b/pallets/native-barrier/src/lib.rs new file mode 100644 index 000000000..f2d918dd8 --- /dev/null +++ b/pallets/native-barrier/src/lib.rs @@ -0,0 +1,292 @@ +// Copyright 2020-2023 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . +// +//! # Native Barrier Pallet + +#![cfg_attr(not(feature = "std"), no_std)] + +mod benchmarking; +mod mock; +#[cfg(test)] +mod tests; +pub mod weights; + +use codec::{Codec, MaxEncodedLen}; +use core::time::Duration; +#[cfg(feature = "std")] +use frame_support::traits::GenesisBuild; +use frame_support::{ensure, pallet_prelude::DispatchResult, traits::UnixTime}; +pub use pallet::*; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize, Zero}, + FixedPointOperand, Saturating, +}; +use sp_std::{fmt::Debug, prelude::*}; +pub use weights::WeightInfo; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The balance of an account. + type Balance: Parameter + + Member + + AtLeast32BitUnsigned + + Codec + + Default + + Copy + + MaybeSerializeDeserialize + + Debug + + MaxEncodedLen + + TypeInfo + + FixedPointOperand; + + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + + /// Timestamp provider + type UnixTime: UnixTime; + } + + /// The current storage version. + const STORAGE_VERSION: frame_support::traits::StorageVersion = + frame_support::traits::StorageVersion::new(0); + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(PhantomData); + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + NativeBarrierInitialized { + init: Option<(T::Balance, Duration)>, + }, + AccountsAddedToBarrier { + accounts: Vec, + }, + AccountsRemovedFromBarrier { + accounts: Vec, + }, + } + + #[pallet::error] + pub enum Error { + NativeBarrierLimitExceeded, + NativeBarrierNotInitialized, + } + + /// Stores daily limit value + #[pallet::storage] + #[pallet::getter(fn get_configurations)] + pub type Configurations = StorageValue<_, (T::Balance, Duration), OptionQuery>; + + /// Stores remaining limit for each account. Skipped days are accumulated. + #[pallet::storage] + #[pallet::getter(fn get_remaining_limit)] + pub type RemainingLimit = + StorageMap<_, Identity, T::AccountId, T::Balance, OptionQuery>; + + /// Caches the last processed day, used to check for start of new days + #[pallet::storage] + #[pallet::getter(fn get_last_day_processed)] + pub type LastDayProcessed = StorageValue<_, u64, OptionQuery>; + + #[pallet::genesis_config] + pub struct GenesisConfig { + pub barrier_accounts: Vec, + pub daily_limit: T::Balance, + pub start_unix_time: Duration, + } + + #[cfg(feature = "std")] + impl Default for GenesisConfig { + fn default() -> Self { + Self { + barrier_accounts: vec![], + daily_limit: T::Balance::zero(), + start_unix_time: Duration::default(), + } + } + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + Configurations::::set(Some((self.daily_limit, self.start_unix_time))); + for account_id in self.barrier_accounts.iter() { + RemainingLimit::::insert(account_id, T::Balance::zero()); + } + } + } + + #[cfg(feature = "std")] + impl GenesisConfig { + /// Direct implementation of `GenesisBuild::build_storage`. + /// + /// Kept in order not to break dependency. + pub fn build_storage(&self) -> Result { + >::build_storage(self) + } + + /// Direct implementation of `GenesisBuild::assimilate_storage`. + /// + /// Kept in order not to break dependency. + pub fn assimilate_storage(&self, storage: &mut sp_runtime::Storage) -> Result<(), String> { + >::assimilate_storage(self, storage) + } + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(_n: T::BlockNumber) -> Weight { + if let Some((_, start_unix_time)) = Configurations::::get() { + let now = T::UnixTime::now(); + if start_unix_time <= now { + let days_since_start = + (now.as_secs() - start_unix_time.as_secs()) / (24 * 60 * 60); + + if let Some(last_day_processed) = >::get() { + if days_since_start > last_day_processed { + Self::reset_remaining_limit(days_since_start - last_day_processed); + >::put(days_since_start); + } + } else { + >::put(0); + } + } + } + + T::WeightInfo::on_initialize() + } + } + + #[pallet::call] + impl Pallet { + /// Sets the start unix time and daily limit for the barrier logic. + /// Can be in the past or the future. Can be disabled by setting to None. + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::initialize_native_barrier())] + pub fn initialize_native_barrier( + origin: OriginFor, + init: Option<(T::Balance, Duration)>, + ) -> DispatchResult { + ensure_root(origin)?; + >::set(init); + if >::get().is_none() { + >::put(0); + } + Self::deposit_event(Event::NativeBarrierInitialized { init }); + Ok(()) + } + + /// Add `accounts` to barrier to make them have limited native transfers + /// Sets the in the RemainingLimits storage item, + /// and sets their limit to the amount of one daily limit. Can be used multiple times. + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::add_accounts_to_native_barrier())] + pub fn add_accounts_to_native_barrier( + origin: OriginFor, + accounts: Vec, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + + if let Some((daily_limit, _)) = Configurations::::get() { + for account_id in accounts.iter() { + if !RemainingLimit::::contains_key(account_id) { + RemainingLimit::::insert(account_id, daily_limit); + } + } + + Self::deposit_event(Event::AccountsAddedToBarrier { accounts }); + } else { + return Err(Error::::NativeBarrierNotInitialized.into()); + } + + Ok(().into()) + } + + /// Remove `accounts` from the barrier's RemainingLimit storage + #[pallet::call_index(2)] + #[pallet::weight(T::WeightInfo::remove_accounts_from_native_barrier())] + pub fn remove_accounts_from_native_barrier( + origin: OriginFor, + accounts: Vec, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + + for account_id in accounts.iter() { + RemainingLimit::::remove(account_id); + } + + Self::deposit_event(Event::AccountsRemovedFromBarrier { accounts }); + + Ok(().into()) + } + } +} + +impl orml_traits::native_barrier::NativeBarrier for Pallet { + fn ensure_limit_not_exceeded(account_id: &T::AccountId, amount: T::Balance) -> DispatchResult { + if let Some((_, start_unix_time)) = >::get() { + let now = T::UnixTime::now(); + if start_unix_time <= now { + if let Some(remaining_limit) = RemainingLimit::::get(account_id) { + ensure!( + amount <= remaining_limit, + Error::::NativeBarrierLimitExceeded + ); + + // If the ensure didn't return an error, update the native transfers + Self::update_native_barrier(account_id, amount); + } + } + } + + Ok(()) + } + + fn update_native_barrier(account_id: &T::AccountId, amount: T::Balance) { + >::mutate_exists(account_id, |maybe_remainder| match maybe_remainder { + Some(remainder) => { + *remainder = remainder.saturating_sub(amount); + } + None => {} + }); + } +} + +impl Pallet { + fn reset_remaining_limit(unprocessed_days: u64) { + if let Some((daily_limit, _)) = >::get() { + for (account_id, mut remaining_limit) in RemainingLimit::::iter() { + for _ in 0..unprocessed_days { + remaining_limit += daily_limit; + } + >::insert(&account_id, remaining_limit); + } + } + } +} diff --git a/pallets/native-barrier/src/mock.rs b/pallets/native-barrier/src/mock.rs new file mode 100644 index 000000000..10605f51b --- /dev/null +++ b/pallets/native-barrier/src/mock.rs @@ -0,0 +1,151 @@ +// Copyright 2020-2023 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . +// +// The pallet-tx-pause pallet is forked from Acala's transaction-pause module https://github.com/AcalaNetwork/Acala/tree/master/modules/transaction-pause +// The original license is the following - SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +//! Mocks for the native barrier pallet. + +#![cfg(test)] + +use super::*; +use frame_support::{ + construct_runtime, ord_parameter_types, parameter_types, + traits::{ConstU32, Contains, UnixTime}, +}; +use manta_primitives::types::{Balance, BlockNumber, Header}; + +use sp_core::H256; +use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; + +pub type AccountId = u128; + +mod pallet_native_barrier { + pub use super::super::*; +} + +pub struct BaseFilter; +impl Contains for BaseFilter { + fn contains(_call: &RuntimeCall) -> bool { + true + } +} + +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = BlockNumber; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU32<250>; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = BaseFilter; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const NativeTokenExistentialDeposit: Balance = 10; +} + +impl pallet_balances::Config for Runtime { + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = NativeTokenExistentialDeposit; + type AccountStore = System; + type MaxLocks = (); + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = (); + type WeightInfo = (); + type NativeBarrierType = NativeBarrier; +} + +ord_parameter_types! { + pub const One: AccountId = 1; +} + +use std::cell::RefCell; + +// This will be our mutable mock time. +thread_local! { + pub static MOCK_TIME: RefCell = RefCell::new(Duration::default()); +} +pub struct TestTimeSource; + +impl UnixTime for TestTimeSource { + fn now() -> Duration { + MOCK_TIME.with(|time| *time.borrow()) + } +} + +parameter_types! { + pub NonPausablePallets: Vec> = vec![b"Democracy".to_vec(), b"Balances".to_vec(), b"Council".to_vec(), b"CouncilCollective".to_vec(), b"TechnicalCommittee".to_vec(), b"TechnicalCollective".to_vec()]; +} + +impl Config for Runtime { + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type UnixTime = TestTimeSource; + type WeightInfo = (); +} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + NativeBarrier: pallet_native_barrier::{Pallet, Storage, Call, Event}, + Balances: pallet_balances::{Pallet, Storage, Call, Event}, + } +); + +pub struct ExtBuilder; + +impl Default for ExtBuilder { + fn default() -> Self { + ExtBuilder + } +} + +impl ExtBuilder { + pub fn build(self) -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + t.into() + } +} diff --git a/pallets/native-barrier/src/tests.rs b/pallets/native-barrier/src/tests.rs new file mode 100644 index 000000000..08e23e62a --- /dev/null +++ b/pallets/native-barrier/src/tests.rs @@ -0,0 +1,386 @@ +// Copyright 2020-2023 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . + +use crate::{ + mock::{Balances, ExtBuilder, NativeBarrier, Runtime, RuntimeOrigin, MOCK_TIME}, + Error, +}; + +use core::time::Duration; +use frame_support::{assert_err, assert_noop, assert_ok, traits::OnInitialize}; +use frame_system::RawOrigin; + +#[test] +fn extrinsics_as_normal_user_should_not_work() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + NativeBarrier::initialize_native_barrier( + RuntimeOrigin::signed(1), + Some((10u128, Default::default())), + ), + sp_runtime::DispatchError::BadOrigin + ); + assert_noop!( + NativeBarrier::add_accounts_to_native_barrier(RuntimeOrigin::signed(1), vec![]), + sp_runtime::DispatchError::BadOrigin + ); + assert_noop!( + NativeBarrier::remove_accounts_from_native_barrier(RuntimeOrigin::signed(1), vec![]), + sp_runtime::DispatchError::BadOrigin + ); + }); +} + +#[test] +fn extrinsics_as_root_should_work() { + ExtBuilder::default().build().execute_with(|| { + assert_err!( + NativeBarrier::add_accounts_to_native_barrier(RawOrigin::Root.into(), vec![]), + Error::::NativeBarrierNotInitialized + ); + assert_err!( + NativeBarrier::add_accounts_to_native_barrier(RawOrigin::Root.into(), vec![]), + Error::::NativeBarrierNotInitialized + ); + assert_ok!(NativeBarrier::initialize_native_barrier( + RawOrigin::Root.into(), + Some((10, Duration::default())), + )); + assert_ok!(NativeBarrier::add_accounts_to_native_barrier( + RawOrigin::Root.into(), + vec![1, 2, 3] + )); + assert_ne!(NativeBarrier::get_remaining_limit(1), None); + assert_ok!(NativeBarrier::remove_accounts_from_native_barrier( + RawOrigin::Root.into(), + vec![1] + )); + assert_eq!(NativeBarrier::get_remaining_limit(1), None); + }); +} + +fn set_mock_time(new_time: Duration) { + MOCK_TIME.with(|time| { + *time.borrow_mut() = new_time; + }); +} + +fn advance_mock_time(delta: Duration) { + MOCK_TIME.with(|time| { + *time.borrow_mut() += delta; + }); +} + +fn initialize_test_environment() { + // add balances to 1,2,3 + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 1000, 0)); + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 2, 1000, 0)); + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 3, 1000, 0)); + + // transfer more than limit should work + assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), 2, 20)); +} + +fn initialize_native_barrier(daily_limit: u128, start_unix_time: Duration) { + set_mock_time(Duration::default()); + + // add balances to 1,2,3 + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 1000, 0)); + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 2, 1000, 0)); + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 3, 1000, 0)); + + assert_ok!(NativeBarrier::initialize_native_barrier( + RawOrigin::Root.into(), + Some((daily_limit, start_unix_time)), + )); + assert_ok!(NativeBarrier::add_accounts_to_native_barrier( + RawOrigin::Root.into(), + vec![1, 2, 3] + )); +} + +#[test] +fn start_in_the_past_should_work() { + ExtBuilder::default().build().execute_with(|| { + initialize_test_environment(); + + let daily_limit = 10u128; + let day_in_secs = 86400; + + initialize_native_barrier(daily_limit, Duration::default()); + advance_mock_time(Duration::from_secs(day_in_secs)); + + // transfer more than 1 limit should not work until next block + assert_eq!(NativeBarrier::get_remaining_limit(1), Some(daily_limit)); + assert_err!( + Balances::transfer(RuntimeOrigin::signed(1), 2, daily_limit + 1), + Error::::NativeBarrierLimitExceeded + ); + + NativeBarrier::on_initialize(1); + + // transfer under limit should now work + assert_eq!(NativeBarrier::get_remaining_limit(1), Some(2 * daily_limit)); + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(1), + 2, + daily_limit / 2 + )); + + // transfer more than limit should not work + assert_eq!( + NativeBarrier::get_remaining_limit(1), + Some(daily_limit + daily_limit / 2) + ); + assert_err!( + Balances::transfer(RuntimeOrigin::signed(1), 2, 2 * daily_limit + 1), + Error::::NativeBarrierLimitExceeded + ); + assert_err!( + Balances::transfer_keep_alive(RuntimeOrigin::signed(1), 2, 2 * daily_limit + 1), + Error::::NativeBarrierLimitExceeded + ); + assert_err!( + Balances::transfer_all(RuntimeOrigin::signed(1), 2, false), + Error::::NativeBarrierLimitExceeded + ); + + // limit should be multiple of daily limit (now - epoch_start) + // roll one day + advance_mock_time(Duration::from_secs(day_in_secs)); + NativeBarrier::on_initialize(1); + + // check that limit has been updated + // transfer more than limit should not work + assert_eq!( + NativeBarrier::get_remaining_limit(1), + Some(2 * daily_limit + daily_limit / 2) + ); + assert_err!( + Balances::transfer( + RuntimeOrigin::signed(1), + 2, + 2 * daily_limit + daily_limit / 2 + 1 + ), + Error::::NativeBarrierLimitExceeded + ); + assert_err!( + Balances::transfer_keep_alive( + RuntimeOrigin::signed(1), + 2, + 2 * daily_limit + daily_limit / 2 + 1 + ), + Error::::NativeBarrierLimitExceeded + ); + assert_err!( + Balances::transfer_all(RuntimeOrigin::signed(1), 2, false), + Error::::NativeBarrierLimitExceeded + ); + // transfer under limit should work + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(1), + 2, + daily_limit + daily_limit / 2 + )); + + assert_ok!(NativeBarrier::remove_accounts_from_native_barrier( + RawOrigin::Root.into(), + vec![1] + )); + + // transfer more than limit should work for 1 + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(1), + 3, + daily_limit * 5 + )); + assert_eq!(NativeBarrier::get_remaining_limit(2), Some(3 * daily_limit)); + assert_err!( + Balances::transfer(RuntimeOrigin::signed(2), 2, 3 * daily_limit + 1), + Error::::NativeBarrierLimitExceeded + ); + + assert_ok!(NativeBarrier::initialize_native_barrier( + RawOrigin::Root.into(), + None, + )); + + // transfer more than limit should work for all + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(1), + 3, + daily_limit * 5 + )); + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(2), + 3, + daily_limit * 5 + )); + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(3), + 3, + daily_limit * 5 + )); + }); +} + +#[test] +fn start_in_the_future_should_work() { + ExtBuilder::default().build().execute_with(|| { + initialize_test_environment(); + + let daily_limit = 10u128; + let advance_10 = 10 * 86400; + let future_days = 10; + + // transfer more than limit should work + assert_eq!(NativeBarrier::get_remaining_limit(1), None); + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(1), + 2, + daily_limit + 5 + )); + + initialize_native_barrier(daily_limit, Duration::from_secs(advance_10)); + + // has 1 daily limit but can over-spend it as the barrier is in the future + assert_eq!(NativeBarrier::get_remaining_limit(1), Some(daily_limit)); + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(1), + 2, + daily_limit * 2 + ),); + // The limit should not have been updated + assert_eq!(NativeBarrier::get_remaining_limit(1), Some(daily_limit)); + + advance_mock_time(Duration::from_secs(advance_10 * 2)); + NativeBarrier::on_initialize(1); + + // check that limit has been updated + // transfer more than limit should not work + assert_eq!( + NativeBarrier::get_remaining_limit(1), + Some(daily_limit + 10 * daily_limit) + ); + assert_err!( + Balances::transfer( + RuntimeOrigin::signed(1), + 2, + daily_limit + future_days as u128 * daily_limit + 1 + ), + Error::::NativeBarrierLimitExceeded + ); + assert_err!( + Balances::transfer_keep_alive( + RuntimeOrigin::signed(1), + 2, + daily_limit + future_days as u128 * daily_limit + 1 + ), + Error::::NativeBarrierLimitExceeded + ); + assert_err!( + Balances::transfer_all(RuntimeOrigin::signed(1), 2, false), + Error::::NativeBarrierLimitExceeded + ); + + // transfer under limit should work + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(1), + 2, + future_days as u128 * daily_limit + )); + + assert_ok!(NativeBarrier::remove_accounts_from_native_barrier( + RawOrigin::Root.into(), + vec![1] + )); + + // transfer more than limit should work for 1 + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(1), + 3, + future_days as u128 * daily_limit + 5 + )); + assert_err!( + Balances::transfer( + RuntimeOrigin::signed(2), + 2, + daily_limit + future_days as u128 * daily_limit + 1 + ), + Error::::NativeBarrierLimitExceeded + ); + + assert_ok!(NativeBarrier::initialize_native_barrier( + RawOrigin::Root.into(), + None, + )); + + // transfer more than limit should work for all + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(1), + 3, + (future_days as u128 + 1) * daily_limit * 2 + )); + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(2), + 3, + (future_days as u128 + 1) * daily_limit * 2 + )); + assert_ok!(Balances::transfer( + RuntimeOrigin::signed(3), + 3, + (future_days as u128 + 1) * daily_limit * 2 + )); + }); +} + +fn loop_on_init_and_assert_accounts(should_be: Option) { + for _ in 0..1000 { + NativeBarrier::on_initialize(1); + } + + // check that limit has not changed + for k in 1..4 { + let limit = NativeBarrier::get_remaining_limit(k); + assert_eq!(limit, should_be); + } +} + +#[test] +fn limits_should_grow_appropriately() { + ExtBuilder::default().build().execute_with(|| { + initialize_test_environment(); + loop_on_init_and_assert_accounts(None); + assert_eq!(NativeBarrier::get_last_day_processed(), None); + + let daily_limit = 10u128; + let advance_10 = 10 * 86400; + + initialize_native_barrier(daily_limit, Duration::from_secs(advance_10)); + loop_on_init_and_assert_accounts(Some(daily_limit)); + assert_eq!(NativeBarrier::get_last_day_processed(), Some(0)); + + // roll 20 days (10 days after start) + advance_mock_time(Duration::from_secs(advance_10 * 2)); + loop_on_init_and_assert_accounts(Some(daily_limit + daily_limit * 10)); + assert_eq!(NativeBarrier::get_last_day_processed(), Some(10)); + + // roll another 10 days + advance_mock_time(Duration::from_secs(advance_10)); + loop_on_init_and_assert_accounts(Some(daily_limit + daily_limit * 20)); + assert_eq!(NativeBarrier::get_last_day_processed(), Some(20)); + }); +} diff --git a/pallets/native-barrier/src/weights.rs b/pallets/native-barrier/src/weights.rs new file mode 100644 index 000000000..f8af43ca9 --- /dev/null +++ b/pallets/native-barrier/src/weights.rs @@ -0,0 +1,122 @@ +// Copyright 2020-2023 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . + +//! Autogenerated weights for pallet_native_barrier +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-08-30, STEPS: `50`, REPEAT: 40, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("manta-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/manta +// benchmark +// pallet +// --chain=manta-dev +// --steps=50 +// --repeat=40 +// --pallet=pallet_native_barrier +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./scripts/benchmarking/frame-weights-output/pallet_native_barrier.rs +// --template=.github/resources/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(clippy::unnecessary_cast)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; +use manta_primitives::constants::RocksDbWeight; + +/// Weight functions needed for pallet_native_barrier. +pub trait WeightInfo { + fn on_initialize() -> Weight; + fn initialize_native_barrier() -> Weight; + fn add_accounts_to_native_barrier() -> Weight; + fn remove_accounts_from_native_barrier() -> Weight; +} + +/// Weights for pallet_native_barrier using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: NativeBarrier Configurations (r:1 w:0) + // Storage: Timestamp Now (r:1 w:0) + // Storage: NativeBarrier LastDayProcessed (r:1 w:0) + fn on_initialize() -> Weight { + // Minimum execution time: 8_005 nanoseconds. + Weight::from_ref_time(8_346_000) + .saturating_add(T::DbWeight::get().reads(3)) + } + // Storage: NativeBarrier LastDayProcessed (r:1 w:1) + // Storage: NativeBarrier Configurations (r:0 w:1) + fn initialize_native_barrier() -> Weight { + // Minimum execution time: 37_480 nanoseconds. + Weight::from_ref_time(38_803_000) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: NativeBarrier Configurations (r:1 w:0) + // Storage: NativeBarrier RemainingLimit (r:5 w:5) + fn add_accounts_to_native_barrier() -> Weight { + // Minimum execution time: 27_011 nanoseconds. + Weight::from_ref_time(27_622_000) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(5)) + } + // Storage: NativeBarrier RemainingLimit (r:0 w:5) + fn remove_accounts_from_native_barrier() -> Weight { + // Minimum execution time: 17_994 nanoseconds. + Weight::from_ref_time(18_445_000) + .saturating_add(T::DbWeight::get().writes(5)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: NativeBarrier Configurations (r:1 w:0) + // Storage: Timestamp Now (r:1 w:0) + // Storage: NativeBarrier LastDayProcessed (r:1 w:0) + fn on_initialize() -> Weight { + // Minimum execution time: 8_005 nanoseconds. + Weight::from_ref_time(8_346_000) + .saturating_add(RocksDbWeight::get().reads(3)) + } + // Storage: NativeBarrier LastDayProcessed (r:1 w:1) + // Storage: NativeBarrier Configurations (r:0 w:1) + fn initialize_native_barrier() -> Weight { + // Minimum execution time: 37_480 nanoseconds. + Weight::from_ref_time(38_803_000) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(2)) + } + // Storage: NativeBarrier Configurations (r:1 w:0) + // Storage: NativeBarrier RemainingLimit (r:5 w:5) + fn add_accounts_to_native_barrier() -> Weight { + // Minimum execution time: 27_011 nanoseconds. + Weight::from_ref_time(27_622_000) + .saturating_add(RocksDbWeight::get().reads(6)) + .saturating_add(RocksDbWeight::get().writes(5)) + } + // Storage: NativeBarrier RemainingLimit (r:0 w:5) + fn remove_accounts_from_native_barrier() -> Weight { + // Minimum execution time: 17_994 nanoseconds. + Weight::from_ref_time(18_445_000) + .saturating_add(RocksDbWeight::get().writes(5)) + } +} diff --git a/pallets/pallet-lottery/Cargo.toml b/pallets/pallet-lottery/Cargo.toml index 3e04c15e2..840cf5aa1 100644 --- a/pallets/pallet-lottery/Cargo.toml +++ b/pallets/pallet-lottery/Cargo.toml @@ -40,7 +40,8 @@ rand = { version = "0.8.5", default-features = false, optional = true } calamari-runtime = { path = "../../runtime/calamari", default-features = false } lazy_static = "1.4.0" manta-collator-selection = { path = "../collator-selection", default-features = false } -pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } +orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "ghzlatarev/polkadot-v0.9.37" } +pallet-balances = { path = "../balances" } pallet-preimage = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } pallet-randomness = { path = '../randomness', default-features = false } pallet-scheduler = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } diff --git a/pallets/pallet-lottery/src/mock.rs b/pallets/pallet-lottery/src/mock.rs index 279d8a07c..cbba3a5e6 100644 --- a/pallets/pallet-lottery/src/mock.rs +++ b/pallets/pallet-lottery/src/mock.rs @@ -111,6 +111,24 @@ impl frame_system::Config for Test { type OnSetCode = (); type MaxConsumers = frame_support::traits::ConstU32<16>; } + +pub struct MockNativeBarrier; +impl orml_traits::native_barrier::NativeBarrier for MockNativeBarrier { + fn update_native_barrier(_account_id: &AccountId, _amount: Balance) {} + fn ensure_limit_not_exceeded( + _account_id: &AccountId, + _amount: Balance, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) + } +} + +impl orml_traits::native_barrier::NativeChecker for MockNativeBarrier { + fn is_native(_currency_id: &u64) -> bool { + true + } +} + parameter_types! { pub const ExistentialDeposit: u128 = 1; } @@ -124,6 +142,7 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); + type NativeBarrierType = MockNativeBarrier; } parameter_types! { diff --git a/pallets/parachain-staking/Cargo.toml b/pallets/parachain-staking/Cargo.toml index 41c3bf00b..db6276094 100644 --- a/pallets/parachain-staking/Cargo.toml +++ b/pallets/parachain-staking/Cargo.toml @@ -33,10 +33,12 @@ sp-staking = { git = 'https://github.com/paritytech/substrate.git', default-feat [dev-dependencies] similar-asserts = "1.1.0" -pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } +pallet-balances = { path = "../balances" } sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } +orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "ghzlatarev/polkadot-v0.9.37" } + [features] default = ["std"] runtime-benchmarks = [ diff --git a/pallets/parachain-staking/src/mock.rs b/pallets/parachain-staking/src/mock.rs index 10317ad3f..ebdd55655 100644 --- a/pallets/parachain-staking/src/mock.rs +++ b/pallets/parachain-staking/src/mock.rs @@ -87,6 +87,24 @@ impl frame_system::Config for Test { type OnSetCode = (); type MaxConsumers = frame_support::traits::ConstU32<16>; } + +pub struct MockNativeBarrier; +impl orml_traits::native_barrier::NativeBarrier for MockNativeBarrier { + fn update_native_barrier(_account_id: &AccountId, _amount: Balance) {} + fn ensure_limit_not_exceeded( + _account_id: &AccountId, + _amount: Balance, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) + } +} + +impl orml_traits::native_barrier::NativeChecker for MockNativeBarrier { + fn is_native(_currency_id: &u64) -> bool { + true + } +} + parameter_types! { pub const ExistentialDeposit: u128 = 1; } @@ -100,6 +118,7 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); + type NativeBarrierType = MockNativeBarrier; } impl block_author::Config for Test {} parameter_types! { diff --git a/pallets/randomness/Cargo.toml b/pallets/randomness/Cargo.toml index 000f52e6d..5c616a6bb 100644 --- a/pallets/randomness/Cargo.toml +++ b/pallets/randomness/Cargo.toml @@ -27,7 +27,8 @@ sp-std = { git = 'https://github.com/paritytech/substrate.git', branch = "polkad [dev-dependencies] derive_more = "0.99" -pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37", features = ["std"] } +orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "ghzlatarev/polkadot-v0.9.37" } +pallet-balances = { path = "../balances", features = ["std"] } [features] default = ["std"] diff --git a/pallets/randomness/src/mock.rs b/pallets/randomness/src/mock.rs index b15ef304f..bbcfeceb2 100644 --- a/pallets/randomness/src/mock.rs +++ b/pallets/randomness/src/mock.rs @@ -79,6 +79,23 @@ impl frame_system::Config for Test { type MaxConsumers = frame_support::traits::ConstU32<16>; } +pub struct MockNativeBarrier; +impl orml_traits::native_barrier::NativeBarrier for MockNativeBarrier { + fn update_native_barrier(_account_id: &AccountId, _amount: Balance) {} + fn ensure_limit_not_exceeded( + _account_id: &AccountId, + _amount: Balance, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) + } +} + +impl orml_traits::native_barrier::NativeChecker for MockNativeBarrier { + fn is_native(_currency_id: &u64) -> bool { + true + } +} + parameter_types! { pub const ExistentialDeposit: u128 = 0; } @@ -92,6 +109,7 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); + type NativeBarrierType = MockNativeBarrier; } pub struct BabeDataGetter; diff --git a/pallets/tx-pause/Cargo.toml b/pallets/tx-pause/Cargo.toml index 45b20d484..a1020baa4 100644 --- a/pallets/tx-pause/Cargo.toml +++ b/pallets/tx-pause/Cargo.toml @@ -18,7 +18,8 @@ sp-std = { git = "https://github.com/paritytech/substrate.git", branch = "polkad [dev-dependencies] manta-primitives = { path = '../../primitives/manta' } -pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } +orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "ghzlatarev/polkadot-v0.9.37" } +pallet-balances = { path = "../balances" } sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } diff --git a/pallets/tx-pause/src/mock.rs b/pallets/tx-pause/src/mock.rs index 13d6de39a..cd428b898 100644 --- a/pallets/tx-pause/src/mock.rs +++ b/pallets/tx-pause/src/mock.rs @@ -72,6 +72,22 @@ impl frame_system::Config for Runtime { type OnSetCode = (); type MaxConsumers = frame_support::traits::ConstU32<16>; } +pub struct MockNativeBarrier; +impl orml_traits::native_barrier::NativeBarrier for MockNativeBarrier { + fn update_native_barrier(_account_id: &AccountId, _amount: Balance) {} + fn ensure_limit_not_exceeded( + _account_id: &AccountId, + _amount: Balance, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) + } +} + +impl orml_traits::native_barrier::NativeChecker for MockNativeBarrier { + fn is_native(_currency_id: &u64) -> bool { + true + } +} parameter_types! { pub const NativeTokenExistentialDeposit: Balance = 10; @@ -87,6 +103,7 @@ impl pallet_balances::Config for Runtime { type MaxReserves = ConstU32<50>; type ReserveIdentifier = (); type WeightInfo = (); + type NativeBarrierType = MockNativeBarrier; } ord_parameter_types! { diff --git a/pallets/vesting/Cargo.toml b/pallets/vesting/Cargo.toml index 3856c741b..8aca47098 100644 --- a/pallets/vesting/Cargo.toml +++ b/pallets/vesting/Cargo.toml @@ -14,7 +14,7 @@ scale-info = { version = "2.1.2", default-features = false, features = ["derive" frame-benchmarking = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37", optional = true } frame-support = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } frame-system = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } -pallet-balances = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37", optional = true } +pallet-balances = { path = "../balances", default-features = false } pallet-timestamp = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37", optional = true } sp-runtime = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } sp-std = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } @@ -22,7 +22,8 @@ sp-std = { git = 'https://github.com/paritytech/substrate.git', default-features [dev-dependencies] chrono = "0.4" manta-primitives = { path = "../../primitives/manta" } -pallet-balances = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } +orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "ghzlatarev/polkadot-v0.9.37" } +pallet-balances = { path = "../balances" } pallet-timestamp = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } sp-core = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } sp-io = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } diff --git a/pallets/vesting/src/mock.rs b/pallets/vesting/src/mock.rs index 40cd37bb8..3f66114e3 100644 --- a/pallets/vesting/src/mock.rs +++ b/pallets/vesting/src/mock.rs @@ -75,6 +75,23 @@ impl frame_system::Config for Test { type MaxConsumers = frame_support::traits::ConstU32<16>; } +pub struct MockNativeBarrier; +impl orml_traits::native_barrier::NativeBarrier for MockNativeBarrier { + fn update_native_barrier(_account_id: &AccountId, _amount: Balance) {} + fn ensure_limit_not_exceeded( + _account_id: &AccountId, + _amount: Balance, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) + } +} + +impl orml_traits::native_barrier::NativeChecker for MockNativeBarrier { + fn is_native(_currency_id: &u64) -> bool { + true + } +} + impl pallet_balances::Config for Test { type AccountStore = System; type Balance = Balance; @@ -85,6 +102,7 @@ impl pallet_balances::Config for Test { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type WeightInfo = (); + type NativeBarrierType = MockNativeBarrier; } parameter_types! { diff --git a/primitives/manta/Cargo.toml b/primitives/manta/Cargo.toml index 8b5b068ff..e46add363 100644 --- a/primitives/manta/Cargo.toml +++ b/primitives/manta/Cargo.toml @@ -28,7 +28,7 @@ xcm = { git = 'https://github.com/paritytech/polkadot.git', default-features = f xcm-builder = { git = 'https://github.com/paritytech/polkadot.git', default-features = false, branch = "release-v0.9.37" } xcm-executor = { git = 'https://github.com/paritytech/polkadot.git', default-features = false, branch = "release-v0.9.37" } -orml-traits = { git = 'https://github.com/manta-network/open-runtime-module-library.git', branch = "polkadot-v0.9.37", default-features = false } +orml-traits = { git = 'https://github.com/manta-network/open-runtime-module-library.git', branch = "ghzlatarev/polkadot-v0.9.37", default-features = false } [features] default = ["std"] diff --git a/runtime/calamari/Cargo.toml b/runtime/calamari/Cargo.toml index d6cf61286..90cbf6c78 100644 --- a/runtime/calamari/Cargo.toml +++ b/runtime/calamari/Cargo.toml @@ -44,7 +44,7 @@ frame-try-runtime = { git = "https://github.com/paritytech/substrate.git", defau pallet-assets = { git = "https://github.com/paritytech/substrate.git", default-features = false, branch = "polkadot-v0.9.37" } pallet-aura = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } pallet-authorship = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } -pallet-balances = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } +pallet-balances = { path = "../../pallets/balances", default-features = false } pallet-collective = { git = "https://github.com/paritytech/substrate.git", default-features = false, branch = "polkadot-v0.9.37" } pallet-democracy = { git = "https://github.com/paritytech/substrate.git", default-features = false, branch = "polkadot-v0.9.37" } pallet-membership = { git = "https://github.com/paritytech/substrate.git", default-features = false, branch = "polkadot-v0.9.37" } @@ -100,6 +100,7 @@ pallet-lottery = { path = '../../pallets/pallet-lottery', default-features = fal pallet-manta-pay = { path = '../../pallets/manta-pay', default-features = false, features = ["runtime"] } pallet-manta-sbt = { path = '../../pallets/manta-sbt', default-features = false, features = ["runtime"] } pallet-name-service = { path = '../../pallets/name-service', default-features = false } +pallet-native-barrier = { path = '../../pallets/native-barrier', default-features = false } pallet-parachain-staking = { path = '../../pallets/parachain-staking', default-features = false } pallet-randomness = { path = '../../pallets/randomness', default-features = false } pallet-tx-pause = { path = '../../pallets/tx-pause', default-features = false } @@ -107,8 +108,8 @@ runtime-common = { path = '../common', default-features = false } session-key-primitives = { path = '../../primitives/session-keys', default-features = false } # Third party (vendored) dependencies -orml-traits = { git = 'https://github.com/manta-network/open-runtime-module-library.git', default-features = false, branch = "polkadot-v0.9.37" } -orml-xtokens = { git = 'https://github.com/manta-network/open-runtime-module-library.git', default-features = false, branch = "polkadot-v0.9.37" } +orml-traits = { git = 'https://github.com/manta-network/open-runtime-module-library.git', default-features = false, branch = "ghzlatarev/polkadot-v0.9.37" } +orml-xtokens = { git = 'https://github.com/manta-network/open-runtime-module-library.git', default-features = false, branch = "ghzlatarev/polkadot-v0.9.37" } zenlink-protocol = { git = 'https://github.com/manta-network/Zenlink', branch = "polkadot-v0.9.37", default-features = false } zenlink-protocol-runtime-api = { git = 'https://github.com/manta-network/Zenlink', branch = "polkadot-v0.9.37", default-features = false } @@ -182,6 +183,8 @@ runtime-benchmarks = [ "pallet-conviction-voting/runtime-benchmarks", "pallet-referenda/runtime-benchmarks", "pallet-ranked-collective/runtime-benchmarks", + 'pallet-native-barrier/runtime-benchmarks', + ] try-runtime = [ 'frame-try-runtime', @@ -226,6 +229,7 @@ try-runtime = [ 'pallet-randomness/try-runtime', 'pallet-lottery/try-runtime', "pallet-farming/try-runtime", + "pallet-native-barrier/try-runtime", ] # Set timing constants (e.g. session period) to faster versions to speed up testing. fast-runtime = [] @@ -288,6 +292,7 @@ std = [ 'xcm/std', 'xcm-builder/std', 'xcm-executor/std', + 'pallet-native-barrier/std', 'polkadot-primitives/std', 'manta-collator-selection/std', 'calamari-vesting/std', diff --git a/runtime/calamari/src/assets_config.rs b/runtime/calamari/src/assets_config.rs index 37dacd9f2..ddef743b6 100644 --- a/runtime/calamari/src/assets_config.rs +++ b/runtime/calamari/src/assets_config.rs @@ -16,8 +16,8 @@ use super::{ weights, xcm_config::SelfReserve, AssetManager, Assets, Balances, - EnsureRootOrThreeFourthsCouncil, NativeTokenExistentialDeposit, Runtime, RuntimeEvent, - RuntimeOrigin, TechnicalCollective, Timestamp, KMA, + EnsureRootOrThreeFourthsCouncil, NativeBarrier, NativeTokenExistentialDeposit, Runtime, + RuntimeEvent, RuntimeOrigin, TechnicalCollective, Timestamp, KMA, }; use manta_primitives::{ @@ -195,6 +195,7 @@ impl pallet_manta_pay::Config for Runtime { type WeightInfo = weights::pallet_manta_pay::SubstrateWeight; type AssetConfig = CalamariAssetConfig; type PalletId = MantaPayPalletId; + type NativeBarrierType = NativeBarrier; } parameter_types! { diff --git a/runtime/calamari/src/diff_tx_fees.rs b/runtime/calamari/src/diff_tx_fees.rs index ecfd8794d..cc7cc32f0 100644 --- a/runtime/calamari/src/diff_tx_fees.rs +++ b/runtime/calamari/src/diff_tx_fees.rs @@ -105,7 +105,7 @@ fn diff_tx_fees() { frame_support::metadata::RuntimeMetadata::V14(metadata) => metadata.pallets, _ => unreachable!(), }; - assert_eq!(pallets.len(), 39); + assert_eq!(pallets.len(), 40); }); let fee_multipliers = fee_multipliers(); @@ -140,7 +140,6 @@ fn diff_tx_fees() { } #[test] -#[ignore] fn generate_all_current_extrinsics_tx_fee_to_csv() { const VERSION: &str = env!("CARGO_PKG_VERSION"); const CURRENT_PATH: &str = env!("CARGO_MANIFEST_DIR"); @@ -3169,5 +3168,47 @@ fn calculate_all_current_extrinsic_tx_fee() -> ( )); } + // pallet_native_barrier + { + assert_eq!( + crate::RuntimeCall::get_call_names("NativeBarrier").len(), + 3, + "Please update new extrinsic here." + ); + + let call = crate::RuntimeCall::NativeBarrier( + pallet_native_barrier::Call::initialize_native_barrier { init: None }, + ); + let (dispatch_info, call_len) = get_call_details(&call); + calamari_runtime_calls.push(( + "pallet_native_barrier", + "initialize_native_barrier", + dispatch_info, + call_len, + )); + + let call = crate::RuntimeCall::NativeBarrier( + pallet_native_barrier::Call::add_accounts_to_native_barrier { accounts: vec![] }, + ); + let (dispatch_info, call_len) = get_call_details(&call); + calamari_runtime_calls.push(( + "pallet_native_barrier", + "add_accounts_to_native_barrier", + dispatch_info, + call_len, + )); + + let call = crate::RuntimeCall::NativeBarrier( + pallet_native_barrier::Call::remove_accounts_from_native_barrier { accounts: vec![] }, + ); + let (dispatch_info, call_len) = get_call_details(&call); + calamari_runtime_calls.push(( + "pallet_native_barrier", + "remove_accounts_from_native_barrier", + dispatch_info, + call_len, + )); + } + (calamari_runtime_calls, t) } diff --git a/runtime/calamari/src/lib.rs b/runtime/calamari/src/lib.rs index 37fb1fc44..bd81c233d 100644 --- a/runtime/calamari/src/lib.rs +++ b/runtime/calamari/src/lib.rs @@ -451,6 +451,14 @@ impl pallet_balances::Config for Runtime { type ExistentialDeposit = NativeTokenExistentialDeposit; type AccountStore = frame_system::Pallet; type WeightInfo = weights::pallet_balances::SubstrateWeight; + type NativeBarrierType = NativeBarrier; +} + +impl pallet_native_barrier::Config for Runtime { + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type UnixTime = Timestamp; + type WeightInfo = weights::pallet_native_barrier::SubstrateWeight; } parameter_types! { @@ -960,6 +968,7 @@ construct_runtime!( CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin} = 32, DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 33, XTokens: orml_xtokens::{Pallet, Call, Event, Storage} = 34, + NativeBarrier: pallet_native_barrier::{Pallet, Call, Event, Storage} = 35, // Handy utilities. Utility: pallet_utility::{Pallet, Call, Event} = 40, @@ -1055,6 +1064,7 @@ mod benches { [pallet_manta_pay, MantaPay] [pallet_manta_sbt, MantaSbt] [pallet_name_service, NameService] + [pallet_native_barrier, NativeBarrier] // Dex [zenlink_protocol, ZenlinkProtocol] [pallet_farming, Farming] @@ -1331,11 +1341,11 @@ impl_runtime_apis! { impl pallet_farming_rpc_runtime_api::FarmingRuntimeApi for Runtime { fn get_farming_rewards(who: AccountId, pid: PoolId) -> Vec<(CalamariAssetId, Balance)> { - Farming::get_farming_rewards(&who, pid).unwrap_or(Vec::new()) + Farming::get_farming_rewards(&who, pid).unwrap_or_default() } fn get_gauge_rewards(who: AccountId, pid: PoolId) -> Vec<(CalamariAssetId, Balance)> { - Farming::get_gauge_rewards(&who, pid).unwrap_or(Vec::new()) + Farming::get_gauge_rewards(&who, pid).unwrap_or_default() } } diff --git a/runtime/calamari/src/weights/mod.rs b/runtime/calamari/src/weights/mod.rs index 0bdf918f6..11f19c0cf 100644 --- a/runtime/calamari/src/weights/mod.rs +++ b/runtime/calamari/src/weights/mod.rs @@ -33,6 +33,7 @@ pub mod pallet_manta_sbt; pub mod pallet_membership; pub mod pallet_multisig; pub mod pallet_name_service; +pub mod pallet_native_barrier; pub mod pallet_parachain_staking; pub mod pallet_preimage; pub mod pallet_randomness; diff --git a/runtime/calamari/src/weights/pallet_native_barrier.rs b/runtime/calamari/src/weights/pallet_native_barrier.rs new file mode 100644 index 000000000..f79e13b5d --- /dev/null +++ b/runtime/calamari/src/weights/pallet_native_barrier.rs @@ -0,0 +1,122 @@ +// Copyright 2020-2023 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . + +//! Autogenerated weights for pallet_native_barrier +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-08-30, STEPS: `50`, REPEAT: 40, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("manta-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/manta +// benchmark +// pallet +// --chain=manta-dev +// --steps=50 +// --repeat=40 +// --pallet=pallet_native_barrier +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./scripts/benchmarking/frame-weights-output/pallet_native_barrier.rs +// --template=.github/resources/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(clippy::unnecessary_cast)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; +use manta_primitives::constants::RocksDbWeight; + +/// Weight functions needed for pallet_native_barrier. +pub trait WeightInfo { + fn on_initialize() -> Weight; + fn initialize_native_barrier() -> Weight; + fn add_accounts_to_native_barrier() -> Weight; + fn remove_accounts_from_native_barrier() -> Weight; +} + +/// Weights for pallet_native_barrier using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_native_barrier::WeightInfo for SubstrateWeight { + // Storage: NativeBarrier Configurations (r:1 w:0) + // Storage: Timestamp Now (r:1 w:0) + // Storage: NativeBarrier LastDayProcessed (r:1 w:0) + fn on_initialize() -> Weight { + // Minimum execution time: 8_005 nanoseconds. + Weight::from_ref_time(8_346_000) + .saturating_add(T::DbWeight::get().reads(3)) + } + // Storage: NativeBarrier LastDayProcessed (r:1 w:1) + // Storage: NativeBarrier Configurations (r:0 w:1) + fn initialize_native_barrier() -> Weight { + // Minimum execution time: 37_480 nanoseconds. + Weight::from_ref_time(38_803_000) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: NativeBarrier Configurations (r:1 w:0) + // Storage: NativeBarrier RemainingLimit (r:5 w:5) + fn add_accounts_to_native_barrier() -> Weight { + // Minimum execution time: 27_011 nanoseconds. + Weight::from_ref_time(27_622_000) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(5)) + } + // Storage: NativeBarrier RemainingLimit (r:0 w:5) + fn remove_accounts_from_native_barrier() -> Weight { + // Minimum execution time: 17_994 nanoseconds. + Weight::from_ref_time(18_445_000) + .saturating_add(T::DbWeight::get().writes(5)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: NativeBarrier Configurations (r:1 w:0) + // Storage: Timestamp Now (r:1 w:0) + // Storage: NativeBarrier LastDayProcessed (r:1 w:0) + fn on_initialize() -> Weight { + // Minimum execution time: 8_005 nanoseconds. + Weight::from_ref_time(8_346_000) + .saturating_add(RocksDbWeight::get().reads(3)) + } + // Storage: NativeBarrier LastDayProcessed (r:1 w:1) + // Storage: NativeBarrier Configurations (r:0 w:1) + fn initialize_native_barrier() -> Weight { + // Minimum execution time: 37_480 nanoseconds. + Weight::from_ref_time(38_803_000) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(2)) + } + // Storage: NativeBarrier Configurations (r:1 w:0) + // Storage: NativeBarrier RemainingLimit (r:5 w:5) + fn add_accounts_to_native_barrier() -> Weight { + // Minimum execution time: 27_011 nanoseconds. + Weight::from_ref_time(27_622_000) + .saturating_add(RocksDbWeight::get().reads(6)) + .saturating_add(RocksDbWeight::get().writes(5)) + } + // Storage: NativeBarrier RemainingLimit (r:0 w:5) + fn remove_accounts_from_native_barrier() -> Weight { + // Minimum execution time: 17_994 nanoseconds. + Weight::from_ref_time(18_445_000) + .saturating_add(RocksDbWeight::get().writes(5)) + } +} diff --git a/runtime/calamari/src/xcm_config.rs b/runtime/calamari/src/xcm_config.rs index 0e414c51c..2721e487b 100644 --- a/runtime/calamari/src/xcm_config.rs +++ b/runtime/calamari/src/xcm_config.rs @@ -15,9 +15,10 @@ // along with Manta. If not, see . use super::{ - assets_config::CalamariAssetConfig, AssetManager, Assets, Balances, DmpQueue, - EnsureRootOrMoreThanHalfCouncil, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, - RuntimeCall, RuntimeEvent, RuntimeOrigin, Treasury, XcmpQueue, MAXIMUM_BLOCK_WEIGHT, + assets_config::{CalamariAssetConfig, NativeAssetLocation}, + AssetManager, Assets, Balances, DmpQueue, EnsureRootOrMoreThanHalfCouncil, NativeBarrier, + ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, + Treasury, XcmpQueue, MAXIMUM_BLOCK_WEIGHT, }; use codec::{Decode, Encode}; use core::marker::PhantomData; @@ -347,6 +348,16 @@ impl Contains for AssetManager { } } +impl orml_traits::native_barrier::NativeChecker for NativeBarrier { + fn is_native(currency_id: &CurrencyId) -> bool { + let asset_location = + CurrencyIdtoMultiLocation::>::convert( + currency_id.clone(), + ); + asset_location == NativeAssetLocation::get().into() || asset_location == Some(Here.into()) + } +} + // The XCM message wrapper wrapper impl orml_xtokens::Config for Runtime { type RuntimeEvent = RuntimeEvent; @@ -375,5 +386,6 @@ impl orml_xtokens::Config for Runtime { type MinXcmFee = AssetManager; type MultiLocationsFilter = AssetManager; type OutgoingAssetsFilter = AssetManager; + type NativeBarrierType = NativeBarrier; type ReserveProvider = AbsoluteReserveProvider; } diff --git a/runtime/calamari/tx-fees-data/4.4.0-tx-fees.csv b/runtime/calamari/tx-fees-data/4.4.0-tx-fees.csv new file mode 100644 index 000000000..eba00f5d4 --- /dev/null +++ b/runtime/calamari/tx-fees-data/4.4.0-tx-fees.csv @@ -0,0 +1,736 @@ +module,extrinsic,tx_fee_with_decimal,tx_fee_without_decimal,fee_multiplier +frame_system,remark-length=32,495236584103,0.495236584103,0.0002 +frame_system,remark-length=64,495236584103,0.495236584103,0.0002 +frame_system,set_heap_pages,495221061000,0.495221061,0.0002 +frame_system,set_code,995220000000,0.99522,0.0002 +frame_system,set_code_without_checks,995220000000,0.99522,0.0002 +frame_system,set_storage,495668341581,0.495668341581,0.0002 +frame_system,kill_storage,495765187030,0.49576518703,0.0002 +frame_system,kill_prefix,496188802074,0.496188802074,0.0002 +frame_system,remark_with_event-length=32,495233714184,0.495233714184,0.0002 +frame_system,remark_with_event-length=64,495233714184,0.495233714184,0.0002 +pallet_treasury,propose_spend,495487955000,0.495487955,0.0002 +pallet_treasury,reject_proposal,495200370000,0.49520037,0.0002 +pallet_treasury,approve_proposal,495075579757,0.495075579757,0.0002 +pallet_treasury,spend,495230174000,0.495230174,0.0002 +pallet_treasury,remove_approval,495035698000,0.495035698,0.0002 +pallet_timestamp,set,495036265000,0.495036265,0.0002 +pallet_preimage,note_preimage-length=32,495478847784,0.495478847784,0.0002 +pallet_preimage,note_preimage-length=32,495808942568,0.495808942568,0.0002 +pallet_preimage,unnote_preimage,495479954000,0.495479954,0.0002 +pallet_preimage,request_preimage,495365568000,0.495365568,0.0002 +pallet_preimage,unrequest_preimage,495465126000,0.495465126,0.0002 +pallet_multisig,as_multi_threshold_1,495959895248,0.495959895248,0.0002 +pallet_multisig,as_multi,495904049703,0.495904049703,0.0002 +pallet_multisig,approve_as_multi,495778813262,0.495778813262,0.0002 +pallet_multisig,cancel_as_multi,495803345841,0.495803345841,0.0002 +pallet_membership,add_member,495270000000,0.49527,0.0002 +pallet_membership,remove_member,495270000000,0.49527,0.0002 +pallet_membership,swap_member,495600000000,0.4956,0.0002 +pallet_membership,reset_members,495590000000,0.49559,0.0002 +pallet_membership,change_key,495270000000,0.49527,0.0002 +pallet_membership,set_prime,495270000000,0.49527,0.0002 +pallet_membership,clear_prime,494940000000,0.49494,0.0002 +pallet_democracy,propose,495378902000,0.495378902,0.0002 +pallet_democracy,second,495072267000,0.495072267,0.0002 +pallet_democracy,vote,495518381000,0.495518381,0.0002 +pallet_democracy,emergency_cancel,495203577000,0.495203577,0.0002 +pallet_democracy,external_propose,495109374000,0.495109374,0.0002 +pallet_democracy,external_propose_majority,495045715000,0.495045715,0.0002 +pallet_democracy,external_propose_default,495045725000,0.495045725,0.0002 +pallet_democracy,fast_track,495683337000,0.495683337,0.0002 +pallet_democracy,veto_external,495491460000,0.49549146,0.0002 +pallet_democracy,cancel_referendum,495015367000,0.495015367,0.0002 +pallet_democracy,delegate,508835450468,0.508835450468,0.0002 +pallet_democracy,undelegate,508059878865,0.508059878865,0.0002 +pallet_democracy,clear_public_proposals,494995711000,0.494995711,0.0002 +pallet_democracy,unlock,495642516193,0.495642516193,0.0002 +pallet_democracy,remove_vote,495214206442,0.495214206442,0.0002 +pallet_democracy,remove_other_vote,495544218326,0.495544218326,0.0002 +pallet_democracy,blacklist,496074355000,0.496074355,0.0002 +pallet_democracy,cancel_proposal,495350354000,0.495350354,0.0002 +pallet_collective,set_members,509583916204,0.509583916204,0.0002 +pallet_collective,execute,495428914103,0.495428914103,0.0002 +pallet_collective,propose,495492248312,0.495492248312,0.0002 +pallet_collective,vote,495421021218,0.495421021218,0.0002 +pallet_collective,close,495746009966,0.495746009966,0.0002 +pallet_collective,disapprove_proposal,495575869517,0.495575869517,0.0002 +cumulus_pallet_xcmp_queue,service_overweight,495051000064,0.495051000064,0.0002 +cumulus_pallet_xcmp_queue,suspend_xcm_execution,494990000000,0.49499,0.0002 +cumulus_pallet_xcmp_queue,resume_xcm_execution,494990000000,0.49499,0.0002 +cumulus_pallet_xcmp_queue,update_suspend_threshold,495062837000,0.495062837,0.0002 +cumulus_pallet_xcmp_queue,update_drop_threshold,495062837000,0.495062837,0.0002 +cumulus_pallet_xcmp_queue,update_resume_threshold,495062837000,0.495062837,0.0002 +cumulus_pallet_xcmp_queue,update_threshold_weight,495102783000,0.495102783,0.0002 +cumulus_pallet_xcmp_queue,update_weight_restrict_decay,495102783000,0.495102783,0.0002 +cumulus_pallet_xcmp_queue,update_xcmp_max_individual_weight,495102783000,0.495102783,0.0002 +orml_xtokens,transfer,495260000000,0.49526,0.0002 +orml_xtokens,transfer_multiasset,495070000000,0.49507,0.0002 +orml_xtokens,transfer_with_fee,495420000000,0.49542,0.0002 +orml_xtokens,transfer_multiasset_with_fee,495210000000,0.49521,0.0002 +orml_xtokens,transfer_multicurrencies,495640000000,0.49564,0.0002 +orml_xtokens,transfer_multiassets,495120000000,0.49512,0.0002 +calamari_vesting,vest,495362893000,0.495362893,0.0002 +manta_collator_selection,set_invulnerables,495334837782,0.495334837782,0.0002 +manta_collator_selection,set_desired_candidates,495046158000,0.495046158,0.0002 +manta_collator_selection,set_candidacy_bond,495164180000,0.49516418,0.0002 +manta_collator_selection,register_as_candidate,495179221570,0.49517922157,0.0002 +manta_collator_selection,register_candidate,495499796707,0.495499796707,0.0002 +manta_collator_selection,leave_intent,495060413062,0.495060413062,0.0002 +manta_collator_selection,remove_collator,495409516018,0.495409516018,0.0002 +manta_collator_selection,set_eviction_baseline,495013685000,0.495013685,0.0002 +manta_collator_selection,set_eviction_tolerance,495040747000,0.495040747,0.0002 +pallet_asset_manager,register_asset,495989552000,0.495989552,0.0002 +pallet_asset_manager,update_asset_location,495626475000,0.495626475,0.0002 +pallet_asset_manager,update_asset_metadata,495728106000,0.495728106,0.0002 +pallet_asset_manager,set_units_per_second,495098556000,0.495098556,0.0002 +pallet_asset_manager,mint_asset,495759826000,0.495759826,0.0002 +pallet_asset_manager,set_min_xcm_fee,495081065000,0.495081065,0.0002 +pallet_asset_manager,update_outgoing_filtered_assets,495081519000,0.495081519,0.0002 +pallet_asset_manager,register_lp_asset,496336198000,0.496336198,0.0002 +pallet_asset_manager,permissionless_register_asset,495903605000,0.495903605,0.0002 +pallet_assets,create,495686405000,0.495686405,0.0002 +pallet_assets,force_create,495545322000,0.495545322,0.0002 +pallet_assets,start_destroy,495197486000,0.495197486,0.0002 +pallet_assets,destroy_accounts,761139518000,0.761139518,0.0002 +pallet_assets,destroy_approvals,628394183000,0.628394183,0.0002 +pallet_assets,finish_destroy,495222391000,0.495222391,0.0002 +pallet_assets,mint,495677658000,0.495677658,0.0002 +pallet_assets,burn,495699077000,0.495699077,0.0002 +pallet_assets,transfer,495946472000,0.495946472,0.0002 +pallet_assets,transfer_keep_alive,495940254000,0.495940254,0.0002 +pallet_assets,force_transfer,496280000000,0.49628,0.0002 +pallet_assets,freeze,495558178000,0.495558178,0.0002 +pallet_assets,thaw,495557470000,0.49555747,0.0002 +pallet_assets,freeze_asset,495197176000,0.495197176,0.0002 +pallet_assets,thaw_asset,495196838000,0.495196838,0.0002 +pallet_assets,transfer_ownership,495554006000,0.495554006,0.0002 +pallet_assets,set_team,496186200000,0.4961862,0.0002 +pallet_assets,set_metadata,495895848933,0.495895848933,0.0002 +pallet_assets,clear_metadata,495224987000,0.495224987,0.0002 +pallet_assets,force_set_metadata,495905080757,0.495905080757,0.0002 +pallet_assets,force_clear_metadata,495226169000,0.495226169,0.0002 +pallet_assets,force_asset_status,496545769000,0.496545769,0.0002 +pallet_assets,approve_transfer,495667409000,0.495667409,0.0002 +pallet_assets,cancel_approval,495661896000,0.495661896,0.0002 +pallet_assets,force_cancel_approval,495990207000,0.495990207,0.0002 +pallet_assets,transfer_approved,496412932000,0.496412932,0.0002 +pallet_assets,touch,495337658000,0.495337658,0.0002 +pallet_assets,refund,495347658000,0.495347658,0.0002 +pallet_author_inherent,kick_off_authorship_validation,495108286000,0.495108286,0.0002 +pallet_balances,transfer-1,495404145000,0.495404145,0.0002 +pallet_balances,transfer-1000_000,495434145000,0.495434145,0.0002 +pallet_balances,set_balance,495393053000,0.495393053,0.0002 +pallet_balances,force_transfer,495859215000,0.495859215,0.0002 +pallet_balances,transfer_keep_alive-1,495391952000,0.495391952,0.0002 +pallet_balances,transfer_keep_alive-1000_000,495421952000,0.495421952,0.0002 +pallet_balances,transfer_all,495398137000,0.495398137,0.0002 +pallet_balances,force_unreserve,495527173000,0.495527173,0.0002 +pallet_manta_pay,to_private,536744552000,0.536744552,0.0002 +pallet_manta_pay,private_transfer,568451354000,0.568451354,0.0002 +pallet_manta_pay,to_public,550273559000,0.550273559,0.0002 +pallet_manta_pay,public_transfer,496119832000,0.496119832,0.0002 +pallet_manta_sbt,to_private,536298839000,0.536298839,0.0002 +pallet_manta_sbt,force_to_private,536472517000,0.536472517,0.0002 +pallet_manta_sbt,reserve_sbt,495374480000,0.49537448,0.0002 +pallet_manta_sbt,allowlist_evm_account,495486961000,0.495486961,0.0002 +pallet_manta_sbt,set_next_sbt_id,495176150000,0.49517615,0.0002 +pallet_manta_sbt,remove_allowlist_evm_account,495248155000,0.495248155,0.0002 +pallet_manta_sbt,change_free_reserve_account,495340832000,0.495340832,0.0002 +pallet_manta_sbt,force_mint_sbt_eth,537142245000,0.537142245,0.0002 +pallet_manta_sbt,change_force_account,495353327000,0.495353327,0.0002 +pallet_manta_sbt,mint_sbt_eth,537431745000,0.537431745,0.0002 +pallet_manta_sbt,change_allowlist_account,495336498000,0.495336498,0.0002 +pallet_manta_sbt,update_mint_info,495288604000,0.495288604,0.0002 +pallet_manta_sbt,new_mint_info,495345001000,0.495345001,0.0002 +pallet_parachain_staking,set_staking_expectations,495516633000,0.495516633,0.0002 +pallet_parachain_staking,set_inflation,495203366000,0.495203366,0.0002 +pallet_parachain_staking,set_parachain_bond_account,495356305000,0.495356305,0.0002 +pallet_parachain_staking,set_parachain_bond_account,495356305000,0.495356305,0.0002 +pallet_parachain_staking,set_parachain_bond_reserve_percent,495045931000,0.495045931,0.0002 +pallet_parachain_staking,set_total_selected,495047483000,0.495047483,0.0002 +pallet_parachain_staking,set_collator_commission,495044417000,0.495044417,0.0002 +pallet_parachain_staking,set_blocks_per_round,495122854000,0.495122854,0.0002 +pallet_parachain_staking,join_candidates,495854275235,0.495854275235,0.0002 +pallet_parachain_staking,schedule_leave_candidates,495188345951,0.495188345951,0.0002 +pallet_parachain_staking,execute_leave_candidates,496614827404,0.496614827404,0.0002 +pallet_parachain_staking,cancel_leave_candidates,495185953459,0.495185953459,0.0002 +pallet_parachain_staking,go_offline,495170264000,0.495170264,0.0002 +pallet_parachain_staking,go_offline,495170264000,0.495170264,0.0002 +pallet_parachain_staking,candidate_bond_more,495578456000,0.495578456,0.0002 +pallet_parachain_staking,schedule_candidate_bond_less,495204067000,0.495204067,0.0002 +pallet_parachain_staking,execute_candidate_bond_less,495898385000,0.495898385,0.0002 +pallet_parachain_staking,cancel_candidate_bond_less,495041928000,0.495041928,0.0002 +pallet_parachain_staking,delegate_1_2_3,496195737539,0.496195737539,0.0002 +pallet_parachain_staking,delegate_1_25_3,496203015429,0.496203015429,0.0002 +pallet_parachain_staking,delegate_1_2_100,496222335403,0.496222335403,0.0002 +pallet_parachain_staking,schedule_leave_delegators,495173383000,0.495173383,0.0002 +pallet_parachain_staking,execute_leave_delegators,496707467185,0.496707467185,0.0002 +pallet_parachain_staking,cancel_leave_delegators,495173693000,0.495173693,0.0002 +pallet_parachain_staking,schedule_revoke_delegation,495493589000,0.495493589,0.0002 +pallet_parachain_staking,delegator_bond_more,496195289000,0.496195289,0.0002 +pallet_parachain_staking,schedule_delegator_bond_less,495504323000,0.495504323,0.0002 +pallet_parachain_staking,execute_delegation_request,496615390000,0.49661539,0.0002 +pallet_parachain_staking,cancel_delegation_request,495498232000,0.495498232,0.0002 +pallet_scheduler,cancel,495290142096,0.495290142096,0.0002 +pallet_scheduler,schedule,495239803891,0.495239803891,0.0002 +pallet_scheduler,schedule_named,495771016738,0.495771016738,0.0002 +pallet_scheduler,cancel_named,495558185578,0.495558185578,0.0002 +pallet_scheduler,schedule_after,495479803891,0.495479803891,0.0002 +pallet_scheduler,schedule_named_after,496011016738,0.496011016738,0.0002 +pallet_session,set_keys,496713201000,0.496713201,0.0002 +pallet_session,purge_keys,495340334000,0.495340334,0.0002 +pallet_tx_pause,pause_transaction,495707235000,0.495707235,0.0002 +pallet_tx_pause,unpause_transaction,495698948000,0.495698948,0.0002 +pallet_tx_pause,pause_transactions,496214470000,0.49621447,0.0002 +pallet_tx_pause,unpause_transactions,496197896000,0.496197896,0.0002 +pallet_tx_pause,pause_pallets,503421750000,0.50342175,0.0002 +pallet_tx_pause,unpause_pallets,503421750000,0.50342175,0.0002 +pallet_utility,batch-size=1,495001986439,0.495001986439,0.0002 +pallet_utility,batch-size=32,497125684188,0.497125684188,0.0002 +pallet_utility,as_derivative,497298281188,0.497298281188,0.0002 +pallet_utility,batch_all-size=1,494998351038,0.494998351038,0.0002 +pallet_utility,batch_all-size=32,497009351356,0.497009351356,0.0002 +pallet_utility,dispatch_as,497385768356,0.497385768356,0.0002 +pallet_utility,force_batch,494925118410,0.49492511841,0.0002 +pallet_randomness,set_babe_randomness_results,495284503000,0.495284503,0.0002 +pallet_name_service,register,495586762000,0.495586762,0.0002 +pallet_name_service,accept_register,495512388000,0.495512388,0.0002 +pallet_name_service,set_primary_name,495438492000,0.495438492,0.0002 +pallet_name_service,cancel_pending_register,495409747000,0.495409747,0.0002 +pallet_name_service,remove_register,495437675000,0.495437675,0.0002 +pallet_farming,create_farming_pool,496515458000,0.496515458,0.0002 +pallet_farming,charge,496389696000,0.496389696,0.0002 +pallet_farming,deposit,496105501000,0.496105501,0.0002 +pallet_farming,withdraw,495508597000,0.495508597,0.0002 +pallet_farming,claim,495366723000,0.495366723,0.0002 +pallet_farming,withdraw_claim,495366723000,0.495366723,0.0002 +pallet_farming,close_pool,495050001000,0.495050001,0.0002 +pallet_farming,set_retire_limit,494930001000,0.494930001,0.0002 +pallet_farming,retire_pool,495050001000,0.495050001,0.0002 +pallet_farming,reset_pool,496060001000,0.496060001,0.0002 +pallet_farming,kill_pool,495050001000,0.495050001,0.0002 +pallet_farming,edit_pool,496490001000,0.496490001,0.0002 +pallet_farming,gauge_withdraw,495395220000,0.49539522,0.0002 +pallet_farming,force_gauge_claim,495050001000,0.495050001,0.0002 +pallet_lottery,deposit,497127379480,0.49712737948,0.0002 +pallet_lottery,request_withdraw,496394120403,0.496394120403,0.0002 +pallet_lottery,claim_my_winnings,495470892425,0.495470892425,0.0002 +pallet_lottery,rebalance_stake,494890000000,0.49489,0.0002 +pallet_lottery,start_lottery,495311718000,0.495311718,0.0002 +pallet_lottery,stop_lottery,495171919000,0.495171919,0.0002 +pallet_lottery,draw_lottery,495296111000,0.495296111,0.0002 +pallet_lottery,process_matured_withdrawals,495007101000,0.495007101,0.0002 +pallet_lottery,liquidate_lottery,494890000000,0.49489,0.0002 +pallet_lottery,set_min_deposit,495184766000,0.495184766,0.0002 +pallet_lottery,set_min_withdraw,495155916000,0.495155916,0.0002 +pallet_lottery,set_gas_reserve,495155750000,0.49515575,0.0002 +zenlink_protocol,set_fee_receiver,495363111000,0.495363111,0.0002 +zenlink_protocol,set_fee_point,495036392000,0.495036392,0.0002 +zenlink_protocol,transfer,495351000000,0.495351,0.0002 +zenlink_protocol,transfer,495457424000,0.495457424,0.0002 +zenlink_protocol,add_liquidity,498968018000,0.498968018,0.0002 +zenlink_protocol,remove_liquidity,497562898000,0.497562898,0.0002 +zenlink_protocol,swap_exact_assets_for_assets,496570262000,0.496570262,0.0002 +zenlink_protocol,swap_assets_for_exact_assets,496570543000,0.496570543,0.0002 +zenlink_protocol,bootstrap_create,496490665000,0.496490665,0.0002 +zenlink_protocol,bootstrap_contribute,496071203000,0.496071203,0.0002 +zenlink_protocol,bootstrap_claim,498344570000,0.49834457,0.0002 +zenlink_protocol,bootstrap_end,499159387000,0.499159387,0.0002 +zenlink_protocol,bootstrap_update,496532242000,0.496532242,0.0002 +zenlink_protocol,bootstrap_refund,495995301000,0.495995301,0.0002 +zenlink_protocol,bootstrap_charge_reward,495840000000,0.49584,0.0002 +zenlink_protocol,bootstrap_withdraw_reward,495570000000,0.49557,0.0002 +pallet_native_barrier,initialize_native_barrier,1095014678000,0.495014678,0.0002 +pallet_native_barrier,add_accounts_to_native_barrier,495576570000,0.49557657,0.0002 +pallet_native_barrier,remove_accounts_from_native_barrier,495419336000,0.495419336,0.0002 +frame_system,remark-length=32,511804103000,0.511804103,0.2 +frame_system,remark-length=64,511804103000,0.511804103,0.2 +frame_system,set_heap_pages,746031000000,0.746031,0.2 +frame_system,set_code,500495220000000,500.49521999999996,0.2 +frame_system,set_code_without_checks,500495220000000,500.49521999999996,0.2 +frame_system,set_storage,603901581000,0.603901581,0.2 +frame_system,kill_storage,700747030000,0.70074703,0.2 +frame_system,kill_prefix,1424062074000,1.424062074,0.2 +frame_system,remark_with_event-length=32,508934184000,0.508934184,0.2 +frame_system,remark_with_event-length=64,508934184000,0.508934184,0.2 +pallet_treasury,propose_spend,753185000000,0.753185,0.2 +pallet_treasury,reject_proposal,795270000000,0.79527,0.2 +pallet_treasury,approve_proposal,670479757000,0.670479757,0.2 +pallet_treasury,spend,495404000000,0.495404,0.2 +pallet_treasury,remove_approval,630598000000,0.630598,0.2 +pallet_timestamp,set,631165000000,0.631165,0.2 +pallet_preimage,note_preimage-length=32,754067784000,0.754067784,0.2 +pallet_preimage,note_preimage-length=32,754492568000,0.754492568,0.2 +pallet_preimage,unnote_preimage,765164000000,0.765164,0.2 +pallet_preimage,request_preimage,650778000000,0.650778,0.2 +pallet_preimage,unrequest_preimage,750336000000,0.750336,0.2 +pallet_multisig,as_multi_threshold_1,895455248000,0.895455248,0.2 +pallet_multisig,as_multi,789659703000,0.789659703,0.2 +pallet_multisig,approve_as_multi,684403262000,0.684403262,0.2 +pallet_multisig,cancel_as_multi,658985841000,0.658985841,0.2 +pallet_membership,add_member,545220000000,0.54522,0.2 +pallet_membership,remove_member,545220000000,0.54522,0.2 +pallet_membership,swap_member,545550000000,0.54555,0.2 +pallet_membership,reset_members,545540000000,0.54554,0.2 +pallet_membership,change_key,545220000000,0.54522,0.2 +pallet_membership,set_prime,545220000000,0.54522,0.2 +pallet_membership,clear_prime,544890000000,0.54489,0.2 +pallet_democracy,propose,923852000000,0.923852,0.2 +pallet_democracy,second,667167000000,0.667167,0.2 +pallet_democracy,vote,933461000000,0.933461,0.2 +pallet_democracy,emergency_cancel,768507000000,0.768507,0.2 +pallet_democracy,external_propose,664314000000,0.664314,0.2 +pallet_democracy,external_propose_majority,600655000000,0.600655,0.2 +pallet_democracy,external_propose_default,600665000000,0.600665,0.2 +pallet_democracy,fast_track,888627000000,0.888627,0.2 +pallet_democracy,veto_external,776670000000,0.77667,0.2 +pallet_democracy,cancel_referendum,610267000000,0.610267,0.2 +pallet_democracy,delegate,13940840468000,13.940840468,0.2 +pallet_democracy,undelegate,13664768865000,13.664768865,0.2 +pallet_democracy,clear_public_proposals,600601000000,0.600601,0.2 +pallet_democracy,unlock,917736193000,0.917736193,0.2 +pallet_democracy,remove_vote,779136442000,0.779136442,0.2 +pallet_democracy,remove_other_vote,779478326000,0.779478326,0.2 +pallet_democracy,blacklist,1309615000000,1.309615,0.2 +pallet_democracy,cancel_proposal,945254000000,0.945254,0.2 +pallet_collective,set_members,14169826204000,14.169826204,0.2 +pallet_collective,execute,993844103000,0.993844103,0.2 +pallet_collective,propose,1047188312000,1.047188312,0.2 +pallet_collective,vote,686251218000,0.686251218,0.2 +pallet_collective,close,991259966000,0.991259966,0.2 +pallet_collective,disapprove_proposal,861079517000,0.861079517,0.2 +cumulus_pallet_xcmp_queue,service_overweight,496050064000,0.496050064,0.2 +cumulus_pallet_xcmp_queue,suspend_xcm_execution,594890000000,0.59489,0.2 +cumulus_pallet_xcmp_queue,resume_xcm_execution,594890000000,0.59489,0.2 +cumulus_pallet_xcmp_queue,update_suspend_threshold,627767000000,0.627767,0.2 +cumulus_pallet_xcmp_queue,update_drop_threshold,627767000000,0.627767,0.2 +cumulus_pallet_xcmp_queue,update_resume_threshold,627767000000,0.627767,0.2 +cumulus_pallet_xcmp_queue,update_threshold_weight,627753000000,0.627753,0.2 +cumulus_pallet_xcmp_queue,update_weight_restrict_decay,627753000000,0.627753,0.2 +cumulus_pallet_xcmp_queue,update_xcmp_max_individual_weight,627753000000,0.627753,0.2 +orml_xtokens,transfer,495260000000,0.49526,0.2 +orml_xtokens,transfer_multiasset,495070000000,0.49507,0.2 +orml_xtokens,transfer_with_fee,495420000000,0.49542,0.2 +orml_xtokens,transfer_multiasset_with_fee,495210000000,0.49521,0.2 +orml_xtokens,transfer_multicurrencies,495640000000,0.49564,0.2 +orml_xtokens,transfer_multiassets,495120000000,0.49512,0.2 +calamari_vesting,vest,967783000000,0.967783,0.2 +manta_collator_selection,set_invulnerables,610057782000,0.610057782,0.2 +manta_collator_selection,set_desired_candidates,611088000000,0.611088,0.2 +manta_collator_selection,set_candidacy_bond,609230000000,0.60923,0.2 +manta_collator_selection,register_as_candidate,784111570000,0.78411157,0.2 +manta_collator_selection,register_candidate,785006707000,0.785006707,0.2 +manta_collator_selection,leave_intent,665303062000,0.665303062,0.2 +manta_collator_selection,remove_collator,694726018000,0.694726018,0.2 +manta_collator_selection,set_eviction_baseline,608585000000,0.608585,0.2 +manta_collator_selection,set_eviction_tolerance,635647000000,0.635647,0.2 +pallet_asset_manager,register_asset,1254782000000,1.254782,0.2 +pallet_asset_manager,update_asset_location,1191405000000,1.191405,0.2 +pallet_asset_manager,update_asset_metadata,863466000000,0.863466,0.2 +pallet_asset_manager,set_units_per_second,683466000000,0.683466,0.2 +pallet_asset_manager,mint_asset,875206000000,0.875206,0.2 +pallet_asset_manager,set_min_xcm_fee,645995000000,0.645995,0.2 +pallet_asset_manager,update_outgoing_filtered_assets,646449000000,0.646449,0.2 +pallet_asset_manager,register_lp_asset,1311718000000,1.311718,0.2 +pallet_asset_manager,permissionless_register_asset,1318685000000,1.318685,0.2 +pallet_assets,create,641945000000,0.641945,0.2 +pallet_assets,force_create,640722000000,0.640722,0.2 +pallet_assets,start_destroy,642536000000,0.642536,0.2 +pallet_assets,destroy_accounts,266584568000000,266.584568,0.2 +pallet_assets,destroy_approvals,133839233000000,133.839233,0.2 +pallet_assets,finish_destroy,667441000000,0.667441,0.2 +pallet_assets,mint,783048000000,0.783048,0.2 +pallet_assets,burn,804467000000,0.804467,0.2 +pallet_assets,transfer,1051862000000,1.051862,0.2 +pallet_assets,transfer_keep_alive,1045644000000,1.045644,0.2 +pallet_assets,force_transfer,1055720000000,1.05572,0.2 +pallet_assets,freeze,673558000000,0.673558,0.2 +pallet_assets,thaw,672850000000,0.67285,0.2 +pallet_assets,freeze_asset,642226000000,0.642226,0.2 +pallet_assets,thaw_asset,641888000000,0.641888,0.2 +pallet_assets,transfer_ownership,669386000000,0.669386,0.2 +pallet_assets,set_team,642240000000,0.64224,0.2 +pallet_assets,set_metadata,671568933000,0.671568933,0.2 +pallet_assets,clear_metadata,670037000000,0.670037,0.2 +pallet_assets,force_set_metadata,670810757000,0.670810757,0.2 +pallet_assets,force_clear_metadata,671219000000,0.671219,0.2 +pallet_assets,force_asset_status,642169000000,0.642169,0.2 +pallet_assets,approve_transfer,772799000000,0.772799,0.2 +pallet_assets,cancel_approval,777276000000,0.777276,0.2 +pallet_assets,force_cancel_approval,775917000000,0.775917,0.2 +pallet_assets,transfer_approved,1188652000000,1.188652,0.2 +pallet_assets,touch,782708000000,0.782708,0.2 +pallet_assets,refund,782718000000,0.782718,0.2 +pallet_author_inherent,kick_off_authorship_validation,713176000000,0.713176,0.2 +pallet_balances,transfer-1,669375000000,0.669375,0.2 +pallet_balances,transfer-1000_000,669405000000,0.669405,0.2 +pallet_balances,set_balance,648293000000,0.648293,0.2 +pallet_balances,force_transfer,794775000000,0.794775,0.2 +pallet_balances,transfer_keep_alive-1,657182000000,0.657182,0.2 +pallet_balances,transfer_keep_alive-1000_000,657212000000,0.657212,0.2 +pallet_balances,transfer_all,663367000000,0.663367,0.2 +pallet_balances,force_unreserve,642553000000,0.642553,0.2 +pallet_manta_pay,to_private,41000792000000,41.000792,0.2 +pallet_manta_pay,private_transfer,72707594000000,72.707594,0.2 +pallet_manta_pay,to_public,54529799000000,54.529799000000004,0.2 +pallet_manta_pay,public_transfer,925522000000,0.925522,0.2 +pallet_manta_sbt,to_private,40515119000000,40.515119000000006,0.2 +pallet_manta_sbt,force_to_private,40359127000000,40.359127,0.2 +pallet_manta_sbt,reserve_sbt,969380000000,0.96938,0.2 +pallet_manta_sbt,allowlist_evm_account,852091000000,0.852091,0.2 +pallet_manta_sbt,set_next_sbt_id,611210000000,0.61121,0.2 +pallet_manta_sbt,remove_allowlist_evm_account,613285000000,0.613285,0.2 +pallet_manta_sbt,change_free_reserve_account,616052000000,0.616052,0.2 +pallet_manta_sbt,force_mint_sbt_eth,40489395000000,40.489395,0.2 +pallet_manta_sbt,change_force_account,628547000000,0.628547,0.2 +pallet_manta_sbt,mint_sbt_eth,40569105000000,40.56910499999999,0.2 +pallet_manta_sbt,change_allowlist_account,611718000000,0.611718,0.2 +pallet_manta_sbt,update_mint_info,743644000000,0.743644,0.2 +pallet_manta_sbt,new_mint_info,840001000000,0.840001,0.2 +pallet_parachain_staking,set_staking_expectations,642003000000,0.642003,0.2 +pallet_parachain_staking,set_inflation,688376000000,0.688376,0.2 +pallet_parachain_staking,set_parachain_bond_account,641515000000,0.641515,0.2 +pallet_parachain_staking,set_parachain_bond_account,641515000000,0.641515,0.2 +pallet_parachain_staking,set_parachain_bond_reserve_percent,640831000000,0.640831,0.2 +pallet_parachain_staking,set_total_selected,642383000000,0.642383,0.2 +pallet_parachain_staking,set_collator_commission,639317000000,0.639317,0.2 +pallet_parachain_staking,set_blocks_per_round,717754000000,0.717754,0.2 +pallet_parachain_staking,join_candidates,1439185235000,1.439185235,0.2 +pallet_parachain_staking,schedule_leave_candidates,783245951000,0.783245951,0.2 +pallet_parachain_staking,execute_leave_candidates,1890047404000,1.890047404,0.2 +pallet_parachain_staking,cancel_leave_candidates,780853459000,0.780853459,0.2 +pallet_parachain_staking,go_offline,775154000000,0.775154,0.2 +pallet_parachain_staking,go_offline,775154000000,0.775154,0.2 +pallet_parachain_staking,candidate_bond_more,1173356000000,1.173356,0.2 +pallet_parachain_staking,schedule_candidate_bond_less,649117000000,0.649117,0.2 +pallet_parachain_staking,execute_candidate_bond_less,1183595000000,1.183595,0.2 +pallet_parachain_staking,cancel_candidate_bond_less,646818000000,0.646818,0.2 +pallet_parachain_staking,delegate_1_2_3,1450977539000,1.450977539,0.2 +pallet_parachain_staking,delegate_1_25_3,1458255429000,1.458255429,0.2 +pallet_parachain_staking,delegate_1_2_100,1467585403000,1.467585403,0.2 +pallet_parachain_staking,schedule_leave_delegators,778273000000,0.778273,0.2 +pallet_parachain_staking,execute_leave_delegators,1982687185000,1.982687185,0.2 +pallet_parachain_staking,cancel_leave_delegators,778583000000,0.778583,0.2 +pallet_parachain_staking,schedule_revoke_delegation,778799000000,0.778799,0.2 +pallet_parachain_staking,delegator_bond_more,1470509000000,1.470509,0.2 +pallet_parachain_staking,schedule_delegator_bond_less,779543000000,0.779543,0.2 +pallet_parachain_staking,execute_delegation_request,1580920000000,1.58092,0.2 +pallet_parachain_staking,cancel_delegation_request,783442000000,0.783442,0.2 +pallet_scheduler,cancel,815112096000,0.815112096,0.2 +pallet_scheduler,schedule,684853891000,0.684853891,0.2 +pallet_scheduler,schedule_named,816466738000,0.816466738,0.2 +pallet_scheduler,cancel_named,843395578000,0.843395578,0.2 +pallet_scheduler,schedule_after,685093891000,0.685093891,0.2 +pallet_scheduler,schedule_named_after,816706738000,0.816706738,0.2 +pallet_session,set_keys,1029381000000,1.029381,0.2 +pallet_session,purge_keys,945224000000,0.945224,0.2 +pallet_tx_pause,pause_transaction,652785000000,0.652785,0.2 +pallet_tx_pause,unpause_transaction,644498000000,0.644498,0.2 +pallet_tx_pause,pause_transactions,810370000000,0.81037,0.2 +pallet_tx_pause,unpause_transactions,793796000000,0.793796,0.2 +pallet_tx_pause,pause_pallets,8357310000000,8.35731,0.2 +pallet_tx_pause,unpause_pallets,8357310000000,8.35731,0.2 +pallet_utility,batch-size=1,566916439000,0.566916439,0.2 +pallet_utility,batch-size=32,1761544188000,1.761544188,0.2 +pallet_utility,as_derivative,1894181188000,1.894181188,0.2 +pallet_utility,batch_all-size=1,563281038000,0.563281038,0.2 +pallet_utility,batch_all-size=32,1645211356000,1.645211356,0.2 +pallet_utility,dispatch_as,1661988356000,1.661988356,0.2 +pallet_utility,force_batch,520018410000,0.52001841,0.2 +pallet_randomness,set_babe_randomness_results,889393000000,0.889393,0.2 +pallet_name_service,register,822022000000,0.822022,0.2 +pallet_name_service,accept_register,747648000000,0.747648,0.2 +pallet_name_service,set_primary_name,673752000000,0.673752,0.2 +pallet_name_service,cancel_pending_register,645007000000,0.645007,0.2 +pallet_name_service,remove_register,672935000000,0.672935,0.2 +pallet_farming,create_farming_pool,971498000000,0.971498,0.2 +pallet_farming,charge,1185396000000,1.185396,0.2 +pallet_farming,deposit,1320781000000,1.320781,0.2 +pallet_farming,withdraw,783817000000,0.783817,0.2 +pallet_farming,claim,811773000000,0.811773,0.2 +pallet_farming,withdraw_claim,811773000000,0.811773,0.2 +pallet_farming,close_pool,495051000000,0.495051,0.2 +pallet_farming,set_retire_limit,494931000000,0.494931,0.2 +pallet_farming,retire_pool,495051000000,0.495051,0.2 +pallet_farming,reset_pool,496061000000,0.496061,0.2 +pallet_farming,kill_pool,495051000000,0.495051,0.2 +pallet_farming,edit_pool,496491000000,0.496491,0.2 +pallet_farming,gauge_withdraw,840270000000,0.84027,0.2 +pallet_farming,force_gauge_claim,495051000000,0.495051,0.2 +pallet_lottery,deposit,2572429480000,2.57242948,0.2 +pallet_lottery,request_withdraw,1839170403000,1.839170403,0.2 +pallet_lottery,claim_my_winnings,1075782425000,1.075782425,0.2 +pallet_lottery,rebalance_stake,494890000000,0.49489,0.2 +pallet_lottery,start_lottery,916608000000,0.916608,0.2 +pallet_lottery,stop_lottery,776809000000,0.776809,0.2 +pallet_lottery,draw_lottery,901001000000,0.901001,0.2 +pallet_lottery,process_matured_withdrawals,611991000000,0.611991,0.2 +pallet_lottery,liquidate_lottery,494890000000,0.49489,0.2 +pallet_lottery,set_min_deposit,629816000000,0.629816,0.2 +pallet_lottery,set_min_withdraw,600966000000,0.600966,0.2 +pallet_lottery,set_gas_reserve,600800000000,0.6008,0.2 +zenlink_protocol,set_fee_receiver,638331000000,0.638331,0.2 +zenlink_protocol,set_fee_point,631292000000,0.631292,0.2 +zenlink_protocol,transfer,496350000000,0.49635,0.2 +zenlink_protocol,transfer,802574000000,0.802574,0.2 +zenlink_protocol,add_liquidity,4263218000000,4.263218,0.2 +zenlink_protocol,remove_liquidity,2548408000000,2.548408,0.2 +zenlink_protocol,swap_exact_assets_for_assets,1425902000000,1.425902,0.2 +zenlink_protocol,swap_assets_for_exact_assets,1426183000000,1.426183,0.2 +zenlink_protocol,bootstrap_create,846805000000,0.846805,0.2 +zenlink_protocol,bootstrap_contribute,1366403000000,1.366403,0.2 +zenlink_protocol,bootstrap_claim,3360050000000,3.36005,0.2 +zenlink_protocol,bootstrap_end,4504537000000,4.504537,0.2 +zenlink_protocol,bootstrap_update,878392000000,0.878392,0.2 +zenlink_protocol,bootstrap_refund,1340451000000,1.340451,0.2 +zenlink_protocol,bootstrap_charge_reward,595740000000,0.59574,0.2 +zenlink_protocol,bootstrap_withdraw_reward,595470000000,0.59547,0.2 +pallet_native_barrier,initialize_native_barrier,609578000000,0.609578,0.2 +pallet_native_barrier,add_accounts_to_native_barrier,1171470000000,1.17147,0.2 +pallet_native_barrier,remove_accounts_from_native_barrier,1014236000000,1.014236,0.2 +frame_system,remark-length=32,578140515000,0.578140515,1 +frame_system,remark-length=64,578140515000,0.578140515,1 +frame_system,set_heap_pages,1750275000000,1.750275,1 +frame_system,set_code,2500495220000000,2500.49522,1 +frame_system,set_code_without_checks,2500495220000000,2500.49522,1 +frame_system,set_storage,1037267905000,1.037267905,1 +frame_system,kill_storage,1521495150000,1.52149515,1 +frame_system,kill_prefix,5139270370000,5.13927037,1 +frame_system,remark_with_event-length=32,563790920000,0.56379092,1 +frame_system,remark_with_event-length=64,563790920000,0.56379092,1 +pallet_treasury,propose_spend,1785005000000,1.785005,1 +pallet_treasury,reject_proposal,1996750000000,1.99675,1 +pallet_treasury,approve_proposal,1372798785000,1.372798785,1 +pallet_treasury,spend,496100000000,0.4961,1 +pallet_treasury,remove_approval,1173390000000,1.17339,1 +pallet_timestamp,set,1176225000000,1.176225,1 +pallet_preimage,note_preimage-length=32,1789458920000,1.78945892,1 +pallet_preimage,note_preimage-length=32,1790262840000,1.79026284,1 +pallet_preimage,unnote_preimage,1844980000000,1.84498,1 +pallet_preimage,request_preimage,1273050000000,1.27305,1 +pallet_preimage,unrequest_preimage,1770840000000,1.77084,1 +pallet_multisig,as_multi_threshold_1,2495036240000,2.49503624,1 +pallet_multisig,as_multi,1965858515000,1.965858515,1 +pallet_multisig,approve_as_multi,1439656310000,1.43965631,1 +pallet_multisig,cancel_as_multi,1312369205000,1.312369205,1 +pallet_membership,add_member,745220000000,0.74522,1 +pallet_membership,remove_member,745220000000,0.74522,1 +pallet_membership,swap_member,745550000000,0.74555,1 +pallet_membership,reset_members,745540000000,0.74554,1 +pallet_membership,change_key,745220000000,0.74522,1 +pallet_membership,set_prime,745220000000,0.74522,1 +pallet_membership,clear_prime,744890000000,0.74489,1 +pallet_democracy,propose,2639460000000,2.63946,1 +pallet_democracy,second,1356235000000,1.356235,1 +pallet_democracy,vote,2686985000000,2.686985,1 +pallet_democracy,emergency_cancel,1862815000000,1.862815,1 +pallet_democracy,external_propose,1341810000000,1.34181,1 +pallet_democracy,external_propose_majority,1023515000000,1.023515,1 +pallet_democracy,external_propose_default,1023565000000,1.023565,1 +pallet_democracy,fast_track,2461975000000,2.461975,1 +pallet_democracy,veto_external,1902510000000,1.90251,1 +pallet_democracy,cancel_referendum,1071735000000,1.071735,1 +pallet_democracy,delegate,67722642340000,67.72264234,1 +pallet_democracy,undelegate,66344284325000,66.344284325,1 +pallet_democracy,clear_public_proposals,1023445000000,1.023445,1 +pallet_democracy,unlock,2607800965000,2.607800965,1 +pallet_democracy,remove_vote,1915962210000,1.91596221,1 +pallet_democracy,remove_other_vote,1916351630000,1.91635163,1 +pallet_democracy,blacklist,4567035000000,4.567035,1 +pallet_democracy,cancel_proposal,2746670000000,2.74667,1 +pallet_collective,set_members,68865491020000,68.86549102000001,1 +pallet_collective,execute,2989500515000,2.989500515,1 +pallet_collective,propose,3256181560000,3.25618156,1 +pallet_collective,vote,1450336090000,1.45033609,1 +pallet_collective,close,2975299830000,2.97529983,1 +pallet_collective,disapprove_proposal,2324557585000,2.324557585,1 +cumulus_pallet_xcmp_queue,service_overweight,500050320000,0.50005032,1 +cumulus_pallet_xcmp_queue,suspend_xcm_execution,994890000000,0.99489,1 +cumulus_pallet_xcmp_queue,resume_xcm_execution,994890000000,0.99489,1 +cumulus_pallet_xcmp_queue,update_suspend_threshold,1159115000000,1.159115,1 +cumulus_pallet_xcmp_queue,update_drop_threshold,1159115000000,1.159115,1 +cumulus_pallet_xcmp_queue,update_resume_threshold,1159115000000,1.159115,1 +cumulus_pallet_xcmp_queue,update_threshold_weight,1158885000000,1.158885,1 +cumulus_pallet_xcmp_queue,update_weight_restrict_decay,1158885000000,1.158885,1 +cumulus_pallet_xcmp_queue,update_xcmp_max_individual_weight,1158885000000,1.158885,1 +orml_xtokens,transfer,495260000000,0.49526,1 +orml_xtokens,transfer_multiasset,495070000000,0.49507,1 +orml_xtokens,transfer_with_fee,495420000000,0.49542,1 +orml_xtokens,transfer_multiasset_with_fee,495210000000,0.49521,1 +orml_xtokens,transfer_multicurrencies,495640000000,0.49564,1 +orml_xtokens,transfer_multiassets,495120000000,0.49512,1 +calamari_vesting,vest,2859355000000,2.859355,1 +manta_collator_selection,set_invulnerables,1069408910000,1.06940891,1 +manta_collator_selection,set_desired_candidates,1075720000000,1.07572,1 +manta_collator_selection,set_candidacy_bond,1065950000000,1.06595,1 +manta_collator_selection,register_as_candidate,1940997850000,1.94099785,1 +manta_collator_selection,register_candidate,1944193535000,1.944193535,1 +manta_collator_selection,leave_intent,1346955310000,1.34695531,1 +manta_collator_selection,remove_collator,1492790090000,1.49279009,1 +manta_collator_selection,set_eviction_baseline,1063325000000,1.063325,1 +manta_collator_selection,set_eviction_tolerance,1198635000000,1.198635,1 +pallet_asset_manager,register_asset,4292990000000,4.29299,1 +pallet_asset_manager,update_asset_location,3977305000000,3.977305,1 +pallet_asset_manager,update_asset_metadata,2335890000000,2.33589,1 +pallet_asset_manager,set_units_per_second,1437690000000,1.43769,1 +pallet_asset_manager,mint_asset,2394510000000,2.39451,1 +pallet_asset_manager,set_min_xcm_fee,1250255000000,1.250255,1 +pallet_asset_manager,update_outgoing_filtered_assets,1252525000000,1.252525,1 +pallet_asset_manager,register_lp_asset,4576510000000,4.57651,1 +pallet_asset_manager,permissionless_register_asset,4613105000000,4.613105,1 +pallet_assets,create,1227565000000,1.227565,1 +pallet_assets,force_create,1222010000000,1.22201,1 +pallet_assets,start_destroy,1232480000000,1.23248,1 +pallet_assets,destroy_accounts,1330942640000000,1330.94264,1 +pallet_assets,destroy_approvals,667215965000000,667.215965,1 +pallet_assets,finish_destroy,1357005000000,1.357005,1 +pallet_assets,mint,1933680000000,1.93368,1 +pallet_assets,burn,2040775000000,2.040775,1 +pallet_assets,transfer,3277750000000,3.27775,1 +pallet_assets,transfer_keep_alive,3246660000000,3.24666,1 +pallet_assets,force_transfer,3295720000000,3.29572,1 +pallet_assets,freeze,1386270000000,1.38627,1 +pallet_assets,thaw,1382730000000,1.38273,1 +pallet_assets,freeze_asset,1230930000000,1.23093,1 +pallet_assets,thaw_asset,1229240000000,1.22924,1 +pallet_assets,transfer_ownership,1365410000000,1.36541,1 +pallet_assets,set_team,1227040000000,1.22704,1 +pallet_assets,set_metadata,1374964665000,1.374964665,1 +pallet_assets,clear_metadata,1369985000000,1.369985,1 +pallet_assets,force_set_metadata,1371133785000,1.371133785,1 +pallet_assets,force_clear_metadata,1375895000000,1.375895,1 +pallet_assets,force_asset_status,1225245000000,1.225245,1 +pallet_assets,approve_transfer,1882435000000,1.882435,1 +pallet_assets,cancel_approval,1904860000000,1.90486,1 +pallet_assets,force_cancel_approval,1896745000000,1.896745,1 +pallet_assets,transfer_approved,3960380000000,3.96038,1 +pallet_assets,touch,1933340000000,1.93334,1 +pallet_assets,refund,1933350000000,1.93335,1 +pallet_author_inherent,kick_off_authorship_validation,1586320000000,1.58632,1 +pallet_balances,transfer-1,1365955000000,1.365955,1 +pallet_balances,transfer-1000_000,1365985000000,1.365985,1 +pallet_balances,set_balance,1260505000000,1.260505,1 +pallet_balances,force_transfer,1991635000000,1.991635,1 +pallet_balances,transfer_keep_alive-1,1304990000000,1.30499,1 +pallet_balances,transfer_keep_alive-1000_000,1305020000000,1.30502,1 +pallet_balances,transfer_all,1335915000000,1.335915,1 +pallet_balances,force_unreserve,1231245000000,1.231245,1 +pallet_manta_pay,to_private,203019000000000,203.019,1 +pallet_manta_pay,private_transfer,361553010000000,361.55301,1 +pallet_manta_pay,to_public,270664035000000,270.664035,1 +pallet_manta_pay,public_transfer,2644850000000,2.64485,1 +pallet_manta_sbt,to_private,200590475000000,200.590475,1 +pallet_manta_sbt,force_to_private,199809195000000,199.809195,1 +pallet_manta_sbt,reserve_sbt,2867300000000,2.8673,1 +pallet_manta_sbt,allowlist_evm_account,2279935000000,2.279935,1 +pallet_manta_sbt,set_next_sbt_id,1075810000000,1.07581,1 +pallet_manta_sbt,remove_allowlist_evm_account,1085905000000,1.085905,1 +pallet_manta_sbt,change_free_reserve_account,1099380000000,1.09938,1 +pallet_manta_sbt,force_mint_sbt_eth,200458375000000,200.45837500000002,1 +pallet_manta_sbt,change_force_account,1161855000000,1.161855,1 +pallet_manta_sbt,mint_sbt_eth,200856085000000,200.856085,1 +pallet_manta_sbt,change_allowlist_account,1077710000000,1.07771,1 +pallet_manta_sbt,update_mint_info,1738060000000,1.73806,1 +pallet_manta_sbt,new_mint_info,2220005000000,2.220005,1 +pallet_parachain_staking,set_staking_expectations,1228535000000,1.228535,1 +pallet_parachain_staking,set_inflation,1461840000000,1.46184,1 +pallet_parachain_staking,set_parachain_bond_account,1226735000000,1.226735,1 +pallet_parachain_staking,set_parachain_bond_account,1226735000000,1.226735,1 +pallet_parachain_staking,set_parachain_bond_reserve_percent,1224555000000,1.224555,1 +pallet_parachain_staking,set_total_selected,1232315000000,1.232315,1 +pallet_parachain_staking,set_collator_commission,1216985000000,1.216985,1 +pallet_parachain_staking,set_blocks_per_round,1609170000000,1.60917,1 +pallet_parachain_staking,join_candidates,5216286175000,5.2162861750000005,1 +pallet_parachain_staking,schedule_leave_candidates,1936629755000,1.936629755,1 +pallet_parachain_staking,execute_leave_candidates,7469357020000,7.46935702,1 +pallet_parachain_staking,cancel_leave_candidates,1924667295000,1.924667295,1 +pallet_parachain_staking,go_offline,1896210000000,1.89621,1 +pallet_parachain_staking,go_offline,1896210000000,1.89621,1 +pallet_parachain_staking,candidate_bond_more,3887180000000,3.88718,1 +pallet_parachain_staking,schedule_candidate_bond_less,1265385000000,1.265385,1 +pallet_parachain_staking,execute_candidate_bond_less,3937135000000,3.937135,1 +pallet_parachain_staking,cancel_candidate_bond_less,1254530000000,1.25453,1 +pallet_parachain_staking,delegate_1_2_3,5273927695000,5.273927695,1 +pallet_parachain_staking,delegate_1_25_3,5310317145000,5.310317144999999,1 +pallet_parachain_staking,delegate_1_2_100,5356927015000,5.356927015,1 +pallet_parachain_staking,schedule_leave_delegators,1911805000000,1.911805,1 +pallet_parachain_staking,execute_leave_delegators,7932555925000,7.932555924999999,1 +pallet_parachain_staking,cancel_leave_delegators,1913355000000,1.913355,1 +pallet_parachain_staking,schedule_revoke_delegation,1913155000000,1.913155,1 +pallet_parachain_staking,delegator_bond_more,5371665000000,5.371665,1 +pallet_parachain_staking,schedule_delegator_bond_less,1916835000000,1.916835,1 +pallet_parachain_staking,execute_delegation_request,5922480000000,5.92248,1 +pallet_parachain_staking,cancel_delegation_request,1936370000000,1.93637,1 +pallet_scheduler,cancel,2095680480000,2.09568048,1 +pallet_scheduler,schedule,1444069455000,1.444069455,1 +pallet_scheduler,schedule_named,2100533690000,2.10053369,1 +pallet_scheduler,cancel_named,2236137890000,2.23613789,1 +pallet_scheduler,schedule_after,1444309455000,1.444309455,1 +pallet_scheduler,schedule_named_after,2100773690000,2.10077369,1 +pallet_session,set_keys,3162185000000,3.162185,1 +pallet_session,purge_keys,2746560000000,2.74656,1 +pallet_tx_pause,pause_transaction,1281725000000,1.281725,1 +pallet_tx_pause,unpause_transaction,1240290000000,1.24029,1 +pallet_tx_pause,pause_transactions,2068250000000,2.06825,1 +pallet_tx_pause,unpause_transactions,1985380000000,1.98538,1 +pallet_tx_pause,pause_pallets,39804310000000,39.80431,1 +pallet_tx_pause,unpause_pallets,39804310000000,39.80431,1 +pallet_utility,batch-size=1,854862195000,0.854862195,1 +pallet_utility,batch-size=32,6824280940000,6.82428094,1 +pallet_utility,as_derivative,7487305940000,7.48730594,1 +pallet_utility,batch_all-size=1,836685190000,0.83668519,1 +pallet_utility,batch_all-size=32,6242616780000,6.24261678,1 +pallet_utility,dispatch_as,6325061780000,6.32506178,1 +pallet_utility,force_batch,620492050000,0.62049205,1 +pallet_randomness,set_babe_randomness_results,2467405000000,2.467405,1 +pallet_name_service,register,2129070000000,2.12907,1 +pallet_name_service,accept_register,1757200000000,1.7572,1 +pallet_name_service,set_primary_name,1387720000000,1.38772,1 +pallet_name_service,cancel_pending_register,1243995000000,1.243995,1 +pallet_name_service,remove_register,1383635000000,1.383635,1 +pallet_farming,create_farming_pool,2873330000000,2.87333,1 +pallet_farming,charge,3944180000000,3.94418,1 +pallet_farming,deposit,4622785000000,4.622785,1 +pallet_farming,withdraw,1938205000000,1.938205,1 +pallet_farming,claim,2078665000000,2.078665,1 +pallet_farming,withdraw_claim,2078665000000,2.078665,1 +pallet_farming,close_pool,495055000000,0.495055,1 +pallet_farming,set_retire_limit,494935000000,0.494935,1 +pallet_farming,retire_pool,495055000000,0.495055,1 +pallet_farming,reset_pool,496065000000,0.496065,1 +pallet_farming,kill_pool,495055000000,0.495055,1 +pallet_farming,edit_pool,496495000000,0.496495,1 +pallet_farming,gauge_withdraw,2221150000000,2.22115,1 +pallet_farming,force_gauge_claim,495055000000,0.495055,1 +pallet_lottery,deposit,10881947400000,10.8819474,1 +pallet_lottery,request_withdraw,7215652015000,7.215652015000001,1 +pallet_lottery,claim_my_winnings,3399352125000,3.399352125,1 +pallet_lottery,rebalance_stake,494890000000,0.49489,1 +pallet_lottery,start_lottery,2603480000000,2.60348,1 +pallet_lottery,stop_lottery,1904485000000,1.904485,1 +pallet_lottery,draw_lottery,2525445000000,2.525445,1 +pallet_lottery,process_matured_withdrawals,1080395000000,1.080395,1 +pallet_lottery,liquidate_lottery,494890000000,0.49489,1 +pallet_lottery,set_min_deposit,1168880000000,1.16888,1 +pallet_lottery,set_min_withdraw,1024630000000,1.02463,1 +pallet_lottery,set_gas_reserve,1023800000000,1.0238,1 +zenlink_protocol,set_fee_receiver,1210775000000,1.210775,1 +zenlink_protocol,set_fee_point,1176860000000,1.17686,1 +zenlink_protocol,transfer,500350000000,0.50035,1 +zenlink_protocol,transfer,2032270000000,2.03227,1 +zenlink_protocol,add_liquidity,19335290000000,19.33529,1 +zenlink_protocol,remove_liquidity,10760000000000,10.76,1 +zenlink_protocol,swap_exact_assets_for_assets,5146950000000,5.14695,1 +zenlink_protocol,swap_assets_for_exact_assets,5148355000000,5.148355,1 +zenlink_protocol,bootstrap_create,2249465000000,2.249465,1 +zenlink_protocol,bootstrap_contribute,4851215000000,4.851215,1 +zenlink_protocol,bootstrap_claim,14818330000000,14.81833,1 +zenlink_protocol,bootstrap_end,20542085000000,20.542085,1 +zenlink_protocol,bootstrap_update,2407360000000,2.40736,1 +zenlink_protocol,bootstrap_refund,4721655000000,4.721655,1 +zenlink_protocol,bootstrap_charge_reward,995740000000,0.99574,1 +zenlink_protocol,bootstrap_withdraw_reward,995470000000,0.99547,1 +pallet_native_barrier,initialize_native_barrier,1068290000000,1.06829,1 +pallet_native_barrier,add_accounts_to_native_barrier,3877750000000,3.87775,1 +pallet_native_barrier,remove_accounts_from_native_barrier,3091580000000,3.09158,1 diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml index 76a89e73a..cb3b8343b 100644 --- a/runtime/common/Cargo.toml +++ b/runtime/common/Cargo.toml @@ -30,7 +30,7 @@ scale-info = { version = "2.1.2", features = ["derive"] } # Substrate dependencies frame-system = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } pallet-assets = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } -pallet-balances = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } +pallet-balances = { path = "../../pallets/balances" } pallet-utility = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } sp-core = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } sp-io = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } @@ -56,8 +56,8 @@ cumulus-test-relay-sproof-builder = { git = "https://github.com/paritytech/cumul parachain-info = { git = 'https://github.com/paritytech/cumulus.git', branch = "polkadot-v0.9.37" } # Orml dependencies -orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "polkadot-v0.9.37" } -orml-xtokens = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "polkadot-v0.9.37" } +orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "ghzlatarev/polkadot-v0.9.37" } +orml-xtokens = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "ghzlatarev/polkadot-v0.9.37" } # Self dependencies pallet-asset-manager = { path = '../../pallets/asset-manager' } diff --git a/runtime/integration-tests/Cargo.toml b/runtime/integration-tests/Cargo.toml index b3d1e6b5a..811892523 100644 --- a/runtime/integration-tests/Cargo.toml +++ b/runtime/integration-tests/Cargo.toml @@ -18,7 +18,7 @@ frame-support = { git = 'https://github.com/paritytech/substrate.git', branch = frame-system = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } lazy_static = "1.4.0" pallet-assets = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" } -pallet-balances = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } +pallet-balances = { path = "../../pallets/balances" } pallet-collective = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } pallet-democracy = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } pallet-membership = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" } @@ -52,8 +52,8 @@ cumulus-test-relay-sproof-builder = { git = "https://github.com/paritytech/cumul parachain-info = { git = 'https://github.com/paritytech/cumulus.git', branch = "polkadot-v0.9.37" } # Orml dependencies -orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "polkadot-v0.9.37" } -orml-xtokens = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "polkadot-v0.9.37" } +orml-traits = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "ghzlatarev/polkadot-v0.9.37" } +orml-xtokens = { git = "https://github.com/manta-network/open-runtime-module-library.git", branch = "ghzlatarev/polkadot-v0.9.37" } # Self dependencies calamari-vesting = { path = '../../pallets/vesting' } diff --git a/runtime/integration-tests/src/integrations_mock/test_common.rs b/runtime/integration-tests/src/integrations_mock/test_common.rs index ad2fb31bf..96c8a21ba 100644 --- a/runtime/integration-tests/src/integrations_mock/test_common.rs +++ b/runtime/integration-tests/src/integrations_mock/test_common.rs @@ -189,7 +189,7 @@ mod parachain_staking_tests { FERDIE.clone(), ]); // Increase self-bond for everyone but ALICE - for collator in vec![ + for collator in [ BOB.clone(), CHARLIE.clone(), DAVE.clone(), @@ -203,7 +203,7 @@ mod parachain_staking_tests { } // Delegate a large amount of tokens to EVE and ALICE - for collator in vec![EVE.clone(), ALICE.clone()] { + for collator in [EVE.clone(), ALICE.clone()] { assert_ok!(ParachainStaking::delegate( RuntimeOrigin::signed(USER.clone()), collator, @@ -293,7 +293,7 @@ mod parachain_staking_tests { FERDIE.clone(), ]); // Increase bond for everyone but FERDIE - for collator in vec![ + for collator in [ BOB.clone(), CHARLIE.clone(), DAVE.clone(), diff --git a/runtime/integration-tests/src/xcm_mock/parachain.rs b/runtime/integration-tests/src/xcm_mock/parachain.rs index f900f9ca3..f7ffca82c 100644 --- a/runtime/integration-tests/src/xcm_mock/parachain.rs +++ b/runtime/integration-tests/src/xcm_mock/parachain.rs @@ -108,6 +108,23 @@ impl frame_system::Config for Runtime { type MaxConsumers = ConstU32<16>; } +pub struct MockNativeBarrier; +impl orml_traits::native_barrier::NativeBarrier for MockNativeBarrier { + fn update_native_barrier(_account_id: &AccountId, _amount: Balance) {} + fn ensure_limit_not_exceeded( + _account_id: &AccountId, + _amount: Balance, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) + } +} + +impl orml_traits::native_barrier::NativeChecker for MockNativeBarrier { + fn is_native(_currency_id: &CurrencyId) -> bool { + true + } +} + parameter_types! { pub ExistentialDeposit: Balance = 1; pub const MaxLocks: u32 = 50; @@ -124,6 +141,7 @@ impl pallet_balances::Config for Runtime { type WeightInfo = (); type MaxReserves = MaxReserves; type ReserveIdentifier = [u8; 8]; + type NativeBarrierType = MockNativeBarrier; } parameter_types! { @@ -700,6 +718,7 @@ impl orml_xtokens::Config for Runtime { type MultiLocationsFilter = AssetManager; type OutgoingAssetsFilter = AssetManager; type ReserveProvider = orml_traits::location::AbsoluteReserveProvider; + type NativeBarrierType = MockNativeBarrier; } impl parachain_info::Config for Runtime {} diff --git a/runtime/integration-tests/src/xcm_mock/relay_chain.rs b/runtime/integration-tests/src/xcm_mock/relay_chain.rs index a8ae41597..d453b8317 100644 --- a/runtime/integration-tests/src/xcm_mock/relay_chain.rs +++ b/runtime/integration-tests/src/xcm_mock/relay_chain.rs @@ -78,6 +78,23 @@ impl frame_system::Config for Runtime { type MaxConsumers = ConstU32<16>; } +pub struct MockNativeBarrier; +impl orml_traits::native_barrier::NativeBarrier for MockNativeBarrier { + fn update_native_barrier(_account_id: &AccountId, _amount: Balance) {} + fn ensure_limit_not_exceeded( + _account_id: &AccountId, + _amount: Balance, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) + } +} + +impl orml_traits::native_barrier::NativeChecker for MockNativeBarrier { + fn is_native(_currency_id: &u64) -> bool { + true + } +} + parameter_types! { pub ExistentialDeposit: Balance = 1; pub const MaxLocks: u32 = 50; @@ -94,6 +111,7 @@ impl pallet_balances::Config for Runtime { type WeightInfo = (); type MaxReserves = MaxReserves; type ReserveIdentifier = [u8; 8]; + type NativeBarrierType = MockNativeBarrier; } impl pallet_utility::Config for Runtime { diff --git a/runtime/manta/Cargo.toml b/runtime/manta/Cargo.toml index 0b88d3b3f..21b9cdc59 100644 --- a/runtime/manta/Cargo.toml +++ b/runtime/manta/Cargo.toml @@ -44,7 +44,7 @@ frame-try-runtime = { git = "https://github.com/paritytech/substrate.git", defau pallet-assets = { git = "https://github.com/paritytech/substrate.git", default-features = false, branch = "polkadot-v0.9.37" } pallet-aura = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } pallet-authorship = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } -pallet-balances = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" } +pallet-balances = { path = "../../pallets/balances", default-features = false } pallet-collective = { git = "https://github.com/paritytech/substrate.git", default-features = false, branch = "polkadot-v0.9.37" } pallet-democracy = { git = "https://github.com/paritytech/substrate.git", default-features = false, branch = "polkadot-v0.9.37" } pallet-membership = { git = "https://github.com/paritytech/substrate.git", default-features = false, branch = "polkadot-v0.9.37" } @@ -85,8 +85,8 @@ xcm-builder = { git = 'https://github.com/paritytech/polkadot.git', default-feat xcm-executor = { git = 'https://github.com/paritytech/polkadot.git', default-features = false, branch = "release-v0.9.37" } # Third party (vendored) dependencies -orml-traits = { git = 'https://github.com/manta-network/open-runtime-module-library.git', default-features = false, branch = "polkadot-v0.9.37" } -orml-xtokens = { git = 'https://github.com/manta-network/open-runtime-module-library.git', default-features = false, branch = "polkadot-v0.9.37" } +orml-traits = { git = 'https://github.com/manta-network/open-runtime-module-library.git', default-features = false, branch = "ghzlatarev/polkadot-v0.9.37" } +orml-xtokens = { git = 'https://github.com/manta-network/open-runtime-module-library.git', default-features = false, branch = "ghzlatarev/polkadot-v0.9.37" } zenlink-protocol = { git = 'https://github.com/manta-network/Zenlink', branch = "polkadot-v0.9.37", default-features = false } zenlink-protocol-runtime-api = { git = 'https://github.com/manta-network/Zenlink', branch = "polkadot-v0.9.37", default-features = false } @@ -102,6 +102,7 @@ pallet-lottery = { path = '../../pallets/pallet-lottery', default-features = fal pallet-manta-pay = { path = '../../pallets/manta-pay', default-features = false, features = ["runtime"] } pallet-manta-sbt = { path = '../../pallets/manta-sbt', default-features = false, features = ["runtime"] } pallet-name-service = { path = '../../pallets/name-service', default-features = false } +pallet-native-barrier = { path = '../../pallets/native-barrier', default-features = false } pallet-parachain-staking = { path = '../../pallets/parachain-staking', default-features = false } pallet-randomness = { path = '../../pallets/randomness', default-features = false } pallet-tx-pause = { path = '../../pallets/tx-pause', default-features = false } @@ -163,6 +164,7 @@ runtime-benchmarks = [ 'pallet-name-service/runtime-benchmarks', "zenlink-protocol/runtime-benchmarks", 'pallet-farming/runtime-benchmarks', + 'pallet-native-barrier/runtime-benchmarks', ] try-runtime = [ 'frame-try-runtime', @@ -207,6 +209,7 @@ try-runtime = [ 'pallet-lottery/try-runtime', "zenlink-protocol/try-runtime", "pallet-farming/try-runtime", + "pallet-native-barrier/try-runtime", ] # Set timing constants (e.g. session period) to faster versions to speed up testing. fast-runtime = [] @@ -285,6 +288,7 @@ std = [ "zenlink-protocol-runtime-api/std", "pallet-farming/std", "pallet-farming-rpc-runtime-api/std", + "pallet-native-barrier/std", ] # A feature that should be enabled when the runtime should be build for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm diff --git a/runtime/manta/src/assets_config.rs b/runtime/manta/src/assets_config.rs index a2498b943..5b7d5ec9b 100644 --- a/runtime/manta/src/assets_config.rs +++ b/runtime/manta/src/assets_config.rs @@ -16,8 +16,8 @@ use super::{ weights, xcm_config::SelfReserve, AssetManager, Assets, Balances, CouncilCollective, - NativeTokenExistentialDeposit, Runtime, RuntimeEvent, RuntimeOrigin, TechnicalCollective, - Timestamp, MANTA, + NativeBarrier, NativeTokenExistentialDeposit, Runtime, RuntimeEvent, RuntimeOrigin, + TechnicalCollective, Timestamp, MANTA, }; use manta_primitives::{ @@ -196,6 +196,7 @@ impl pallet_manta_pay::Config for Runtime { type WeightInfo = weights::pallet_manta_pay::SubstrateWeight; type AssetConfig = MantaAssetConfig; type PalletId = MantaPayPalletId; + type NativeBarrierType = NativeBarrier; } impl pallet_manta_sbt::Config for Runtime { diff --git a/runtime/manta/src/diff_tx_fees.rs b/runtime/manta/src/diff_tx_fees.rs index dcf596c0a..4f0891c00 100644 --- a/runtime/manta/src/diff_tx_fees.rs +++ b/runtime/manta/src/diff_tx_fees.rs @@ -105,7 +105,7 @@ fn diff_tx_fees() { frame_support::metadata::RuntimeMetadata::V14(metadata) => metadata.pallets, _ => unreachable!(), }; - assert_eq!(pallets.len(), 39); + assert_eq!(pallets.len(), 40); }); let fee_multipliers = fee_multipliers(); @@ -3156,5 +3156,47 @@ fn calculate_all_current_extrinsic_tx_fee() -> ( )); } + // pallet_native_barrier + { + assert_eq!( + crate::RuntimeCall::get_call_names("NativeBarrier").len(), + 3, + "Please update new extrinsic here." + ); + + let call = crate::RuntimeCall::NativeBarrier( + pallet_native_barrier::Call::initialize_native_barrier { init: None }, + ); + let (dispatch_info, call_len) = get_call_details(&call); + calamari_runtime_calls.push(( + "pallet_native_barrier", + "initialize_native_barrier", + dispatch_info, + call_len, + )); + + let call = crate::RuntimeCall::NativeBarrier( + pallet_native_barrier::Call::add_accounts_to_native_barrier { accounts: vec![] }, + ); + let (dispatch_info, call_len) = get_call_details(&call); + calamari_runtime_calls.push(( + "pallet_native_barrier", + "add_accounts_to_native_barrier", + dispatch_info, + call_len, + )); + + let call = crate::RuntimeCall::NativeBarrier( + pallet_native_barrier::Call::remove_accounts_from_native_barrier { accounts: vec![] }, + ); + let (dispatch_info, call_len) = get_call_details(&call); + calamari_runtime_calls.push(( + "pallet_native_barrier", + "remove_accounts_from_native_barrier", + dispatch_info, + call_len, + )); + } + (calamari_runtime_calls, t) } diff --git a/runtime/manta/src/lib.rs b/runtime/manta/src/lib.rs index 9c8af48fe..72ac32bde 100644 --- a/runtime/manta/src/lib.rs +++ b/runtime/manta/src/lib.rs @@ -447,6 +447,14 @@ impl pallet_balances::Config for Runtime { type ExistentialDeposit = NativeTokenExistentialDeposit; type AccountStore = frame_system::Pallet; type WeightInfo = weights::pallet_balances::SubstrateWeight; + type NativeBarrierType = NativeBarrier; +} + +impl pallet_native_barrier::Config for Runtime { + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type UnixTime = Timestamp; + type WeightInfo = weights::pallet_native_barrier::SubstrateWeight; } parameter_types! { @@ -936,6 +944,7 @@ construct_runtime!( CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin} = 32, DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 33, XTokens: orml_xtokens::{Pallet, Call, Event, Storage} = 34, + NativeBarrier: pallet_native_barrier::{Pallet, Call, Event, Storage} = 35, // Handy utilities. Utility: pallet_utility::{Pallet, Call, Event} = 40, @@ -1034,6 +1043,7 @@ mod benches { [pallet_name_service, NameService] [zenlink_protocol, ZenlinkProtocol] [pallet_farming, Farming] + [pallet_native_barrier, NativeBarrier] // Nimbus pallets [pallet_author_inherent, AuthorInherent] ); @@ -1310,11 +1320,11 @@ impl_runtime_apis! { impl pallet_farming_rpc_runtime_api::FarmingRuntimeApi for Runtime { fn get_farming_rewards(who: AccountId, pid: PoolId) -> Vec<(MantaAssetId, Balance)> { - Farming::get_farming_rewards(&who, pid).unwrap_or(Vec::new()) + Farming::get_farming_rewards(&who, pid).unwrap_or_default() } fn get_gauge_rewards(who: AccountId, pid: PoolId) -> Vec<(MantaAssetId, Balance)> { - Farming::get_gauge_rewards(&who, pid).unwrap_or(Vec::new()) + Farming::get_gauge_rewards(&who, pid).unwrap_or_default() } } diff --git a/runtime/manta/src/weights/mod.rs b/runtime/manta/src/weights/mod.rs index 366ae36a2..62d734066 100644 --- a/runtime/manta/src/weights/mod.rs +++ b/runtime/manta/src/weights/mod.rs @@ -32,6 +32,7 @@ pub mod pallet_manta_sbt; pub mod pallet_membership; pub mod pallet_multisig; pub mod pallet_name_service; +pub mod pallet_native_barrier; pub mod pallet_parachain_staking; pub mod pallet_preimage; pub mod pallet_randomness; diff --git a/runtime/manta/src/weights/pallet_native_barrier.rs b/runtime/manta/src/weights/pallet_native_barrier.rs new file mode 100644 index 000000000..f79e13b5d --- /dev/null +++ b/runtime/manta/src/weights/pallet_native_barrier.rs @@ -0,0 +1,122 @@ +// Copyright 2020-2023 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . + +//! Autogenerated weights for pallet_native_barrier +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-08-30, STEPS: `50`, REPEAT: 40, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("manta-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/manta +// benchmark +// pallet +// --chain=manta-dev +// --steps=50 +// --repeat=40 +// --pallet=pallet_native_barrier +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./scripts/benchmarking/frame-weights-output/pallet_native_barrier.rs +// --template=.github/resources/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(clippy::unnecessary_cast)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; +use manta_primitives::constants::RocksDbWeight; + +/// Weight functions needed for pallet_native_barrier. +pub trait WeightInfo { + fn on_initialize() -> Weight; + fn initialize_native_barrier() -> Weight; + fn add_accounts_to_native_barrier() -> Weight; + fn remove_accounts_from_native_barrier() -> Weight; +} + +/// Weights for pallet_native_barrier using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl pallet_native_barrier::WeightInfo for SubstrateWeight { + // Storage: NativeBarrier Configurations (r:1 w:0) + // Storage: Timestamp Now (r:1 w:0) + // Storage: NativeBarrier LastDayProcessed (r:1 w:0) + fn on_initialize() -> Weight { + // Minimum execution time: 8_005 nanoseconds. + Weight::from_ref_time(8_346_000) + .saturating_add(T::DbWeight::get().reads(3)) + } + // Storage: NativeBarrier LastDayProcessed (r:1 w:1) + // Storage: NativeBarrier Configurations (r:0 w:1) + fn initialize_native_barrier() -> Weight { + // Minimum execution time: 37_480 nanoseconds. + Weight::from_ref_time(38_803_000) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: NativeBarrier Configurations (r:1 w:0) + // Storage: NativeBarrier RemainingLimit (r:5 w:5) + fn add_accounts_to_native_barrier() -> Weight { + // Minimum execution time: 27_011 nanoseconds. + Weight::from_ref_time(27_622_000) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(5)) + } + // Storage: NativeBarrier RemainingLimit (r:0 w:5) + fn remove_accounts_from_native_barrier() -> Weight { + // Minimum execution time: 17_994 nanoseconds. + Weight::from_ref_time(18_445_000) + .saturating_add(T::DbWeight::get().writes(5)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: NativeBarrier Configurations (r:1 w:0) + // Storage: Timestamp Now (r:1 w:0) + // Storage: NativeBarrier LastDayProcessed (r:1 w:0) + fn on_initialize() -> Weight { + // Minimum execution time: 8_005 nanoseconds. + Weight::from_ref_time(8_346_000) + .saturating_add(RocksDbWeight::get().reads(3)) + } + // Storage: NativeBarrier LastDayProcessed (r:1 w:1) + // Storage: NativeBarrier Configurations (r:0 w:1) + fn initialize_native_barrier() -> Weight { + // Minimum execution time: 37_480 nanoseconds. + Weight::from_ref_time(38_803_000) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(2)) + } + // Storage: NativeBarrier Configurations (r:1 w:0) + // Storage: NativeBarrier RemainingLimit (r:5 w:5) + fn add_accounts_to_native_barrier() -> Weight { + // Minimum execution time: 27_011 nanoseconds. + Weight::from_ref_time(27_622_000) + .saturating_add(RocksDbWeight::get().reads(6)) + .saturating_add(RocksDbWeight::get().writes(5)) + } + // Storage: NativeBarrier RemainingLimit (r:0 w:5) + fn remove_accounts_from_native_barrier() -> Weight { + // Minimum execution time: 17_994 nanoseconds. + Weight::from_ref_time(18_445_000) + .saturating_add(RocksDbWeight::get().writes(5)) + } +} diff --git a/runtime/manta/src/xcm_config.rs b/runtime/manta/src/xcm_config.rs index fc63d7858..514d8a073 100644 --- a/runtime/manta/src/xcm_config.rs +++ b/runtime/manta/src/xcm_config.rs @@ -15,9 +15,10 @@ // along with Manta. If not, see . use super::{ - assets_config::MantaAssetConfig, AssetManager, Assets, Balance, Balances, DmpQueue, - ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, - Treasury, XcmpQueue, MAXIMUM_BLOCK_WEIGHT, + assets_config::{MantaAssetConfig, NativeAssetLocation}, + AssetManager, Assets, Balance, Balances, DmpQueue, NativeBarrier, ParachainInfo, + ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, Treasury, + XcmpQueue, MAXIMUM_BLOCK_WEIGHT, }; use codec::{Decode, Encode}; @@ -326,6 +327,16 @@ impl Contains for AssetManager { } } +impl orml_traits::native_barrier::NativeChecker for NativeBarrier { + fn is_native(currency_id: &CurrencyId) -> bool { + let asset_location = + CurrencyIdtoMultiLocation::>::convert( + currency_id.clone(), + ); + asset_location == NativeAssetLocation::get().into() || asset_location == Some(Here.into()) + } +} + // The XCM message wrapper wrapper impl orml_xtokens::Config for Runtime { type RuntimeEvent = RuntimeEvent; @@ -352,5 +363,6 @@ impl orml_xtokens::Config for Runtime { type MinXcmFee = AssetManager; type MultiLocationsFilter = AssetManager; type OutgoingAssetsFilter = AssetManager; + type NativeBarrierType = NativeBarrier; type ReserveProvider = AbsoluteReserveProvider; } diff --git a/runtime/manta/tx-fees-data/4.4.0-tx-fees.csv b/runtime/manta/tx-fees-data/4.4.0-tx-fees.csv new file mode 100644 index 000000000..996866940 --- /dev/null +++ b/runtime/manta/tx-fees-data/4.4.0-tx-fees.csv @@ -0,0 +1,733 @@ +module,extrinsic,tx_fee_with_decimal,tx_fee_without_decimal,fee_multiplier +frame_system,remark-length=32,5298832146460000,0.00529883214646,0.0002 +frame_system,remark-length=64,5298832146460000,0.00529883214646,0.0002 +frame_system,set_heap_pages,5051034450000000,0.00505103445,0.0002 +frame_system,set_code,10298700000000000,0.0102987,0.0002 +frame_system,set_code_without_checks,10298700000000000,0.0102987,0.0002 +frame_system,set_storage,5639746997510000,0.00563974699751,0.0002 +frame_system,kill_storage,5640750464760000,0.00564075046476,0.0002 +frame_system,kill_prefix,5347861573090000,0.00534786157309,0.0002 +frame_system,remark_with_event-length=32,5298835865040000,0.00529883586504,0.0002 +frame_system,remark_with_event-length=64,5298835865040000,0.00529883586504,0.0002 +pallet_treasury,propose_spend,5311280870000000,0.00531128087,0.0002 +pallet_treasury,reject_proposal,4981742030000000,0.00498174203,0.0002 +pallet_treasury,approve_proposal,4980459314970000,0.00498045931497,0.0002 +pallet_treasury,spend,5308701710000000,0.00530870171,0.0002 +pallet_treasury,remove_approval,4980055430000000,0.00498005543,0.0002 +pallet_timestamp,set,4980037830000000,0.00498003783,0.0002 +pallet_preimage,note_preimage-length=32,5301290792080000,0.00530129079208,0.0002 +pallet_preimage,note_preimage-length=32,5631291734160000,0.00563129173416,0.0002 +pallet_preimage,unnote_preimage,5291369270000000,0.00529136927,0.0002 +pallet_preimage,request_preimage,5290231710000000,0.00529023171,0.0002 +pallet_preimage,unrequest_preimage,5291230180000000,0.00529123018,0.0002 +pallet_multisig,as_multi_threshold_1,5642675964490000,0.00564267596449,0.0002 +pallet_multisig,as_multi,5691632704520000,0.00569163270452,0.0002 +pallet_multisig,approve_as_multi,5670593606370000,0.00567059360637,0.0002 +pallet_multisig,cancel_as_multi,5720336406670000,0.00572033640667,0.0002 +pallet_membership,add_member,5299200000000000,0.0052992,0.0002 +pallet_membership,remove_member,5299200000000000,0.0052992,0.0002 +pallet_membership,swap_member,5629200000000000,0.0056292,0.0002 +pallet_membership,reset_members,5619200000000000,0.0056192,0.0002 +pallet_membership,change_key,5299200000000000,0.0052992,0.0002 +pallet_membership,set_prime,5299200000000000,0.0052992,0.0002 +pallet_membership,clear_prime,4969200000000000,0.0049692,0.0002 +pallet_democracy,propose,5032996150000000,0.00503299615,0.0002 +pallet_democracy,second,4980427850000000,0.00498042785,0.0002 +pallet_democracy,vote,5163099650000000,0.00516309965,0.0002 +pallet_democracy,emergency_cancel,5011449160000000,0.00501144916,0.0002 +pallet_democracy,external_propose,5020391910000000,0.00502039191,0.0002 +pallet_democracy,external_propose_majority,5019756600000000,0.0050197566,0.0002 +pallet_democracy,external_propose_default,5019752930000000,0.00501975293,0.0002 +pallet_democracy,fast_track,5372439790000000,0.00537243979,0.0002 +pallet_democracy,veto_external,5291524150000000,0.00529152415,0.0002 +pallet_democracy,cancel_referendum,4979854120000000,0.00497985412,0.0002 +pallet_democracy,delegate,5603100511750000,0.00560310051175,0.0002 +pallet_democracy,undelegate,5100368590710000,0.00510036859071,0.0002 +pallet_democracy,clear_public_proposals,4969756880000000,0.00496975688,0.0002 +pallet_democracy,unlock,5302928523210000,0.00530292852321,0.0002 +pallet_democracy,remove_vote,5011552070440000,0.00501155207044,0.0002 +pallet_democracy,remove_other_vote,5341551218650000,0.00534155121865,0.0002 +pallet_democracy,blacklist,5346860120000000,0.00534686012,0.0002 +pallet_democracy,cancel_proposal,4983213060000000,0.00498321306,0.0002 +pallet_collective,set_members,6125485107120000,0.00612548510712,0.0002 +pallet_collective,execute,5013732935810000,0.00501373293581,0.0002 +pallet_collective,propose,5024232816700000,0.0050242328167,0.0002 +pallet_collective,vote,5310619517400000,0.0053106195174,0.0002 +pallet_collective,close,5333667460860000,0.00533366746086,0.0002 +pallet_collective,disapprove_proposal,5292365846660000,0.00529236584666,0.0002 +cumulus_pallet_xcmp_queue,service_overweight,5128710000640000,0.00512871000064,0.0002 +cumulus_pallet_xcmp_queue,suspend_xcm_execution,4969700000000000,0.0049697,0.0002 +cumulus_pallet_xcmp_queue,resume_xcm_execution,4969700000000000,0.0049697,0.0002 +cumulus_pallet_xcmp_queue,update_suspend_threshold,5010025090000000,0.00501002509,0.0002 +cumulus_pallet_xcmp_queue,update_drop_threshold,5010025090000000,0.00501002509,0.0002 +cumulus_pallet_xcmp_queue,update_resume_threshold,5010025090000000,0.00501002509,0.0002 +cumulus_pallet_xcmp_queue,update_threshold_weight,5050121450000000,0.00505012145,0.0002 +cumulus_pallet_xcmp_queue,update_weight_restrict_decay,5050121450000000,0.00505012145,0.0002 +cumulus_pallet_xcmp_queue,update_xcmp_max_individual_weight,5050121450000000,0.00505012145,0.0002 +orml_xtokens,transfer,5338700000000000,0.0053387,0.0002 +orml_xtokens,transfer_multiasset,5148700000000000,0.0051487,0.0002 +orml_xtokens,transfer_with_fee,5498700000000000,0.0054987,0.0002 +orml_xtokens,transfer_multiasset_with_fee,5288700000000000,0.0052887,0.0002 +orml_xtokens,transfer_multicurrencies,5718700000000000,0.0057187,0.0002 +orml_xtokens,transfer_multiassets,5198700000000000,0.0051987,0.0002 +manta_collator_selection,set_invulnerables,5299845518220000,0.00529984551822,0.0002 +manta_collator_selection,set_desired_candidates,5009860390000000,0.00500986039,0.0002 +manta_collator_selection,set_candidacy_bond,5129841580000000,0.00512984158,0.0002 +manta_collator_selection,register_as_candidate,4971566536980000,0.00497156653698,0.0002 +manta_collator_selection,register_candidate,5291582199940000,0.00529158219994,0.0002 +manta_collator_selection,leave_intent,4970396739710000,0.00497039673971,0.0002 +manta_collator_selection,remove_collator,5290671925700000,0.0052906719257,0.0002 +manta_collator_selection,set_eviction_baseline,4979835900000000,0.0049798359,0.0002 +manta_collator_selection,set_eviction_tolerance,4979973540000000,0.00497997354,0.0002 +pallet_asset_manager,register_asset,5316307190000000,0.00531630719,0.0002 +pallet_asset_manager,update_asset_location,5015632340000000,0.00501563234,0.0002 +pallet_asset_manager,update_asset_metadata,5272350760000000,0.00527235076,0.0002 +pallet_asset_manager,set_units_per_second,4990541800000000,0.0049905418,0.0002 +pallet_asset_manager,mint_asset,5462457200000000,0.0054624572,0.0002 +pallet_asset_manager,set_min_xcm_fee,5010191960000000,0.00501019196,0.0002 +pallet_asset_manager,update_outgoing_filtered_assets,5010187800000000,0.0050101878,0.0002 +pallet_asset_manager,register_lp_asset,5606858690000000,0.00560685869,0.0002 +pallet_asset_manager,permissionless_register_asset,5166885130000000,0.00516688513,0.0002 +pallet_assets,create,5620165880000000,0.00562016588,0.0002 +pallet_assets,force_create,5480154500000000,0.0054801545,0.0002 +pallet_assets,start_destroy,5130171810000000,0.00513017181,0.0002 +pallet_assets,destroy_accounts,7791438620000000,0.00779143862,0.0002 +pallet_assets,destroy_approvals,6460804100000000,0.0064608041,0.0002 +pallet_assets,finish_destroy,5130649260000000,0.00513064926,0.0002 +pallet_assets,mint,5471553610000000,0.00547155361,0.0002 +pallet_assets,burn,5471676020000000,0.00547167602,0.0002 +pallet_assets,transfer,5474260460000000,0.00547426046,0.0002 +pallet_assets,transfer_keep_alive,5474217320000000,0.00547421732,0.0002 +pallet_assets,force_transfer,5804289630000000,0.00580428963,0.0002 +pallet_assets,freeze,5460459300000000,0.0054604593,0.0002 +pallet_assets,thaw,5460471790000000,0.00546047179,0.0002 +pallet_assets,freeze_asset,5130166020000000,0.00513016602,0.0002 +pallet_assets,thaw_asset,5130168120000000,0.00513016812,0.0002 +pallet_assets,transfer_ownership,5460444430000000,0.00546044443,0.0002 +pallet_assets,set_team,6120165190000000,0.00612016519,0.0002 +pallet_assets,set_metadata,5800465145370000,0.00580046514537,0.0002 +pallet_assets,clear_metadata,5130451280000000,0.00513045128,0.0002 +pallet_assets,force_set_metadata,5810443381150000,0.00581044338115,0.0002 +pallet_assets,force_clear_metadata,5130449960000000,0.00513044996,0.0002 +pallet_assets,force_asset_status,6480156990000000,0.00648015699,0.0002 +pallet_assets,approve_transfer,5471472360000000,0.00547147236,0.0002 +pallet_assets,cancel_approval,5461509230000000,0.00546150923,0.0002 +pallet_assets,force_cancel_approval,5791499060000000,0.00579149906,0.0002 +pallet_assets,transfer_approved,5805624710000000,0.00580562471,0.0002 +pallet_assets,touch,5131553610000000,0.00513155361,0.0002 +pallet_assets,refund,5141553610000000,0.00514155361,0.0002 +pallet_author_inherent,kick_off_authorship_validation,4970878960000000,0.00497087896,0.0002 +pallet_balances,transfer-1,5310440560000000,0.00531044056,0.0002 +pallet_balances,transfer-1000_000,5340440560000000,0.00534044056,0.0002 +pallet_balances,set_balance,5320244400000000,0.0053202444,0.0002 +pallet_balances,force_transfer,5641671250000000,0.00564167125,0.0002 +pallet_balances,transfer_keep_alive-1,5310309940000000,0.00531030994,0.0002 +pallet_balances,transfer_keep_alive-1000_000,5340309940000000,0.00534030994,0.0002 +pallet_balances,transfer_all,5310368540000000,0.00531036854,0.0002 +pallet_balances,force_unreserve,5460166990000000,0.00546016699,0.0002 +pallet_manta_pay,to_private,6724398890000000,0.00672439889,0.0002 +pallet_manta_pay,private_transfer,7045238020000000,0.00704523802,0.0002 +pallet_manta_pay,to_public,6861796130000000,0.00686179613,0.0002 +pallet_manta_pay,public_transfer,5772978680000000,0.00577297868,0.0002 +pallet_manta_sbt,to_private,6759968430000000,0.00675996843,0.0002 +pallet_manta_sbt,force_to_private,7087821710000000,0.00708782171,0.0002 +pallet_manta_sbt,reserve_sbt,4983246660000000,0.00498324666,0.0002 +pallet_manta_sbt,allowlist_evm_account,5212268910000000,0.00521226891,0.0002 +pallet_manta_sbt,set_next_sbt_id,5139860600000000,0.0051398606,0.0002 +pallet_manta_sbt,remove_allowlist_evm_account,5209884650000000,0.00520988465,0.0002 +pallet_manta_sbt,change_free_reserve_account,5300034690000000,0.00530003469,0.0002 +pallet_manta_sbt,force_mint_sbt_eth,7629585440000000,0.00762958544,0.0002 +pallet_manta_sbt,change_force_account,5299868250000000,0.00529986825,0.0002 +pallet_manta_sbt,mint_sbt_eth,7839986680000000,0.00783998668,0.0002 +pallet_manta_sbt,change_allowlist_account,5299867840000000,0.00529986784,0.0002 +pallet_manta_sbt,update_mint_info,5121189210000000,0.00512118921,0.0002 +pallet_manta_sbt,new_mint_info,5082155170000000,0.00508215517,0.0002 +pallet_parachain_staking,set_staking_expectations,5450169770000000,0.00545016977,0.0002 +pallet_parachain_staking,set_inflation,5090635330000000,0.00509063533,0.0002 +pallet_parachain_staking,set_parachain_bond_account,5290172410000000,0.00529017241,0.0002 +pallet_parachain_staking,set_parachain_bond_account,5290172410000000,0.00529017241,0.0002 +pallet_parachain_staking,set_parachain_bond_reserve_percent,4980163440000000,0.00498016344,0.0002 +pallet_parachain_staking,set_total_selected,4980176300000000,0.0049801763,0.0002 +pallet_parachain_staking,set_collator_commission,4980147930000000,0.00498014793,0.0002 +pallet_parachain_staking,set_blocks_per_round,4980928560000000,0.00498092856,0.0002 +pallet_parachain_staking,join_candidates,4998155510680000,0.00499815551068,0.0002 +pallet_parachain_staking,schedule_leave_candidates,4981590489840000,0.00498159048984,0.0002 +pallet_parachain_staking,execute_leave_candidates,5312653954580000,0.00531265395458,0.0002 +pallet_parachain_staking,cancel_leave_candidates,4981569748780000,0.00498156974878,0.0002 +pallet_parachain_staking,go_offline,4971524000000000,0.004971524,0.0002 +pallet_parachain_staking,go_offline,4971524000000000,0.004971524,0.0002 +pallet_parachain_staking,candidate_bond_more,4985497510000000,0.00498549751,0.0002 +pallet_parachain_staking,schedule_candidate_bond_less,5130235200000000,0.0051302352,0.0002 +pallet_parachain_staking,execute_candidate_bond_less,5295592620000000,0.00529559262,0.0002 +pallet_parachain_staking,cancel_candidate_bond_less,4970222140000000,0.00497022214,0.0002 +pallet_parachain_staking,delegate_1_2_3,5328290401240000,0.00532829040124,0.0002 +pallet_parachain_staking,delegate_1_25_3,5328356837890000,0.00532835683789,0.0002 +pallet_parachain_staking,delegate_1_2_100,5338457533210000,0.00533845753321,0.0002 +pallet_parachain_staking,schedule_leave_delegators,4971525980000000,0.00497152598,0.0002 +pallet_parachain_staking,execute_leave_delegators,5313589823450000,0.00531358982345,0.0002 +pallet_parachain_staking,cancel_leave_delegators,4971569610000000,0.00497156961,0.0002 +pallet_parachain_staking,schedule_revoke_delegation,5291527000000000,0.005291527,0.0002 +pallet_parachain_staking,delegator_bond_more,5308436310000000,0.00530843631,0.0002 +pallet_parachain_staking,schedule_delegator_bond_less,5301545530000000,0.00530154553,0.0002 +pallet_parachain_staking,execute_delegation_request,5619579090000000,0.00561957909,0.0002 +pallet_parachain_staking,cancel_delegation_request,5291591330000000,0.00529159133,0.0002 +pallet_scheduler,cancel,5051918176790000,0.00505191817679,0.0002 +pallet_scheduler,schedule,5130601273350000,0.00513060127335,0.0002 +pallet_scheduler,schedule_named,5531911598540000,0.00553191159854,0.0002 +pallet_scheduler,cancel_named,5292194459230000,0.00529219445923,0.0002 +pallet_scheduler,schedule_after,5370601273350000,0.00537060127335,0.0002 +pallet_scheduler,schedule_named_after,5771911598540000,0.00577191159854,0.0002 +pallet_session,set_keys,5942729070000000,0.00594272907,0.0002 +pallet_session,purge_keys,4972173290000000,0.00497217329,0.0002 +pallet_tx_pause,pause_transaction,5630152180000000,0.00563015218,0.0002 +pallet_tx_pause,unpause_transaction,5630167150000000,0.00563016715,0.0002 +pallet_tx_pause,pause_transactions,5981604360000000,0.00598160436,0.0002 +pallet_tx_pause,unpause_transactions,5981634300000000,0.0059816343,0.0002 +pallet_tx_pause,pause_pallets,5711309000000000,0.005711309,0.0002 +pallet_tx_pause,unpause_pallets,5711309000000000,0.005711309,0.0002 +pallet_utility,batch-size=1,5009447130520000,0.00500944713052,0.0002 +pallet_utility,batch-size=32,5951821389360000,0.00595182138936,0.0002 +pallet_utility,as_derivative,5993143069360000,0.00599314306936,0.0002 +pallet_utility,batch_all-size=1,5009455785630000,0.00500945578563,0.0002 +pallet_utility,batch_all-size=32,5952098352880000,0.00595209835288,0.0002 +pallet_utility,dispatch_as,6312251342880000,0.00631225134288,0.0002 +pallet_utility,force_batch,4979043625280000,0.00497904362528,0.0002 +pallet_randomness,set_babe_randomness_results,4972600220000000,0.00497260022,0.0002 +pallet_name_service,register,5341969540000000,0.00534196954,0.0002 +pallet_name_service,accept_register,5341226430000000,0.00534122643,0.0002 +pallet_name_service,set_primary_name,5340479230000000,0.00534047923,0.0002 +pallet_name_service,cancel_pending_register,5340203160000000,0.00534020316,0.0002 +pallet_name_service,remove_register,5340479160000000,0.00534047916,0.0002 +pallet_farming,create_farming_pool,6123710100000000,0.0061237101,0.0002 +pallet_farming,charge,5785634250000000,0.00578563425,0.0002 +pallet_farming,deposit,5366946200000000,0.0053669462,0.0002 +pallet_farming,withdraw,5301602580000000,0.00530160258,0.0002 +pallet_farming,claim,5131858760000000,0.00513185876,0.0002 +pallet_farming,withdraw_claim,5131858760000000,0.00513185876,0.0002 +pallet_farming,close_pool,5128700010000000,0.00512870001,0.0002 +pallet_farming,set_retire_limit,5008700010000000,0.00500870001,0.0002 +pallet_farming,retire_pool,5128700010000000,0.00512870001,0.0002 +pallet_farming,reset_pool,6138700010000000,0.00613870001,0.0002 +pallet_farming,kill_pool,5128700010000000,0.00512870001,0.0002 +pallet_farming,edit_pool,6568700010000000,0.00656870001,0.0002 +pallet_farming,gauge_withdraw,5132146160000000,0.00513214616,0.0002 +pallet_farming,force_gauge_claim,5128700010000000,0.00512870001,0.0002 +pallet_lottery,deposit,5149392860640000,0.00514939286064,0.0002 +pallet_lottery,request_withdraw,5142131693200000,0.0051421316932,0.0002 +pallet_lottery,claim_my_winnings,4974532813190000,0.00497453281319,0.0002 +pallet_lottery,rebalance_stake,4968700000000000,0.0049687,0.0002 +pallet_lottery,start_lottery,4972949880000000,0.00497294988,0.0002 +pallet_lottery,stop_lottery,4971537340000000,0.00497153734,0.0002 +pallet_lottery,draw_lottery,4974822260000000,0.00497482226,0.0002 +pallet_lottery,process_matured_withdrawals,4969856860000000,0.00496985686,0.0002 +pallet_lottery,liquidate_lottery,4968700000000000,0.0049687,0.0002 +pallet_lottery,set_min_deposit,5130036700000000,0.0051300367,0.0002 +pallet_lottery,set_min_withdraw,5129745420000000,0.00512974542,0.0002 +pallet_lottery,set_gas_reserve,5129745510000000,0.00512974551,0.0002 +zenlink_protocol,set_fee_receiver,5300032150000000,0.00530003215,0.0002 +zenlink_protocol,set_fee_point,4980026310000000,0.00498002631,0.0002 +zenlink_protocol,transfer,5428710000000000,0.00542871,0.0002 +zenlink_protocol,transfer,5231771630000000,0.00523177163,0.0002 +zenlink_protocol,add_liquidity,5316404550000000,0.00531640455,0.0002 +zenlink_protocol,remove_liquidity,5609241100000000,0.0056092411,0.0002 +zenlink_protocol,swap_exact_assets_for_assets,5727991860000000,0.00572799186,0.0002 +zenlink_protocol,swap_assets_for_exact_assets,5727992870000000,0.00572799287,0.0002 +zenlink_protocol,bootstrap_create,6222208840000000,0.00622220884,0.0002 +zenlink_protocol,bootstrap_contribute,5287398280000000,0.00528739828,0.0002 +zenlink_protocol,bootstrap_claim,5587287890000000,0.00558728789,0.0002 +zenlink_protocol,bootstrap_end,5268945340000000,0.00526894534,0.0002 +zenlink_protocol,bootstrap_update,6232524390000000,0.00623252439,0.0002 +zenlink_protocol,bootstrap_refund,5237140050000000,0.00523714005,0.0002 +zenlink_protocol,bootstrap_charge_reward,5819700000000000,0.0058197,0.0002 +zenlink_protocol,bootstrap_withdraw_reward,5549700000000000,0.0055497,0.0002 +pallet_native_barrier,initialize_native_barrier,4981338030000000,0.00498133803,0.0002 +pallet_native_barrier,add_accounts_to_native_barrier,4985476220000000,0.00498547622,0.0002 +pallet_native_barrier,remove_accounts_from_native_barrier,4983884450000000,0.00498388445,0.0002 +frame_system,remark-length=32,5430846460000000,0.00543084646,0.2 +frame_system,remark-length=64,5430846460000000,0.00543084646,0.2 +frame_system,set_heap_pages,7383150000000000,0.00738315,0.2 +frame_system,set_code,5005298700000000000,5.0052987,0.2 +frame_system,set_code_without_checks,5005298700000000000,5.0052987,0.2 +frame_system,set_storage,6685697510000000,0.00668569751,0.2 +frame_system,kill_storage,7689164760000000,0.00768916476,0.2 +frame_system,kill_prefix,14500273090000000,0.01450027309,0.2 +frame_system,remark_with_event-length=32,5434565040000000,0.00543456504,0.2 +frame_system,remark_with_event-length=64,5434565040000000,0.00543456504,0.2 +pallet_treasury,propose_spend,7889570000000000,0.00788957,0.2 +pallet_treasury,reject_proposal,8020730000000000,0.00802073,0.2 +pallet_treasury,approve_proposal,6738014970000000,0.00673801497,0.2 +pallet_treasury,spend,5310410000000000,0.00531041,0.2 +pallet_treasury,remove_approval,6334130000000000,0.00633413,0.2 +pallet_timestamp,set,6316530000000000,0.00631653,0.2 +pallet_preimage,note_preimage-length=32,7889492080000000,0.00788949208,0.2 +pallet_preimage,note_preimage-length=32,8220434160000000,0.00822043416,0.2 +pallet_preimage,unnote_preimage,7957970000000000,0.00795797,0.2 +pallet_preimage,request_preimage,6820410000000000,0.00682041,0.2 +pallet_preimage,unrequest_preimage,7818880000000000,0.00781888,0.2 +pallet_multisig,as_multi_threshold_1,9614664490000000,0.00961466449,0.2 +pallet_multisig,as_multi,8621404520000000,0.00862140452,0.2 +pallet_multisig,approve_as_multi,7562306370000000,0.00756230637,0.2 +pallet_multisig,cancel_as_multi,7355106670000000,0.00735510667,0.2 +pallet_membership,add_member,5798700000000000,0.0057987,0.2 +pallet_membership,remove_member,5798700000000000,0.0057987,0.2 +pallet_membership,swap_member,6128700000000000,0.0061287,0.2 +pallet_membership,reset_members,6118700000000000,0.0061187,0.2 +pallet_membership,change_key,5798700000000000,0.0057987,0.2 +pallet_membership,set_prime,5798700000000000,0.0057987,0.2 +pallet_membership,clear_prime,5468700000000000,0.0054687,0.2 +pallet_democracy,propose,9324850000000000,0.00932485,0.2 +pallet_democracy,second,6706550000000000,0.00670655,0.2 +pallet_democracy,vote,9558350000000000,0.00955835,0.2 +pallet_democracy,emergency_cancel,7757860000000000,0.00775786,0.2 +pallet_democracy,external_propose,6710610000000000,0.00671061,0.2 +pallet_democracy,external_propose_majority,6075300000000000,0.0060753,0.2 +pallet_democracy,external_propose_default,6071630000000000,0.00607163,0.2 +pallet_democracy,fast_track,9108490000000000,0.00910849,0.2 +pallet_democracy,veto_external,8112850000000000,0.00811285,0.2 +pallet_democracy,cancel_referendum,6132820000000000,0.00613282,0.2 +pallet_democracy,delegate,139869211750000000,0.13986921175,0.2 +pallet_democracy,undelegate,136637290710000000,0.13663729071,0.2 +pallet_democracy,clear_public_proposals,6025580000000000,0.00602558,0.2 +pallet_democracy,unlock,9527223210000000,0.00952722321,0.2 +pallet_democracy,remove_vote,7860770440000000,0.00786077044,0.2 +pallet_democracy,remove_other_vote,8189918650000000,0.00818991865,0.2 +pallet_democracy,blacklist,13498820000000000,0.01349882,0.2 +pallet_democracy,cancel_proposal,9491760000000000,0.00949176,0.2 +pallet_collective,set_members,142773807120000000,0.14277380712,0.2 +pallet_collective,execute,10041635810000000,0.01004163581,0.2 +pallet_collective,propose,10551516700000000,0.0105515167,0.2 +pallet_collective,vote,7228217400000000,0.0072282174,0.2 +pallet_collective,close,10296160860000000,0.01029616086,0.2 +pallet_collective,disapprove_proposal,8954546660000000,0.00895454666,0.2 +cumulus_pallet_xcmp_queue,service_overweight,5138700640000000,0.00513870064,0.2 +cumulus_pallet_xcmp_queue,suspend_xcm_execution,5968700000000000,0.0059687,0.2 +cumulus_pallet_xcmp_queue,resume_xcm_execution,5968700000000000,0.0059687,0.2 +cumulus_pallet_xcmp_queue,update_suspend_threshold,6333790000000000,0.00633379,0.2 +cumulus_pallet_xcmp_queue,update_drop_threshold,6333790000000000,0.00633379,0.2 +cumulus_pallet_xcmp_queue,update_resume_threshold,6333790000000000,0.00633379,0.2 +cumulus_pallet_xcmp_queue,update_threshold_weight,6470150000000000,0.00647015,0.2 +cumulus_pallet_xcmp_queue,update_weight_restrict_decay,6470150000000000,0.00647015,0.2 +cumulus_pallet_xcmp_queue,update_xcmp_max_individual_weight,6470150000000000,0.00647015,0.2 +orml_xtokens,transfer,5338700000000000,0.0053387,0.2 +orml_xtokens,transfer_multiasset,5148700000000000,0.0051487,0.2 +orml_xtokens,transfer_with_fee,5498700000000000,0.0054987,0.2 +orml_xtokens,transfer_multiasset_with_fee,5288700000000000,0.0052887,0.2 +orml_xtokens,transfer_multicurrencies,5718700000000000,0.0057187,0.2 +orml_xtokens,transfer_multiassets,5198700000000000,0.0051987,0.2 +manta_collator_selection,set_invulnerables,6444218220000000,0.00644421822,0.2 +manta_collator_selection,set_desired_candidates,6169090000000000,0.00616909,0.2 +manta_collator_selection,set_candidacy_bond,6270280000000000,0.00627028,0.2 +manta_collator_selection,register_as_candidate,7835236980000000,0.00783523698,0.2 +manta_collator_selection,register_candidate,8170899940000000,0.00817089994,0.2 +manta_collator_selection,leave_intent,6665439710000000,0.00666543971,0.2 +manta_collator_selection,remove_collator,7260625700000000,0.0072606257,0.2 +manta_collator_selection,set_eviction_baseline,6114600000000000,0.0061146,0.2 +manta_collator_selection,set_eviction_tolerance,6252240000000000,0.00625224,0.2 +pallet_asset_manager,register_asset,12915890000000000,0.01291589,0.2 +pallet_asset_manager,update_asset_location,11941040000000000,0.01194104,0.2 +pallet_asset_manager,update_asset_metadata,8919460000000000,0.00891946,0.2 +pallet_asset_manager,set_units_per_second,6830500000000000,0.0068305,0.2 +pallet_asset_manager,mint_asset,9215900000000000,0.0092159,0.2 +pallet_asset_manager,set_min_xcm_fee,6500660000000000,0.00650066,0.2 +pallet_asset_manager,update_outgoing_filtered_assets,6496500000000000,0.0064965,0.2 +pallet_asset_manager,register_lp_asset,13757390000000000,0.01375739,0.2 +pallet_asset_manager,permissionless_register_asset,13343830000000000,0.01334383,0.2 +pallet_assets,create,7084580000000000,0.00708458,0.2 +pallet_assets,force_create,6933200000000000,0.0069332,0.2 +pallet_assets,start_destroy,6600510000000000,0.00660051,0.2 +pallet_assets,destroy_accounts,2667867320000000000,2.66786732,0.2 +pallet_assets,destroy_approvals,1337232800000000000,1.3372328,0.2 +pallet_assets,finish_destroy,7077960000000000,0.00707796,0.2 +pallet_assets,mint,8322310000000000,0.00832231,0.2 +pallet_assets,burn,8444720000000000,0.00844472,0.2 +pallet_assets,transfer,11029160000000000,0.01102916,0.2 +pallet_assets,transfer_keep_alive,10986020000000000,0.01098602,0.2 +pallet_assets,force_transfer,11388330000000000,0.01138833,0.2 +pallet_assets,freeze,7218000000000000,0.007218,0.2 +pallet_assets,thaw,7230490000000000,0.00723049,0.2 +pallet_assets,freeze_asset,6594720000000000,0.00659472,0.2 +pallet_assets,thaw_asset,6596820000000000,0.00659682,0.2 +pallet_assets,transfer_ownership,7203130000000000,0.00720313,0.2 +pallet_assets,set_team,7583890000000000,0.00758389,0.2 +pallet_assets,set_metadata,7563845370000000,0.00756384537,0.2 +pallet_assets,clear_metadata,6879980000000000,0.00687998,0.2 +pallet_assets,force_set_metadata,7552081150000000,0.00755208115,0.2 +pallet_assets,force_clear_metadata,6878660000000000,0.00687866,0.2 +pallet_assets,force_asset_status,7935690000000000,0.00793569,0.2 +pallet_assets,approve_transfer,8241060000000000,0.00824106,0.2 +pallet_assets,cancel_approval,8267930000000000,0.00826793,0.2 +pallet_assets,force_cancel_approval,8587760000000000,0.00858776,0.2 +pallet_assets,transfer_approved,12723410000000000,0.01272341,0.2 +pallet_assets,touch,7982310000000000,0.00798231,0.2 +pallet_assets,refund,7992310000000000,0.00799231,0.2 +pallet_author_inherent,kick_off_authorship_validation,7147660000000000,0.00714766,0.2 +pallet_balances,transfer-1,7049260000000000,0.00704926,0.2 +pallet_balances,transfer-1000_000,7079260000000000,0.00707926,0.2 +pallet_balances,set_balance,6863100000000000,0.0068631,0.2 +pallet_balances,force_transfer,8609950000000000,0.00860995,0.2 +pallet_balances,transfer_keep_alive-1,6918640000000000,0.00691864,0.2 +pallet_balances,transfer_keep_alive-1000_000,6948640000000000,0.00694864,0.2 +pallet_balances,transfer_all,6977240000000000,0.00697724,0.2 +pallet_balances,force_unreserve,6925690000000000,0.00692569,0.2 +pallet_manta_pay,to_private,412017590000000000,0.41201759,0.2 +pallet_manta_pay,private_transfer,732856720000000000,0.73285672,0.2 +pallet_manta_pay,to_public,549414830000000000,0.54941483,0.2 +pallet_manta_pay,public_transfer,10047380000000000,0.01004738,0.2 +pallet_manta_sbt,to_private,407627130000000000,0.40762713,0.2 +pallet_manta_sbt,force_to_private,405810410000000000,0.40581041,0.2 +pallet_manta_sbt,reserve_sbt,9525360000000000,0.00952536,0.2 +pallet_manta_sbt,allowlist_evm_account,8777610000000000,0.00877761,0.2 +pallet_manta_sbt,set_next_sbt_id,6299300000000000,0.0062993,0.2 +pallet_manta_sbt,remove_allowlist_evm_account,6393350000000000,0.00639335,0.2 +pallet_manta_sbt,change_free_reserve_account,6633390000000000,0.00663339,0.2 +pallet_manta_sbt,force_mint_sbt_eth,408114140000000000,0.40811414,0.2 +pallet_manta_sbt,change_force_account,6466950000000000,0.00646695,0.2 +pallet_manta_sbt,mint_sbt_eth,408725380000000000,0.40872538,0.2 +pallet_manta_sbt,change_allowlist_account,6466540000000000,0.00646654,0.2 +pallet_manta_sbt,update_mint_info,7607910000000000,0.00760791,0.2 +pallet_manta_sbt,new_mint_info,8533870000000000,0.00853387,0.2 +pallet_parachain_staking,set_staking_expectations,6918470000000000,0.00691847,0.2 +pallet_parachain_staking,set_inflation,7024030000000000,0.00702403,0.2 +pallet_parachain_staking,set_parachain_bond_account,6761110000000000,0.00676111,0.2 +pallet_parachain_staking,set_parachain_bond_account,6761110000000000,0.00676111,0.2 +pallet_parachain_staking,set_parachain_bond_reserve_percent,6442140000000000,0.00644214,0.2 +pallet_parachain_staking,set_total_selected,6455000000000000,0.006455,0.2 +pallet_parachain_staking,set_collator_commission,6426630000000000,0.00642663,0.2 +pallet_parachain_staking,set_blocks_per_round,7207260000000000,0.00720726,0.2 +pallet_parachain_staking,join_candidates,14444210680000000,0.01444421068,0.2 +pallet_parachain_staking,schedule_leave_candidates,7869189840000000,0.00786918984,0.2 +pallet_parachain_staking,execute_leave_candidates,19252654580000000,0.01925265458,0.2 +pallet_parachain_staking,cancel_leave_candidates,7848448780000000,0.00784844878,0.2 +pallet_parachain_staking,go_offline,7792700000000000,0.0077927,0.2 +pallet_parachain_staking,go_offline,7792700000000000,0.0077927,0.2 +pallet_parachain_staking,candidate_bond_more,11776210000000000,0.01177621,0.2 +pallet_parachain_staking,schedule_candidate_bond_less,6663900000000000,0.0066639,0.2 +pallet_parachain_staking,execute_candidate_bond_less,12181320000000000,0.01218132,0.2 +pallet_parachain_staking,cancel_candidate_bond_less,6490840000000000,0.00649084,0.2 +pallet_parachain_staking,delegate_1_2_3,14909101240000000,0.01490910124,0.2 +pallet_parachain_staking,delegate_1_25_3,14975537890000000,0.01497553789,0.2 +pallet_parachain_staking,delegate_1_2_100,15086233210000000,0.01508623321,0.2 +pallet_parachain_staking,schedule_leave_delegators,7794680000000000,0.00779468,0.2 +pallet_parachain_staking,execute_leave_delegators,20188523450000000,0.02018852345,0.2 +pallet_parachain_staking,cancel_leave_delegators,7838310000000000,0.00783831,0.2 +pallet_parachain_staking,schedule_revoke_delegation,8115700000000000,0.0081157,0.2 +pallet_parachain_staking,delegator_bond_more,15035010000000000,0.01503501,0.2 +pallet_parachain_staking,schedule_delegator_bond_less,8144230000000000,0.00814423,0.2 +pallet_parachain_staking,execute_delegation_request,16487790000000000,0.01648779,0.2 +pallet_parachain_staking,cancel_delegation_request,8180030000000000,0.00818003,0.2 +pallet_scheduler,cancel,8266876790000000,0.00826687679,0.2 +pallet_scheduler,schedule,7029973350000000,0.00702997335,0.2 +pallet_scheduler,schedule_named,8740298540000000,0.00874029854,0.2 +pallet_scheduler,cancel_named,8783159230000000,0.00878315923,0.2 +pallet_scheduler,schedule_after,7269973350000000,0.00726997335,0.2 +pallet_scheduler,schedule_named_after,8980298540000000,0.00898029854,0.2 +pallet_session,set_keys,9967770000000000,0.00996777,0.2 +pallet_session,purge_keys,8441990000000000,0.00844199,0.2 +pallet_tx_pause,pause_transaction,7080880000000000,0.00708088,0.2 +pallet_tx_pause,unpause_transaction,7095850000000000,0.00709585,0.2 +pallet_tx_pause,pause_transactions,8883060000000000,0.00888306,0.2 +pallet_tx_pause,unpause_transactions,8913000000000000,0.008913,0.2 +pallet_tx_pause,pause_pallets,78247700000000000,0.0782477,0.2 +pallet_tx_pause,unpause_pallets,78247700000000000,0.0782477,0.2 +pallet_utility,batch-size=1,5755830520000000,0.00575583052,0.2 +pallet_utility,batch-size=32,19060089360000000,0.01906008936,0.2 +pallet_utility,as_derivative,20421769360000000,0.02042176936,0.2 +pallet_utility,batch_all-size=1,5764485630000000,0.00576448563,0.2 +pallet_utility,batch_all-size=32,19337052880000000,0.01933705288,0.2 +pallet_utility,dispatch_as,19850042880000000,0.01985004288,0.2 +pallet_utility,force_batch,5322325280000000,0.00532232528,0.2 +pallet_randomness,set_babe_randomness_results,8868920000000000,0.00886892,0.2 +pallet_name_service,register,8608240000000000,0.00860824,0.2 +pallet_name_service,accept_register,7865130000000000,0.00786513,0.2 +pallet_name_service,set_primary_name,7117930000000000,0.00711793,0.2 +pallet_name_service,cancel_pending_register,6841860000000000,0.00684186,0.2 +pallet_name_service,remove_register,7117860000000000,0.00711786,0.2 +pallet_farming,create_farming_pool,11128800000000000,0.0111288,0.2 +pallet_farming,charge,12712950000000000,0.01271295,0.2 +pallet_farming,deposit,13604900000000000,0.0136049,0.2 +pallet_farming,withdraw,8201280000000000,0.00820128,0.2 +pallet_farming,claim,8287460000000000,0.00828746,0.2 +pallet_farming,withdraw_claim,8287460000000000,0.00828746,0.2 +pallet_farming,close_pool,5128710000000000,0.00512871,0.2 +pallet_farming,set_retire_limit,5008710000000000,0.00500871,0.2 +pallet_farming,retire_pool,5128710000000000,0.00512871,0.2 +pallet_farming,reset_pool,6138710000000000,0.00613871,0.2 +pallet_farming,kill_pool,5128710000000000,0.00512871,0.2 +pallet_farming,edit_pool,6568710000000000,0.00656871,0.2 +pallet_farming,gauge_withdraw,8574860000000000,0.00857486,0.2 +pallet_farming,force_gauge_claim,5128710000000000,0.00512871,0.2 +pallet_lottery,deposit,25821560640000000,0.02582156064,0.2 +pallet_lottery,request_withdraw,18560393200000000,0.0185603932,0.2 +pallet_lottery,claim_my_winnings,10801513190000000,0.01080151319,0.2 +pallet_lottery,rebalance_stake,4968700000000000,0.0049687,0.2 +pallet_lottery,start_lottery,9218580000000000,0.00921858,0.2 +pallet_lottery,stop_lottery,7806040000000000,0.00780604,0.2 +pallet_lottery,draw_lottery,11090960000000000,0.01109096,0.2 +pallet_lottery,process_matured_withdrawals,6125560000000000,0.00612556,0.2 +pallet_lottery,liquidate_lottery,4968700000000000,0.0049687,0.2 +pallet_lottery,set_min_deposit,6465400000000000,0.0064654,0.2 +pallet_lottery,set_min_withdraw,6174120000000000,0.00617412,0.2 +pallet_lottery,set_gas_reserve,6174210000000000,0.00617421,0.2 +zenlink_protocol,set_fee_receiver,6630850000000000,0.00663085,0.2 +zenlink_protocol,set_fee_point,6305010000000000,0.00630501,0.2 +zenlink_protocol,transfer,5438700000000000,0.0054387,0.2 +zenlink_protocol,transfer,8300330000000000,0.00830033,0.2 +zenlink_protocol,add_liquidity,42983250000000000,0.04298325,0.2 +zenlink_protocol,remove_liquidity,26129800000000000,0.0261298,0.2 +zenlink_protocol,swap_exact_assets_for_assets,15010560000000000,0.01501056,0.2 +zenlink_protocol,swap_assets_for_exact_assets,15011570000000000,0.01501157,0.2 +zenlink_protocol,bootstrap_create,9727540000000000,0.00972754,0.2 +zenlink_protocol,bootstrap_contribute,13976980000000000,0.01397698,0.2 +zenlink_protocol,bootstrap_claim,34146590000000000,0.03414659,0.2 +zenlink_protocol,bootstrap_end,45474040000000000,0.04547404,0.2 +zenlink_protocol,bootstrap_update,10053090000000000,0.01005309,0.2 +zenlink_protocol,bootstrap_refund,13668750000000000,0.01366875,0.2 +zenlink_protocol,bootstrap_charge_reward,6818700000000000,0.0068187,0.2 +zenlink_protocol,bootstrap_withdraw_reward,6548700000000000,0.0065487,0.2 +pallet_native_barrier,initialize_native_barrier,7616730000000000,0.00761673,0.2 +pallet_native_barrier,add_accounts_to_native_barrier,11754920000000000,0.01175492,0.2 +pallet_native_barrier,remove_accounts_from_native_barrier,10163150000000000,0.01016315,0.2 +frame_system,remark-length=32,5959432300000000,0.0059594323,1 +frame_system,remark-length=64,5959432300000000,0.0059594323,1 +frame_system,set_heap_pages,16720950000000000,0.01672095,1 +frame_system,set_code,25005298700000000000,25.0052987,1 +frame_system,set_code_without_checks,25005298700000000000,25.0052987,1 +frame_system,set_storage,10873687550000000,0.01087368755,1 +frame_system,kill_storage,15891023800000000,0.0158910238,1 +frame_system,kill_prefix,51146565450000000,0.05114656545,1 +frame_system,remark_with_event-length=32,5978025200000000,0.0059780252,1 +frame_system,remark_with_event-length=64,5978025200000000,0.0059780252,1 +pallet_treasury,propose_spend,18213050000000000,0.01821305,1 +pallet_treasury,reject_proposal,20188850000000000,0.02018885,1 +pallet_treasury,approve_proposal,13775274850000000,0.01377527485,1 +pallet_treasury,spend,5317250000000000,0.00531725,1 +pallet_treasury,remove_approval,11755850000000000,0.01175585,1 +pallet_timestamp,set,11667850000000000,0.01166785,1 +pallet_preimage,note_preimage-length=32,18252660400000000,0.0182526604,1 +pallet_preimage,note_preimage-length=32,18587370800000000,0.0185873708,1 +pallet_preimage,unnote_preimage,18635050000000000,0.01863505,1 +pallet_preimage,request_preimage,12947250000000000,0.01294725,1 +pallet_preimage,unrequest_preimage,17939600000000000,0.0179396,1 +pallet_multisig,as_multi_threshold_1,25518522450000000,0.02551852245,1 +pallet_multisig,as_multi,20352222600000000,0.0203522226,1 +pallet_multisig,approve_as_multi,15136731850000000,0.01513673185,1 +pallet_multisig,cancel_as_multi,13900733350000000,0.01390073335,1 +pallet_membership,add_member,7798700000000000,0.0077987,1 +pallet_membership,remove_member,7798700000000000,0.0077987,1 +pallet_membership,swap_member,8128700000000000,0.0081287,1 +pallet_membership,reset_members,8118700000000000,0.0081187,1 +pallet_membership,change_key,7798700000000000,0.0077987,1 +pallet_membership,set_prime,7798700000000000,0.0077987,1 +pallet_membership,clear_prime,7468700000000000,0.0074687,1 +pallet_democracy,propose,26509450000000000,0.02650945,1 +pallet_democracy,second,13617950000000000,0.01361795,1 +pallet_democracy,vote,27156950000000000,0.02715695,1 +pallet_democracy,emergency_cancel,18754500000000000,0.0187545,1 +pallet_democracy,external_propose,13478250000000000,0.01347825,1 +pallet_democracy,external_propose_majority,10301700000000000,0.0103017,1 +pallet_democracy,external_propose_default,10283350000000000,0.01028335,1 +pallet_democracy,fast_track,24067650000000000,0.02406765,1 +pallet_democracy,veto_external,19409450000000000,0.01940945,1 +pallet_democracy,cancel_referendum,10749300000000000,0.0107493,1 +pallet_democracy,delegate,677471258750000000,0.67747125875,1 +pallet_democracy,undelegate,663311653550000000,0.66331165355,1 +pallet_democracy,clear_public_proposals,10253100000000000,0.0102531,1 +pallet_democracy,unlock,26441316050000000,0.02644131605,1 +pallet_democracy,remove_vote,19269052200000000,0.0192690522,1 +pallet_democracy,remove_other_vote,19594793250000000,0.01959479325,1 +pallet_democracy,blacklist,46139300000000000,0.0461393,1 +pallet_democracy,cancel_proposal,27544000000000000,0.027544,1 +pallet_collective,set_members,689914235600000000,0.6899142356,1 +pallet_collective,execute,30173379050000000,0.03017337905,1 +pallet_collective,propose,32682783500000000,0.0326827835,1 +pallet_collective,vote,14906287000000000,0.014906287,1 +pallet_collective,close,30166004300000000,0.0301660043,1 +pallet_collective,disapprove_proposal,23617933300000000,0.0236179333,1 +cumulus_pallet_xcmp_queue,service_overweight,5178703200000000,0.0051787032,1 +cumulus_pallet_xcmp_queue,suspend_xcm_execution,9968700000000000,0.0099687,1 +cumulus_pallet_xcmp_queue,resume_xcm_execution,9968700000000000,0.0099687,1 +cumulus_pallet_xcmp_queue,update_suspend_threshold,11634150000000000,0.01163415,1 +cumulus_pallet_xcmp_queue,update_drop_threshold,11634150000000000,0.01163415,1 +cumulus_pallet_xcmp_queue,update_resume_threshold,11634150000000000,0.01163415,1 +cumulus_pallet_xcmp_queue,update_threshold_weight,12155950000000000,0.01215595,1 +cumulus_pallet_xcmp_queue,update_weight_restrict_decay,12155950000000000,0.01215595,1 +cumulus_pallet_xcmp_queue,update_xcmp_max_individual_weight,12155950000000000,0.01215595,1 +orml_xtokens,transfer,5338700000000000,0.0053387,1 +orml_xtokens,transfer_multiasset,5148700000000000,0.0051487,1 +orml_xtokens,transfer_with_fee,5498700000000000,0.0054987,1 +orml_xtokens,transfer_multiasset_with_fee,5288700000000000,0.0052887,1 +orml_xtokens,transfer_multicurrencies,5718700000000000,0.0057187,1 +orml_xtokens,transfer_multiassets,5198700000000000,0.0051987,1 +manta_collator_selection,set_invulnerables,11026291100000000,0.0110262911,1 +manta_collator_selection,set_desired_candidates,10810650000000000,0.01081065,1 +manta_collator_selection,set_candidacy_bond,10836600000000000,0.0108366,1 +manta_collator_selection,register_as_candidate,19301384900000000,0.0193013849,1 +manta_collator_selection,register_candidate,19699699700000000,0.0196996997,1 +manta_collator_selection,leave_intent,13452398550000000,0.01345239855,1 +manta_collator_selection,remove_collator,15148328500000000,0.0151483285,1 +manta_collator_selection,set_eviction_baseline,10658200000000000,0.0106582,1 +manta_collator_selection,set_eviction_tolerance,11346400000000000,0.0113464,1 +pallet_asset_manager,register_asset,43344650000000000,0.04334465,1 +pallet_asset_manager,update_asset_location,39670400000000000,0.0396704,1 +pallet_asset_manager,update_asset_metadata,23522500000000000,0.0235225,1 +pallet_asset_manager,set_units_per_second,14197700000000000,0.0141977,1 +pallet_asset_manager,mint_asset,24244700000000000,0.0242447,1 +pallet_asset_manager,set_min_xcm_fee,12468500000000000,0.0124685,1 +pallet_asset_manager,update_outgoing_filtered_assets,12447700000000000,0.0124477,1 +pallet_asset_manager,register_lp_asset,46392150000000000,0.04639215,1 +pallet_asset_manager,permissionless_register_asset,46084350000000000,0.04608435,1 +pallet_assets,create,12948100000000000,0.0129481,1 +pallet_assets,force_create,12751200000000000,0.0127512,1 +pallet_assets,start_destroy,12487750000000000,0.01248775,1 +pallet_assets,destroy_accounts,13318821800000000000,13.3188218,1 +pallet_assets,destroy_approvals,6665649200000000000,6.6656492,1 +pallet_assets,finish_destroy,14875000000000000,0.014875,1 +pallet_assets,mint,19736750000000000,0.01973675,1 +pallet_assets,burn,20348800000000000,0.0203488,1 +pallet_assets,transfer,33271000000000000,0.033271,1 +pallet_assets,transfer_keep_alive,33055300000000000,0.0330553,1 +pallet_assets,force_transfer,33746850000000000,0.03374685,1 +pallet_assets,freeze,14255200000000000,0.0142552,1 +pallet_assets,thaw,14317650000000000,0.01431765,1 +pallet_assets,freeze_asset,12458800000000000,0.0124588,1 +pallet_assets,thaw_asset,12469300000000000,0.0124693,1 +pallet_assets,transfer_ownership,14180850000000000,0.01418085,1 +pallet_assets,set_team,13444650000000000,0.01344465,1 +pallet_assets,set_metadata,14624426850000000,0.01462442685,1 +pallet_assets,clear_metadata,13885100000000000,0.0138851,1 +pallet_assets,force_set_metadata,14525605750000000,0.01452560575,1 +pallet_assets,force_clear_metadata,13878500000000000,0.0138785,1 +pallet_assets,force_asset_status,13763650000000000,0.01376365,1 +pallet_assets,approve_transfer,19330500000000000,0.0193305,1 +pallet_assets,cancel_approval,19504850000000000,0.01950485,1 +pallet_assets,force_cancel_approval,19784000000000000,0.019784,1 +pallet_assets,transfer_approved,40422250000000000,0.04042225,1 +pallet_assets,touch,19396750000000000,0.01939675,1 +pallet_assets,refund,19406750000000000,0.01940675,1 +pallet_author_inherent,kick_off_authorship_validation,15863500000000000,0.0158635,1 +pallet_balances,transfer-1,14011500000000000,0.0140115,1 +pallet_balances,transfer-1000_000,14041500000000000,0.0140415,1 +pallet_balances,set_balance,13040700000000000,0.0130407,1 +pallet_balances,force_transfer,20494950000000000,0.02049495,1 +pallet_balances,transfer_keep_alive-1,13358400000000000,0.0133584,1 +pallet_balances,transfer_keep_alive-1000_000,13388400000000000,0.0133884,1 +pallet_balances,transfer_all,13651400000000000,0.0136514,1 +pallet_balances,force_unreserve,12793650000000000,0.01279365,1 +pallet_manta_pay,to_private,2034813150000000000,2.03481315,1 +pallet_manta_pay,private_transfer,3639008800000000000,3.6390088,1 +pallet_manta_pay,to_public,2721799350000000000,2.72179935,1 +pallet_manta_pay,public_transfer,27162100000000000,0.0271621,1 +pallet_manta_sbt,to_private,2012700850000000000,2.01270085,1 +pallet_manta_sbt,force_to_private,2002297250000000000,2.00229725,1 +pallet_manta_sbt,reserve_sbt,27712000000000000,0.027712,1 +pallet_manta_sbt,allowlist_evm_account,23053250000000000,0.02305325,1 +pallet_manta_sbt,set_next_sbt_id,10941700000000000,0.0109417,1 +pallet_manta_sbt,remove_allowlist_evm_account,11131950000000000,0.01113195,1 +pallet_manta_sbt,change_free_reserve_account,11972150000000000,0.01197215,1 +pallet_manta_sbt,force_mint_sbt_eth,2011655900000000000,2.0116559,1 +pallet_manta_sbt,change_force_account,11139950000000000,0.01113995,1 +pallet_manta_sbt,mint_sbt_eth,2013872100000000000,2.0138721,1 +pallet_manta_sbt,change_allowlist_account,11137900000000000,0.0111379,1 +pallet_manta_sbt,update_mint_info,17564750000000000,0.01756475,1 +pallet_manta_sbt,new_mint_info,22354550000000000,0.02235455,1 +pallet_parachain_staking,set_staking_expectations,12797550000000000,0.01279755,1 +pallet_parachain_staking,set_inflation,14765350000000000,0.01476535,1 +pallet_parachain_staking,set_parachain_bond_account,12650750000000000,0.01265075,1 +pallet_parachain_staking,set_parachain_bond_account,12650750000000000,0.01265075,1 +pallet_parachain_staking,set_parachain_bond_reserve_percent,12295900000000000,0.0122959,1 +pallet_parachain_staking,set_total_selected,12360200000000000,0.0123602,1 +pallet_parachain_staking,set_collator_commission,12218350000000000,0.01221835,1 +pallet_parachain_staking,set_blocks_per_round,16121500000000000,0.0161215,1 +pallet_parachain_staking,join_candidates,52266253400000000,0.0522662534,1 +pallet_parachain_staking,schedule_leave_candidates,19431149200000000,0.0194311492,1 +pallet_parachain_staking,execute_leave_candidates,75068472900000000,0.0750684729,1 +pallet_parachain_staking,cancel_leave_candidates,19327443900000000,0.0193274439,1 +pallet_parachain_staking,go_offline,19088700000000000,0.0190887,1 +pallet_parachain_staking,go_offline,19088700000000000,0.0190887,1 +pallet_parachain_staking,candidate_bond_more,38966250000000000,0.03896625,1 +pallet_parachain_staking,schedule_candidate_bond_less,12804700000000000,0.0128047,1 +pallet_parachain_staking,execute_candidate_bond_less,39751800000000000,0.0397518,1 +pallet_parachain_staking,cancel_candidate_bond_less,12579400000000000,0.0125794,1 +pallet_parachain_staking,delegate_1_2_3,53270706200000000,0.0532707062,1 +pallet_parachain_staking,delegate_1_25_3,53602889450000000,0.05360288945,1 +pallet_parachain_staking,delegate_1_2_100,54116366050000000,0.05411636605,1 +pallet_parachain_staking,schedule_leave_delegators,19098600000000000,0.0190986,1 +pallet_parachain_staking,execute_leave_delegators,79747817250000000,0.07974781725,1 +pallet_parachain_staking,cancel_leave_delegators,19316750000000000,0.01931675,1 +pallet_parachain_staking,schedule_revoke_delegation,19423700000000000,0.0194237,1 +pallet_parachain_staking,delegator_bond_more,53980250000000000,0.05398025,1 +pallet_parachain_staking,schedule_delegator_bond_less,19526350000000000,0.01952635,1 +pallet_parachain_staking,execute_delegation_request,60004150000000000,0.06000415,1 +pallet_parachain_staking,cancel_delegation_request,19745350000000000,0.01974535,1 +pallet_scheduler,cancel,21139583950000000,0.02113958395,1 +pallet_scheduler,schedule,14635066750000000,0.01463506675,1 +pallet_scheduler,schedule_named,21586692700000000,0.0215866927,1 +pallet_scheduler,cancel_named,22760996150000000,0.02276099615,1 +pallet_scheduler,schedule_after,14875066750000000,0.01487506675,1 +pallet_scheduler,schedule_named_after,21826692700000000,0.0218266927,1 +pallet_session,set_keys,26084050000000000,0.02608405,1 +pallet_session,purge_keys,22335150000000000,0.02233515,1 +pallet_tx_pause,pause_transaction,12889600000000000,0.0128896,1 +pallet_tx_pause,unpause_transaction,12964450000000000,0.01296445,1 +pallet_tx_pause,pause_transactions,20500500000000000,0.0205005,1 +pallet_tx_pause,unpause_transactions,20650200000000000,0.0206502,1 +pallet_tx_pause,pause_pallets,368683700000000000,0.3686837,1 +pallet_tx_pause,unpause_pallets,368683700000000000,0.3686837,1 +pallet_utility,batch-size=1,8744352600000000,0.0087443526,1 +pallet_utility,batch-size=32,71545646800000000,0.0715456468,1 +pallet_utility,as_derivative,78194046800000000,0.0781940468,1 +pallet_utility,batch_all-size=1,8787628150000000,0.00878762815,1 +pallet_utility,batch_all-size=32,72930464400000000,0.0729304644,1 +pallet_utility,dispatch_as,74055414400000000,0.0740554144,1 +pallet_utility,force_batch,6696826400000000,0.0066968264,1 +pallet_randomness,set_babe_randomness_results,24469800000000000,0.0244698,1 +pallet_name_service,register,21686400000000000,0.0216864,1 +pallet_name_service,accept_register,17970850000000000,0.01797085,1 +pallet_name_service,set_primary_name,14234850000000000,0.01423485,1 +pallet_name_service,cancel_pending_register,12854500000000000,0.0128545,1 +pallet_name_service,remove_register,14234500000000000,0.0142345,1 +pallet_farming,create_farming_pool,31169200000000000,0.0311692,1 +pallet_farming,charge,40449950000000000,0.04044995,1 +pallet_farming,deposit,46589700000000000,0.0465897,1 +pallet_farming,withdraw,19811600000000000,0.0198116,1 +pallet_farming,claim,20922500000000000,0.0209225,1 +pallet_farming,withdraw_claim,20922500000000000,0.0209225,1 +pallet_farming,close_pool,5128750000000000,0.00512875,1 +pallet_farming,set_retire_limit,5008750000000000,0.00500875,1 +pallet_farming,retire_pool,5128750000000000,0.00512875,1 +pallet_farming,reset_pool,6138750000000000,0.00613875,1 +pallet_farming,kill_pool,5128750000000000,0.00512875,1 +pallet_farming,edit_pool,6568750000000000,0.00656875,1 +pallet_farming,gauge_withdraw,22359500000000000,0.0223595,1 +pallet_farming,force_gauge_claim,5128750000000000,0.00512875,1 +pallet_lottery,deposit,108593003200000000,0.1085930032,1 +pallet_lottery,request_withdraw,72287166000000000,0.072287166,1 +pallet_lottery,claim_my_winnings,34132765950000000,0.03413276595,1 +pallet_lottery,rebalance_stake,4968700000000000,0.0049687,1 +pallet_lottery,start_lottery,26218100000000000,0.0262181,1 +pallet_lottery,stop_lottery,19155400000000000,0.0191554,1 +pallet_lottery,draw_lottery,35580000000000000,0.03558,1 +pallet_lottery,process_matured_withdrawals,10753000000000000,0.010753,1 +pallet_lottery,liquidate_lottery,4968700000000000,0.0049687,1 +pallet_lottery,set_min_deposit,11812200000000000,0.0118122,1 +pallet_lottery,set_min_withdraw,10355800000000000,0.0103558,1 +pallet_lottery,set_gas_reserve,10356250000000000,0.01035625,1 +zenlink_protocol,set_fee_receiver,11959450000000000,0.01195945,1 +zenlink_protocol,set_fee_point,11610250000000000,0.01161025,1 +zenlink_protocol,transfer,5478700000000000,0.0054787,1 +zenlink_protocol,transfer,20586850000000000,0.02058685,1 +zenlink_protocol,add_liquidity,193801450000000000,0.19380145,1 +zenlink_protocol,remove_liquidity,108294200000000000,0.1082942,1 +zenlink_protocol,swap_exact_assets_for_assets,52178000000000000,0.052178,1 +zenlink_protocol,swap_assets_for_exact_assets,52183050000000000,0.05218305,1 +zenlink_protocol,bootstrap_create,23762900000000000,0.0237629,1 +zenlink_protocol,bootstrap_contribute,48770100000000000,0.0487701,1 +zenlink_protocol,bootstrap_claim,148498150000000000,0.14849815,1 +zenlink_protocol,bootstrap_end,206455400000000000,0.2064554,1 +zenlink_protocol,bootstrap_update,25350650000000000,0.02535065,1 +zenlink_protocol,bootstrap_refund,47428950000000000,0.04742895,1 +zenlink_protocol,bootstrap_charge_reward,10818700000000000,0.0108187,1 +zenlink_protocol,bootstrap_withdraw_reward,10548700000000000,0.0105487,1 +pallet_native_barrier,initialize_native_barrier,18168850000000000,0.01816885,1 +pallet_native_barrier,add_accounts_to_native_barrier,38859800000000000,0.0388598,1 +pallet_native_barrier,remove_accounts_from_native_barrier,30900950000000000,0.03090095,1