diff --git a/Cargo.lock b/Cargo.lock
index f48c82e40..0c3793408 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1118,8 +1118,8 @@ dependencies = [
"hex-literal",
"lazy_static",
"log",
- "manta-collator-selection",
- "manta-primitives",
+ "manta-collator-selection 4.4.0",
+ "manta-primitives 4.4.0",
"nimbus-primitives",
"orml-traits",
"orml-xtokens",
@@ -1142,7 +1142,7 @@ dependencies = [
"pallet-membership",
"pallet-multisig",
"pallet-name-service",
- "pallet-parachain-staking",
+ "pallet-parachain-staking 4.4.0",
"pallet-preimage",
"pallet-randomness",
"pallet-ranked-collective",
@@ -1164,11 +1164,11 @@ dependencies = [
"polkadot-primitives",
"polkadot-runtime-parachains",
"reqwest",
- "runtime-common",
+ "runtime-common 4.4.0",
"scale-info",
"serde",
"serde_json",
- "session-key-primitives",
+ "session-key-primitives 4.4.0",
"smallvec",
"sp-api",
"sp-application-crypto",
@@ -1203,7 +1203,7 @@ dependencies = [
"frame-benchmarking",
"frame-support",
"frame-system",
- "manta-primitives",
+ "manta-primitives 4.4.0",
"pallet-balances",
"pallet-timestamp",
"parity-scale-codec",
@@ -4176,8 +4176,8 @@ dependencies = [
"frame-support",
"frame-system",
"lazy_static",
- "manta-collator-selection",
- "manta-primitives",
+ "manta-collator-selection 4.4.0",
+ "manta-primitives 4.4.0",
"manta-runtime",
"nimbus-primitives",
"orml-traits",
@@ -4191,7 +4191,7 @@ dependencies = [
"pallet-manta-sbt",
"pallet-manta-support",
"pallet-membership",
- "pallet-parachain-staking",
+ "pallet-parachain-staking 4.4.0",
"pallet-scheduler",
"pallet-session",
"pallet-transaction-payment",
@@ -4203,9 +4203,9 @@ dependencies = [
"polkadot-core-primitives",
"polkadot-parachain",
"polkadot-runtime-parachains",
- "runtime-common",
+ "runtime-common 4.4.0",
"scale-info",
- "session-key-primitives",
+ "session-key-primitives 4.4.0",
"sp-arithmetic",
"sp-core",
"sp-io",
@@ -5314,7 +5314,7 @@ dependencies = [
"hex-literal",
"jsonrpsee",
"log",
- "manta-primitives",
+ "manta-primitives 4.4.0",
"manta-runtime",
"nimbus-consensus",
"nimbus-primitives",
@@ -5324,7 +5324,7 @@ dependencies = [
"pallet-lottery",
"pallet-manta-pay",
"pallet-manta-sbt",
- "pallet-parachain-staking",
+ "pallet-parachain-staking 4.4.0",
"pallet-transaction-payment-rpc",
"parity-scale-codec",
"polkadot-cli",
@@ -5352,7 +5352,7 @@ dependencies = [
"sc-transaction-pool",
"sc-transaction-pool-api",
"serde",
- "session-key-primitives",
+ "session-key-primitives 4.4.0",
"sp-api",
"sp-application-crypto",
"sp-arithmetic",
@@ -5403,7 +5403,7 @@ dependencies = [
"frame-support",
"frame-system",
"log",
- "manta-primitives",
+ "manta-primitives 4.4.0",
"nimbus-primitives",
"pallet-aura",
"pallet-authorship",
@@ -5424,6 +5424,27 @@ dependencies = [
"sp-tracing",
]
+[[package]]
+name = "manta-collator-selection"
+version = "4.4.0"
+source = "git+https://github.com/Manta-Network/Manta.git#0814f5d3c1490be7f6a04d0b41593c3da93e5800"
+dependencies = [
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "log",
+ "nimbus-primitives",
+ "pallet-authorship",
+ "pallet-session",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-arithmetic",
+ "sp-runtime",
+ "sp-staking",
+ "sp-std",
+]
+
[[package]]
name = "manta-crypto"
version = "0.5.15"
@@ -5503,6 +5524,27 @@ dependencies = [
"xcm-executor",
]
+[[package]]
+name = "manta-primitives"
+version = "4.4.0"
+source = "git+https://github.com/Manta-Network/Manta.git#0814f5d3c1490be7f6a04d0b41593c3da93e5800"
+dependencies = [
+ "frame-support",
+ "frame-system",
+ "log",
+ "orml-traits",
+ "parity-scale-codec",
+ "scale-info",
+ "smallvec",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+ "xcm",
+ "xcm-builder",
+ "xcm-executor",
+]
+
[[package]]
name = "manta-runtime"
version = "4.4.0"
@@ -5525,8 +5567,8 @@ dependencies = [
"frame-try-runtime",
"hex-literal",
"log",
- "manta-collator-selection",
- "manta-primitives",
+ "manta-collator-selection 4.4.0",
+ "manta-primitives 4.4.0",
"nimbus-primitives",
"orml-traits",
"orml-xtokens",
@@ -5548,7 +5590,7 @@ dependencies = [
"pallet-membership",
"pallet-multisig",
"pallet-name-service",
- "pallet-parachain-staking",
+ "pallet-parachain-staking 4.4.0",
"pallet-preimage",
"pallet-randomness",
"pallet-scheduler",
@@ -5568,10 +5610,10 @@ dependencies = [
"polkadot-parachain",
"polkadot-primitives",
"polkadot-runtime-parachains",
- "runtime-common",
+ "runtime-common 4.4.0",
"scale-info",
"serde",
- "session-key-primitives",
+ "session-key-primitives 4.4.0",
"smallvec",
"sp-api",
"sp-application-crypto",
@@ -6491,7 +6533,7 @@ dependencies = [
"frame-support",
"frame-system",
"log",
- "manta-primitives",
+ "manta-primitives 4.4.0",
"orml-traits",
"pallet-assets",
"pallet-balances",
@@ -6853,7 +6895,7 @@ dependencies = [
"frame-system",
"hex-literal",
"log",
- "manta-primitives",
+ "manta-primitives 4.4.0",
"orml-traits",
"pallet-asset-manager",
"pallet-assets",
@@ -6873,7 +6915,7 @@ name = "pallet-farming-rpc-api"
version = "4.4.0"
dependencies = [
"jsonrpsee",
- "manta-primitives",
+ "manta-primitives 4.4.0",
"pallet-farming-rpc-runtime-api",
"parity-scale-codec",
"serde",
@@ -6888,7 +6930,7 @@ dependencies = [
name = "pallet-farming-rpc-runtime-api"
version = "4.4.0"
dependencies = [
- "manta-primitives",
+ "manta-primitives 4.4.0",
"parity-scale-codec",
"sp-api",
"sp-std",
@@ -6990,37 +7032,28 @@ dependencies = [
[[package]]
name = "pallet-lottery"
-version = "4.4.0"
+version = "1.0.0"
+source = "git+https://github.com/jumboshrimpslab/lottery-polkadot.git#903c0c30044dc78917c80cca8e1971965a390a75"
dependencies = [
- "calamari-runtime",
"frame-benchmarking",
"frame-support",
"frame-system",
"function_name",
"jsonrpsee",
- "lazy_static",
"log",
- "manta-collator-selection",
- "manta-primitives",
- "pallet-balances",
- "pallet-parachain-staking",
- "pallet-preimage",
- "pallet-randomness",
- "pallet-scheduler",
- "pallet-transaction-payment",
+ "manta-primitives 4.4.0 (git+https://github.com/Manta-Network/Manta.git)",
+ "pallet-parachain-staking 4.4.0 (git+https://github.com/Manta-Network/Manta.git)",
"parity-scale-codec",
"rand 0.8.5",
- "runtime-common",
+ "runtime-common 4.4.0 (git+https://github.com/Manta-Network/Manta.git)",
"scale-info",
- "session-key-primitives",
- "similar-asserts",
+ "session-key-primitives 4.4.0 (git+https://github.com/Manta-Network/Manta.git)",
"sp-api",
"sp-arithmetic",
"sp-blockchain",
"sp-core",
"sp-io",
"sp-runtime",
- "sp-staking",
"sp-std",
]
@@ -7039,7 +7072,7 @@ dependencies = [
"manta-accounting",
"manta-crypto",
"manta-pay",
- "manta-primitives",
+ "manta-primitives 4.4.0",
"manta-util",
"pallet-asset-manager",
"pallet-assets",
@@ -7075,7 +7108,7 @@ dependencies = [
"manta-accounting",
"manta-crypto",
"manta-pay",
- "manta-primitives",
+ "manta-primitives 4.4.0",
"manta-util",
"num_cpus",
"pallet-asset-manager",
@@ -7112,7 +7145,7 @@ dependencies = [
"manta-accounting",
"manta-crypto",
"manta-pay",
- "manta-primitives",
+ "manta-primitives 4.4.0",
"manta-util",
"parity-scale-codec",
"rand_chacha 0.3.1",
@@ -7181,7 +7214,7 @@ dependencies = [
"frame-benchmarking",
"frame-support",
"frame-system",
- "manta-primitives",
+ "manta-primitives 4.4.0",
"pallet-balances",
"pallet-manta-support",
"parity-scale-codec",
@@ -7304,8 +7337,8 @@ dependencies = [
"frame-support",
"frame-system",
"log",
- "manta-collator-selection",
- "manta-primitives",
+ "manta-collator-selection 4.4.0",
+ "manta-primitives 4.4.0",
"pallet-balances",
"pallet-session",
"parity-scale-codec",
@@ -7321,6 +7354,28 @@ dependencies = [
"substrate-fixed 0.5.9 (git+https://github.com/Manta-Network/substrate-fixed.git?tag=v0.5.9)",
]
+[[package]]
+name = "pallet-parachain-staking"
+version = "4.4.0"
+source = "git+https://github.com/Manta-Network/Manta.git#0814f5d3c1490be7f6a04d0b41593c3da93e5800"
+dependencies = [
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "log",
+ "manta-collator-selection 4.4.0 (git+https://github.com/Manta-Network/Manta.git)",
+ "manta-primitives 4.4.0 (git+https://github.com/Manta-Network/Manta.git)",
+ "pallet-session",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-arithmetic",
+ "sp-runtime",
+ "sp-staking",
+ "sp-std",
+ "substrate-fixed 0.5.9 (git+https://github.com/Manta-Network/substrate-fixed.git?tag=v0.5.9)",
+]
+
[[package]]
name = "pallet-preimage"
version = "4.0.0-dev"
@@ -7356,20 +7411,19 @@ dependencies = [
[[package]]
name = "pallet-randomness"
version = "4.4.0"
+source = "git+https://github.com/jumboshrimpslab/lottery-polkadot.git#903c0c30044dc78917c80cca8e1971965a390a75"
dependencies = [
- "derive_more",
"frame-benchmarking",
"frame-support",
"frame-system",
"hex",
"log",
- "manta-primitives",
+ "manta-primitives 4.4.0 (git+https://github.com/Manta-Network/Manta.git)",
"nimbus-primitives",
- "pallet-balances",
"parity-scale-codec",
"scale-info",
"serde",
- "session-key-primitives",
+ "session-key-primitives 4.4.0 (git+https://github.com/Manta-Network/Manta.git)",
"sp-core",
"sp-io",
"sp-runtime",
@@ -7675,7 +7729,7 @@ dependencies = [
"frame-benchmarking",
"frame-support",
"frame-system",
- "manta-primitives",
+ "manta-primitives 4.4.0",
"pallet-balances",
"parity-scale-codec",
"scale-info",
@@ -10053,7 +10107,7 @@ dependencies = [
"frame-support",
"frame-system",
"lazy_static",
- "manta-primitives",
+ "manta-primitives 4.4.0",
"orml-traits",
"orml-xtokens",
"pallet-asset-manager",
@@ -10080,6 +10134,18 @@ dependencies = [
"xcm-simulator",
]
+[[package]]
+name = "runtime-common"
+version = "4.4.0"
+source = "git+https://github.com/Manta-Network/Manta.git#0814f5d3c1490be7f6a04d0b41593c3da93e5800"
+dependencies = [
+ "frame-support",
+ "manta-primitives 4.4.0 (git+https://github.com/Manta-Network/Manta.git)",
+ "pallet-transaction-payment",
+ "sp-runtime",
+ "sp-std",
+]
+
[[package]]
name = "rustc-demangle"
version = "0.1.23"
@@ -11651,7 +11717,25 @@ name = "session-key-primitives"
version = "4.4.0"
dependencies = [
"async-trait",
- "manta-primitives",
+ "manta-primitives 4.4.0",
+ "nimbus-primitives",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-application-crypto",
+ "sp-consensus-aura",
+ "sp-core",
+ "sp-inherents",
+ "sp-runtime",
+]
+
+[[package]]
+name = "session-key-primitives"
+version = "4.4.0"
+source = "git+https://github.com/Manta-Network/Manta.git#0814f5d3c1490be7f6a04d0b41593c3da93e5800"
+dependencies = [
+ "async-trait",
+ "manta-primitives 4.4.0 (git+https://github.com/Manta-Network/Manta.git)",
"nimbus-primitives",
"parity-scale-codec",
"scale-info",
diff --git a/node/Cargo.toml b/node/Cargo.toml
index 52ee47a1c..d293340a0 100644
--- a/node/Cargo.toml
+++ b/node/Cargo.toml
@@ -112,12 +112,13 @@ xcm = { git = "https://github.com/paritytech/polkadot.git", branch = "release-v0
calamari-runtime = { path = '../runtime/calamari' }
manta-primitives = { path = '../primitives/manta' }
manta-runtime = { path = '../runtime/manta' }
-pallet-lottery = { path = '../pallets/pallet-lottery', features = ["rpc"] }
pallet-manta-pay = { path = '../pallets/manta-pay', features = ["rpc", "runtime"] }
pallet-manta-sbt = { path = '../pallets/manta-sbt', features = ["rpc", "runtime"] }
pallet-parachain-staking = { path = '../pallets/parachain-staking' }
session-key-primitives = { path = '../primitives/session-keys' }
+pallet-lottery = { git = 'https://github.com/jumboshrimpslab/lottery-polkadot.git', features = ["rpc"] }
+
[build-dependencies]
substrate-build-script-utils = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37" }
diff --git a/pallets/pallet-lottery/Cargo.toml b/pallets/pallet-lottery/Cargo.toml
deleted file mode 100644
index 3e04c15e2..000000000
--- a/pallets/pallet-lottery/Cargo.toml
+++ /dev/null
@@ -1,82 +0,0 @@
-[package]
-authors = ["Manta Network"]
-description = 'Pallet implementing an APY-maximizing no-loss lottery'
-edition = "2021"
-homepage = 'https://manta.network'
-license = 'GPL-3.0'
-name = 'pallet-lottery'
-repository = 'https://github.com/Manta-Network/Manta/'
-version = '4.4.0'
-
-[dependencies]
-codec = { version = '3.4.0', default-features = false, features = ['derive'], package = 'parity-scale-codec' }
-function_name = "0.3"
-jsonrpsee = { version = "0.16.2", features = ["server", "macros"], optional = true }
-log = { version = "0.4.0", default-features = false }
-scale-info = { version = "2.1.2", default-features = false, features = ["derive"] }
-
-# Substrate dependencies
-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" }
-sp-api = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" }
-sp-arithmetic = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" }
-sp-blockchain = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37", optional = true }
-sp-core = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" }
-sp-io = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" }
-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" }
-
-# Self dependencies
-manta-primitives = { path = "../../primitives/manta", default-features = false }
-pallet-parachain-staking = { path = '../parachain-staking', default-features = false }
-runtime-common = { path = "../../runtime/common", default-features = false }
-session-key-primitives = { path = '../../primitives/session-keys', default-features = false }
-
-# Benchmarking dependencies
-frame-benchmarking = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37", default-features = false, optional = true }
-rand = { version = "0.8.5", default-features = false, optional = true }
-
-[dev-dependencies]
-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" }
-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" }
-pallet-transaction-payment = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.37" }
-rand = "0.8"
-similar-asserts = "1.1.0"
-sp-staking = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.37" }
-
-[features]
-default = ["std"]
-# RPC Interface
-rpc = [
- "jsonrpsee",
- "sp-blockchain",
-]
-runtime-benchmarks = [
- 'frame-benchmarking/runtime-benchmarks',
- 'frame-support/runtime-benchmarks',
- 'pallet-parachain-staking/runtime-benchmarks',
- 'rand/std_rng',
-]
-std = [
- "manta-primitives/std",
- "pallet-parachain-staking/std",
- "pallet-randomness/std",
- "calamari-runtime/std",
- "session-key-primitives/std",
- "sp-core/std",
- "sp-std/std",
- "sp-io/std",
- "sp-runtime/std",
- 'frame-benchmarking/std',
- "frame-support/std",
- "frame-system/std",
-]
-try-runtime = [
- "frame-support/try-runtime",
- "frame-system/try-runtime",
-]
diff --git a/pallets/pallet-lottery/src/benchmarks.rs b/pallets/pallet-lottery/src/benchmarks.rs
deleted file mode 100644
index 577e17cbd..000000000
--- a/pallets/pallet-lottery/src/benchmarks.rs
+++ /dev/null
@@ -1,345 +0,0 @@
-// 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 .
-
-#![cfg(feature = "runtime-benchmarks")]
-
-//! Benchmarking
-use crate::{Call, Config, Pallet, Request};
-use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, Zero};
-use frame_support::{
- assert_ok,
- traits::{Currency, EstimateCallFee, Get, OnFinalize, OnInitialize},
-};
-use frame_system::RawOrigin;
-use pallet_parachain_staking::{
- benchmarks::{create_funded_collator, create_funded_user, parachain_staking_on_finalize},
- BalanceOf, Pallet as Staking,
-};
-use sp_runtime::Saturating;
-
-const MAX_COLLATOR_COUNT: u32 = 63;
-const USER_SEED: u32 = 696969;
-
-/// Run to end block and author
-fn roll_rounds_and_author(rounds: u32) {
- let total_rounds = rounds + 1u32;
- let round_length: T::BlockNumber = Staking::::round().length.into();
- let mut now = >::block_number() + 1u32.into();
- let end = Staking::::round().first + (round_length * total_rounds.into());
- while now < end {
- let use_first_collator_to_author =
- Staking::::selected_candidates().first().unwrap().clone();
- parachain_staking_on_finalize::(use_first_collator_to_author.clone());
- >::on_finalize(>::block_number());
- >::set_block_number(
- >::block_number() + 1u32.into(),
- );
- >::on_initialize(>::block_number());
- Staking::::on_initialize(>::block_number());
- now += 1u32.into();
- }
-}
-
-fn fund_lottery_account(bal: BalanceOf) {
- ::Currency::deposit_creating(
- &Pallet::::account_id(),
- bal,
- );
-}
-
-fn register_collators(number: u32) {
- let original_collator_count = Staking::::candidate_pool().len() as u32;
- let mut collator_seed: u32 = 444;
- for _ in 0..number {
- assert_ok!(create_funded_collator::(
- "collator",
- collator_seed,
- Zero::zero(),
- true,
- original_collator_count + number
- ));
- collator_seed += 1;
- }
-}
-
-fn deposit_prior_users(number: u32, amount: BalanceOf) {
- for user in 0..number {
- >::set_block_number(user.into());
- let (depositor, _) = create_funded_user::("depositor", USER_SEED - 1 - user, amount);
- assert_ok!(Pallet::::deposit(
- RawOrigin::Signed(depositor).into(),
- amount
- ));
- }
-}
-
-benchmarks! {
- // USER DISPATCHABLES
-
- deposit {
- let x in 0u32..1_000u32; // other users that have already deposited to the lottery previously
- let y in 0..MAX_COLLATOR_COUNT; // registered collators
-
- fund_lottery_account::(Pallet::::gas_reserve());
- let min_delegator_bond = <::MinDelegatorStk as Get>>::get();
- let original_collator_count = Staking::::candidate_pool().len() as u32;
- let deposit_amount: BalanceOf = min_delegator_bond * 10_000u32.into();
- // fill collators
- register_collators::(y);
- assert_eq!(Staking::::candidate_pool().len() as u32, original_collator_count + y);
- assert_eq!(Pallet::::total_pot(), Zero::zero());
-
- let original_staked_amount = Staking::::total();
- deposit_prior_users::(x,deposit_amount);
-
- let (caller, _) = create_funded_user::("caller", USER_SEED, deposit_amount);
- }: _(RawOrigin::Signed(caller.clone()), deposit_amount)
- verify {
- assert_eq!(Pallet::::active_balance_per_user(caller), deposit_amount);
- assert_eq!(Pallet::::total_pot(), deposit_amount.saturating_mul((x+1).into()));
- assert_eq!(Staking::::total(), original_staked_amount + deposit_amount.saturating_mul((x+1).into()));
- }
-
- request_withdraw{
- let x in 0..1_000; // other users that have already deposited to the lottery previously
- let y in 0..MAX_COLLATOR_COUNT; // registered collators
-
- fund_lottery_account::(Pallet::::gas_reserve());
- let min_delegator_bond = <::MinDelegatorStk as Get>>::get();
- let original_collator_count = Staking::::candidate_pool().len() as u32;
- let deposit_amount: BalanceOf = min_delegator_bond * 10_000u32.into();
- // fill collators
- register_collators::(y);
- assert_eq!(Staking::::candidate_pool().len() as u32, original_collator_count + y);
-
- let original_staked_amount = Staking::::total();
- deposit_prior_users::(x,deposit_amount);
-
- let (caller, _) = create_funded_user::("caller", USER_SEED, deposit_amount);
- assert_ok!(Pallet::::deposit(RawOrigin::Signed(caller.clone()).into(), deposit_amount));
- assert_eq!(Pallet::::active_balance_per_user(caller.clone()), deposit_amount);
- }: _(RawOrigin::Signed(caller.clone()), deposit_amount)
- verify {
- assert!(Pallet::::active_balance_per_user(caller.clone()).is_zero());
- let now = >::block_number();
- let should_be_request = Request {
- user: caller.clone(),
- block: now,
- balance: deposit_amount,
- };
- let mut request_queue = Pallet::::withdrawal_request_queue();
- assert_eq!(request_queue.len(),1usize);
- assert_eq!(request_queue.pop().unwrap(), should_be_request);
- }
-
- claim_my_winnings {
- let y in 0..MAX_COLLATOR_COUNT; // registered collators
-
- // NOTE: We fund 2x gas reserve to have 1x gas reserve to pay out as winnings
- fund_lottery_account::(Pallet::::gas_reserve().saturating_add(Pallet::::gas_reserve()));
-
- let min_delegator_bond = <::MinDelegatorStk as Get>>::get();
- let original_collator_count = Staking::::candidate_pool().len() as u32;
- let deposit_amount: BalanceOf = min_delegator_bond * 10_000u32.into();
- // fill collators
- register_collators::(y);
- assert_eq!(Staking::::candidate_pool().len() as u32, original_collator_count + y);
-
- let (caller, _) = create_funded_user::("caller", USER_SEED, deposit_amount);
- assert_ok!(Pallet::::deposit(RawOrigin::Signed(caller.clone()).into(), deposit_amount));
- assert_eq!(Pallet::::active_balance_per_user(caller.clone()), deposit_amount);
- roll_rounds_and_author::(2);
- assert_ok!(Pallet::::draw_lottery(RawOrigin::Root.into()));
- // should have won now
- let unclaimed_winnings = Pallet::::total_unclaimed_winnings();
- let account_balance_before = ::Currency::free_balance(&caller);
- let fee_estimate = T::EstimateCallFee::estimate_call_fee(&Call::::claim_my_winnings { }, None::.into());
- assert!(!unclaimed_winnings.is_zero());
- assert_eq!(unclaimed_winnings,Pallet::::unclaimed_winnings_by_account(caller.clone()).unwrap());
- }: _(RawOrigin::Signed(caller.clone()))
- verify {
- assert!(Pallet::::total_unclaimed_winnings().is_zero());
- let account_balance_after = ::Currency::free_balance(&caller);
- assert!(Pallet::::unclaimed_winnings_by_account(caller.clone()).is_none());
- assert!(account_balance_after >= account_balance_before + unclaimed_winnings - fee_estimate);
- assert!(account_balance_after <= account_balance_before + unclaimed_winnings);
- }
-
- // ROOT DISPATCHABLES
- start_lottery {
- fund_lottery_account::(Pallet::::gas_reserve());
- }: _(RawOrigin::Root)
- verify {
- assert!(Pallet::::next_drawing_at().is_some());
- }
-
- stop_lottery {
- fund_lottery_account::(Pallet::::gas_reserve());
- assert_ok!(Pallet::::start_lottery(RawOrigin::Root.into()));
- }: _(RawOrigin::Root)
- verify {
- assert!(Pallet::::next_drawing_at().is_none());
- }
-
- draw_lottery {
- let x in 0..1_000; // other users that have already deposited to the lottery previously
- let y in 0..MAX_COLLATOR_COUNT; // registered collators
-
- // NOTE: We fund 2x gas reserve to have 1x gas reserve to pay out as winnings
- fund_lottery_account::(Pallet::::gas_reserve().saturating_add(Pallet::::gas_reserve()));
-
- let min_delegator_bond = <::MinDelegatorStk as Get>>::get();
- let original_collator_count = Staking::::candidate_pool().len() as u32;
- let deposit_amount: BalanceOf = min_delegator_bond * 10_000u32.into();
- // fill collators
- register_collators::(y);
- assert_eq!(Staking::::candidate_pool().len() as u32, original_collator_count + y);
-
- deposit_prior_users::(x,deposit_amount);
-
- let (caller, _) = create_funded_user::("caller", USER_SEED, deposit_amount);
- assert_ok!(Pallet::::deposit(RawOrigin::Signed(caller.clone()).into(), deposit_amount));
- assert_eq!(Pallet::::active_balance_per_user(&caller), deposit_amount);
- // roll_rounds_and_author::(2);
- }: _(RawOrigin::Root)
- verify {
- // someone should have won now
- let unclaimed_winnings = Pallet::::total_unclaimed_winnings();
- assert!(!unclaimed_winnings.is_zero());
- }
-
- process_matured_withdrawals {
- }: _(RawOrigin::Root)
- verify {
- }
- set_min_deposit {
- assert_ok!(Pallet::::set_min_withdraw(RawOrigin::Root.into(),u32::MAX.into()));
- }: _(RawOrigin::Root,u32::MAX.into())
- verify {
- }
- set_min_withdraw {
- }: _(RawOrigin::Root,u32::MAX.into())
- verify {
- }
- set_gas_reserve {
- }: _(RawOrigin::Root,u32::MAX.into())
- verify {
- }
- // rebalance_stake {
- // }: _()
- // verify {
- // }
-
- // liquidate_lottery {
- // }: _(RawOrigin::Root)
- // verify {
- // }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::{benchmarks::*, mock::Test};
- use frame_support::assert_ok;
- use sp_io::TestExternalities;
-
- pub fn new_test_ext() -> TestExternalities {
- crate::mock::ExtBuilder::default()
- .with_balances(vec![
- (1, 10_000_000_000_000_000_000u128),
- (2, 10_000_000_000_000_000_000u128),
- (3, 10_000_000_000_000_000_000u128),
- (4, 10_000_000_000_000_000_000u128),
- (5, 10_000_000_000_000_000_000u128),
- ])
- .with_candidates(vec![
- (1, 5_000_000_000_000_000_000u128),
- (2, 5_000_000_000_000_000_000u128),
- (3, 5_000_000_000_000_000_000u128),
- (4, 5_000_000_000_000_000_000u128),
- (5, 5_000_000_000_000_000_000u128),
- ])
- .with_funded_lottery_account(10_000_000_000_000_000u128)
- .with_inflation(Default::default())
- // NOTE: using default (=0) inflation means the lottery will not generate income on round change
- .build()
- }
- #[test]
- fn parachain_staking_is_set_up_correctly() {
- new_test_ext().execute_with(|| {
- assert_eq!(Staking::::selected_candidates().len(), 5);
- });
- }
- #[test]
- fn bench_deposit() {
- new_test_ext().execute_with(|| {
- assert_ok!(Pallet::::test_benchmark_deposit());
- });
- }
- #[test]
- fn bench_request_withdraw() {
- new_test_ext().execute_with(|| {
- assert_ok!(Pallet::::test_benchmark_request_withdraw());
- });
- }
- #[test]
- fn bench_claim_my_winnings() {
- new_test_ext().execute_with(|| {
- assert_ok!(Pallet::::test_benchmark_claim_my_winnings());
- });
- }
- // #[test]
- // fn bench_rebalance_stake() {
- // new_test_ext().execute_with(|| {
- // assert_ok!(Pallet::::test_benchmark_rebalance_stake());
- // });
- // }
- #[test]
- fn bench_start_lottery() {
- new_test_ext().execute_with(|| {
- assert_ok!(Pallet::::test_benchmark_start_lottery());
- });
- }
- #[test]
- fn bench_stop_lottery() {
- new_test_ext().execute_with(|| {
- assert_ok!(Pallet::::test_benchmark_stop_lottery());
- });
- }
- #[test]
- fn bench_draw_lottery() {
- new_test_ext().execute_with(|| {
- assert_ok!(Pallet::::test_benchmark_draw_lottery());
- });
- }
- // #[test]
- // fn bench_process_matured_withdrawals() {
- // new_test_ext().execute_with(|| {
- // assert_ok!(Pallet::::test_benchmark_process_matured_withdrawals());
- // });
- // }
- // #[test]
- // fn bench_liquidate_lottery() {
- // new_test_ext().execute_with(|| {
- // assert_ok!(Pallet::::test_benchmark_liquidate_lottery());
- // });
- // }
-}
-
-impl_benchmark_test_suite!(
- Pallet,
- crate::benchmarks::tests::new_test_ext(),
- crate::mock::Test
-);
diff --git a/pallets/pallet-lottery/src/lib.rs b/pallets/pallet-lottery/src/lib.rs
deleted file mode 100644
index cd4cec5d1..000000000
--- a/pallets/pallet-lottery/src/lib.rs
+++ /dev/null
@@ -1,1243 +0,0 @@
-// 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 .
-
-//! # No-Loss-Lottery Module
-//!
-//! ## Overview
-//!
-//! This pallet implements a no-loss-lottery by taking user deposits, generating excess funds by staking funds with [`pallet_parachain_staking`]
-//! and periodically selects a winner from participating users weighted by their deposit amount to receive a claim to the
-//! accrued excess funds.
-//! Funds withdrawn from the the lottery are subject to a timelock determined by parachain-staking before they can be claimed.
-//!
-//! ### Lottery Rules
-//! 1. A drawing is scheduled to happen every [`Config::DrawingInterval`] blocks.
-//! 2. A designated manager can start & stop the drawings as well as rebalance the stake to improve the yield generated through staking
-//! 3. In order to prevent gaming of the lottery drawing mechanism, no modifications to this pallet are allowed [`Config::DrawingFreezeout`] blocks before a drawing
-//! This is needed e.g. using BABE Randomness, where the randomness will be known a day before the scheduled drawing
-//! 4. Winnings must be claimed manually by the winner but there is no time limit for claiming winnings
-//! 5. Deposits are instantly staked by the pallet
-//! 6. Withdrawals must wait for a timelock imposed by [`pallet_parachain_staking`] and are paid out automatically (via scheduler) in the first lottery drawing after it expires
-//! 7. The [`Config::ManageOrigin`] must at the same time be allowed to use [`frame_support::traits::schedule::Named`] e.g. `ScheduleOrigin` in `pallet_scheduler`
-//!
-//! ## Dependencies
-//! 1. To enable fair winner selection, a fair and low-influience randomness provider implementing [`frame_support::traits::Randomness`], e.g. pallet_randomness
-//! 2. To schedule automatic drawings, a scheduling pallet implementing [`frame_support::traits::schedule::Named`], e.g. pallet_scheduler
-//! 3. To generate lottery revenue, [`pallet_parachain_staking`]
-//!
-//! ## Interface
-//!
-//! This pallet contains extrinsics callable by any user and a second set of extrinsic callable only by a *Lottery Manager* Origin
-//! configurable as [`Config::ManageOrigin`] in the [`Config`] struct of this pallet.
-//!
-//! ### User Dispatchable Functions
-//! * [`Call::deposit`]: Allows any user to deposit tokens into the lottery
-//! * [`Call::request_withdraw`]: Allows any user to request return of their deposited tokens to own wallet
-//! * [`Call::claim_my_winnings`]: Allows any user to transfer any accrued winnings into their wallet
-//!
-//! ### Manager Dispatchable Functions
-//! * [`Call::start_lottery`]: Schedules periodic lottery drawings to occur each [`Config::DrawingInterval`]
-//! * [`Call::stop_lottery`]: Cancels the current drawing and stops scheduling new drawings
-//! * [`Call::draw_lottery`]: Immediately executes a lottery drawing ( can be called manually even if lottery is stopped )
-//! * [`Call::process_matured_withdrawals`]: Immediately transfer funds of all matured withdrawals to their respective owner's wallets
-//! * [`Call::liquidate_lottery`]: Unstakes all lottery funds and schedules [`Call::process_matured_withdrawals`] after the timelock period
-//! * [`Call::rebalance_stake`]: Immediately unstakes overweight collators (with low APY) for later restaking into underweight collators (with high APY)
-//!
-//! ### Important state queries callable via RPC
-//! * [`Pallet::next_drawing_at`]: Block number where the next drawing will happen
-//! * [`Pallet::not_in_drawing_freezeout`]: False if deposits/withdrawals are currently frozen
-//! * [`Pallet::current_prize_pool`]: Token amount currently in the pallet the winner would get if the drawing was now
-//! Call these from a frontend as e.g.
-//! ```bash
-//! curl http://localhost:9933 -H "Content-Type:application/json;charset=utf-8" -d '{"jsonrpc":"2.0","id":1,"method":"lottery_next_drawing_at","params": []}'
-//! curl http://localhost:9933 -H "Content-Type:application/json;charset=utf-8" -d '{"jsonrpc":"2.0","id":1,"method":"lottery_current_prize_pool","params": []}'
-//! curl http://localhost:9933 -H "Content-Type:application/json;charset=utf-8" -d '{"jsonrpc":"2.0","id":1,"method":"lottery_not_in_drawing_freezeout","params": []}'
-//! ```
-//!
-//! Please refer to [`Pallet`] for more documentation on each function.
-//! Furthermore, the storage items containing all relevant information about lottery state can be queried via e.g. the [polkadot.js API](https://polkadot.js.org/docs/api)
-
-#![cfg_attr(not(feature = "std"), no_std)]
-
-mod staking;
-
-#[cfg(feature = "rpc")]
-pub mod rpc;
-pub mod runtime;
-
-#[cfg(any(test, feature = "runtime-benchmarks"))]
-mod benchmarks;
-#[cfg(test)]
-mod mock;
-#[cfg(test)]
-mod tests;
-
-pub mod weights;
-pub use weights::WeightInfo;
-
-pub use pallet::*;
-#[frame_support::pallet]
-pub mod pallet {
- use super::*;
- pub use ::function_name::named;
- use frame_support::{
- ensure, log,
- pallet_prelude::*,
- traits::{
- schedule::{v2::Named as ScheduleNamed, DispatchTime, MaybeHashed, LOWEST_PRIORITY},
- ExistenceRequirement::KeepAlive,
- *,
- },
- PalletId,
- };
- use frame_system::{pallet_prelude::*, RawOrigin};
- use pallet_parachain_staking::BalanceOf;
- use sp_arithmetic::traits::SaturatedConversion;
- use sp_core::U256;
- use sp_runtime::{
- traits::{AccountIdConversion, CheckedAdd, CheckedSub, Dispatchable, Saturating, Zero},
- ArithmeticError, DispatchResult,
- };
- use sp_std::prelude::*;
-
- const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
-
- pub type CallOf = ::RuntimeCall;
-
- #[pallet::config]
- pub trait Config: frame_system::Config + pallet_parachain_staking::Config {
- /// The aggregated `RuntimeCall` type.
- type RuntimeCall: Parameter
- + Dispatchable
- + From>;
- type RuntimeEvent: From> + IsType<::RuntimeEvent>;
- /// The Scheduler.
- type Scheduler: ScheduleNamed<
- Self::BlockNumber,
- CallOf,
- Self::PalletsOrigin,
- Hash = Self::Hash,
- >;
- // Randomness source to use for determining lottery winner
- type RandomnessSource: Randomness;
- /// Something that can estimate the cost of sending an extrinsic
- type EstimateCallFee: frame_support::traits::EstimateCallFee<
- pallet_parachain_staking::Call,
- BalanceOf,
- > + frame_support::traits::EstimateCallFee, BalanceOf>;
- /// Origin that can manage lottery parameters and start/stop drawings
- type ManageOrigin: EnsureOrigin;
- /// Overarching type of all pallets origins.
- type PalletsOrigin: From>;
- /// Account Identifier from which the internal Pot is generated.
- #[pallet::constant]
- type LotteryPot: Get;
- /// Time in blocks between lottery drawings
- #[pallet::constant]
- type DrawingInterval: Get;
- /// Time in blocks *before* a drawing in
- /// Depending on the randomness source, the winner might be established before the drawing, this prevents modification of the eligible winning set after the winner
- /// has been established but before it is selected by [`Call::draw_lottery`] which modifications of the win-eligble pool are prevented
- #[pallet::constant]
- type DrawingFreezeout: Get;
- /// Time in blocks until a collator is done unstaking
- #[pallet::constant]
- type UnstakeLockTime: Get; // XXX: could maybe alculate this from staking LeaveDelayRounds * DefaultBlocksPerRound
- /// Weight information for extrinsics in this pallet.
- type WeightInfo: WeightInfo;
- }
-
- #[pallet::pallet]
- #[pallet::storage_version(STORAGE_VERSION)]
- #[pallet::without_storage_info]
- pub struct Pallet(PhantomData);
-
- // Configurable (constant) storage items
-
- /// NOTE: how much KMA to keep in the pallet for gas
- /// This must be initialized at genesis, otherwise the pallet will run out of gas at the first drawing
- #[pallet::storage]
- #[pallet::getter(fn gas_reserve)]
- pub(super) type GasReserve = StorageValue<_, BalanceOf, ValueQuery>;
-
- #[pallet::storage]
- #[pallet::getter(fn min_deposit)]
- pub(super) type MinDeposit = StorageValue<_, BalanceOf, ValueQuery>;
-
- #[pallet::storage]
- #[pallet::getter(fn min_withdraw)]
- pub(super) type MinWithdraw = StorageValue<_, BalanceOf, ValueQuery>;
-
- // Dynamic Storage Items
-
- /// sum of all user's deposits, to ensure balance never drops below
- /// Incremented on [`Call::deposit`]
- /// Decremented on withdrawal to user wallet in [`Call::process_matured_withdrawals`]
- #[pallet::storage]
- #[pallet::getter(fn sum_of_deposits)]
- pub(super) type SumOfDeposits = StorageValue<_, BalanceOf, ValueQuery>;
-
- /// Total number of token eligible to win in the current drawing cycle
- /// Incremented on [`Call::deposit`]
- /// Decremented on [`Call::request_withdraw`]
- #[pallet::storage]
- #[pallet::getter(fn total_pot)]
- pub(super) type TotalPot = StorageValue<_, BalanceOf, ValueQuery>;
-
- #[pallet::storage]
- #[pallet::getter(fn total_users)]
- pub(super) type TotalUsers = StorageValue<_, u32, ValueQuery>;
-
- #[pallet::storage]
- #[pallet::getter(fn is_rebalancing)]
- pub(super) type RebalanceInProgress = StorageValue<_, bool, ValueQuery>;
-
- #[pallet::storage]
- #[pallet::getter(fn active_balance_per_user)]
- pub(super) type ActiveBalancePerUser =
- StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf, ValueQuery>;
-
- #[pallet::storage]
- #[pallet::getter(fn unclaimed_winnings_by_account)]
- pub(super) type UnclaimedWinningsByAccount =
- StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf, OptionQuery>;
-
- /// Free balance in the pallet that belongs to a previous lottery winner
- /// Incremented on winner election in the course of a drawing
- /// Decremented on transfer of winnings to ower wallet in [`Call::claim_my_winnings`]
- #[pallet::storage]
- #[pallet::getter(fn total_unclaimed_winnings)]
- pub(super) type TotalUnclaimedWinnings = StorageValue<_, BalanceOf, ValueQuery>;
-
- /// Free balance in the pallet that was unstaked from a collator and is needed for future withdrawal requests
- /// Incremented on successful unstaking of a collator
- /// Decremented on transfer of funds to withdrawer and on restaking of funds a collator
- #[pallet::storage]
- #[pallet::getter(fn unlocked_unstaking_funds)]
- pub(super) type UnlockedUnstakingFunds = StorageValue<_, BalanceOf, ValueQuery>;
-
- #[derive(Clone, Encode, Decode, TypeInfo)]
- pub(super) struct UnstakingCollator {
- pub account: AccountId,
- pub since: BlockNumber,
- }
-
- #[pallet::storage]
- pub(super) type UnstakingCollators =
- StorageValue<_, Vec>, ValueQuery>;
-
- /// This is balance unstaked from a collator that is not needed to service user's withdrawal requests
- /// Incremented on initiation of a collator unstake in [`Call::request_withdraw`]
- /// Decremented on [`Call::request_withdraw`] (no collator unstake) and [`Call::rebalance_stake`] (restaking of surplus funds)
- #[pallet::storage]
- #[pallet::getter(fn surplus_unstaking_balance)]
- pub(super) type SurplusUnstakingBalance = StorageValue<_, BalanceOf, ValueQuery>;
-
- #[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, TypeInfo)]
- pub struct Request {
- pub user: AccountId,
- pub block: BlockNumber,
- pub balance: Balance,
- }
-
- #[pallet::storage]
- #[pallet::getter(fn withdrawal_request_queue)]
- pub(super) type WithdrawalRequestQueue =
- StorageValue<_, Vec>>, ValueQuery>;
-
- /// Incremented whenever delegating tokens to a collator
- /// Collators are removed from here when their funds are unlocked in [`Call::finish_unstaking_collators`]
- #[pallet::storage]
- #[pallet::getter(fn staked_collators)]
- pub(super) type StakedCollators =
- StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf, ValueQuery>;
-
- #[pallet::genesis_config]
- pub struct GenesisConfig {
- /// amount of token to keep in the pot for paying gas fees
- pub gas_reserve: BalanceOf,
- pub min_deposit: BalanceOf,
- pub min_withdraw: BalanceOf,
- }
-
- #[cfg(feature = "std")]
- impl Default for GenesisConfig {
- fn default() -> Self {
- Self {
- min_deposit: 1u32.into(),
- min_withdraw: 1u32.into(),
- gas_reserve: 10_000u32.into(),
- }
- }
- }
-
- #[pallet::genesis_build]
- impl GenesisBuild for GenesisConfig {
- #[inline]
- fn build(&self) {
- GasReserve::::set(self.gas_reserve);
- MinDeposit::::set(self.min_deposit);
- MinWithdraw::::set(self.min_withdraw);
- }
- }
-
- #[pallet::event]
- #[pallet::generate_deposit(fn deposit_event)]
- pub enum Event {
- LotteryStarted,
- LotteryStopped,
- LotteryWinner {
- account: T::AccountId,
- amount: BalanceOf,
- },
- Deposited {
- account: T::AccountId,
- amount: BalanceOf,
- },
- ScheduledWithdraw {
- account: T::AccountId,
- amount: BalanceOf,
- },
- Withdrawn {
- account: T::AccountId,
- amount: BalanceOf,
- },
- Claimed {
- account: T::AccountId,
- amount: BalanceOf,
- },
- }
-
- #[pallet::error]
- pub enum Error {
- /// Lottery has not been started
- LotteryNotStarted,
- /// Lottery has already been started
- LotteryIsRunning,
- /// Pre-drawing freeze in effect, can't modify balances
- TooCloseToDrawing,
- /// FATAL: Assigning/Transferring winning claims
- /// would **remove** user deposited funds from pallet
- PotBalanceTooLow,
- /// FATAL: Can't stake the requested amount with available funds
- PotBalanceTooLowToStake,
- /// Pallet balance is lower than the needed gas fee buffer
- PotBalanceBelowGasReserve,
- /// Pallet balance is too low to submit a needed transaction
- PotBalanceTooLowToPayTxFee,
- /// No funds eligible to win
- NobodyPlaying,
- /// No funds to win in pool
- NothingToWin,
- /// Fatal: No winner could be selected
- NoWinnerFound,
- /// Deposit amount is below minimum amount
- DepositBelowMinAmount,
- /// Requested Withdrawal amount is below minimum
- WithdrawBelowMinAmount,
- /// Requested to withdraw more than you deposited
- WithdrawAboveDeposit,
- /// No deposits found for this account
- NoDepositForAccount,
- /// Fatal: No collators found to assign this deposit to
- NoCollatorForDeposit,
- /// Fatal: No collators found to assign this deposit to
- NoCollatorForStake,
- /// Fatal: No collators found to withdraw from
- NoCollatorForWithdrawal,
- /// Fatal: A calculation that must not be negative would underflow
- ArithmeticUnderflow,
- /// Fatal: A calculation that would overflow
- ArithmeticOverflow,
- /// Fatal: Pallet configuration violates sanity checks
- PalletMisconfigured,
- /// Fatal: Could not schedule lottery drawings
- CouldNotSchedule,
- /// Fatal: Functionality not yet supported
- NotImplemented,
- }
-
- #[pallet::call]
- impl Pallet {
- /// Allows any user to deposit tokens into the lottery
- ///
- /// # Arguments
- ///
- /// * `amount` - The amount of tokens to be deposited.
- #[pallet::call_index(0)]
- #[pallet::weight(::WeightInfo::deposit(Pallet::::total_users(), pallet_parachain_staking::Pallet::::selected_candidates().len() as u32))]
- pub fn deposit(origin: OriginFor, amount: BalanceOf) -> DispatchResult {
- let caller_account = ensure_signed(origin)?;
- ensure!(
- amount >= Self::min_deposit(),
- Error::::DepositBelowMinAmount
- );
- ensure!(
- Self::not_in_drawing_freezeout(),
- Error::::TooCloseToDrawing
- );
- ensure! { // Sanity check: make sure we dont accept deposits that will fail in staking
- Self::min_deposit() >= ::MinDelegation::get(),
- Error::::PalletMisconfigured
- };
-
- // Transfer funds to pot
- ::Currency::transfer(
- &caller_account,
- &Self::account_id(),
- amount,
- KeepAlive,
- )?;
-
- // Attempt to stake them
- let collator_balance_pairs = Self::calculate_deposit_distribution(amount);
- ensure!(
- !collator_balance_pairs.is_empty(),
- Error::::NoCollatorForDeposit
- );
- for (some_collator, balance) in collator_balance_pairs {
- // TODO: What if the `balance` is below `MinDelegation`a on a new collator? this will fail
- Self::do_stake_one_collator(some_collator, balance)?;
- }
-
- // Add to active funds
- ActiveBalancePerUser::::mutate(caller_account.clone(), |balance| *balance += amount);
- TotalPot::::mutate(|balance| *balance += amount);
- TotalUsers::::mutate(|users| *users += 1);
- SumOfDeposits::::mutate(|balance| *balance += amount);
- Self::deposit_event(Event::Deposited {
- account: caller_account,
- amount,
- });
- Ok(())
- }
-
- /// Requests a withdrawal of `amount` from the caller's active funds.
- ///
- /// Withdrawal is not immediate as funds are subject to a timelock imposed by [`pallet_parachain_staking`]
- /// It will be executed with the first [`Call::draw_lottery`] call after timelock expires
- ///
- /// Withdrawals can NOT be cancelled because they inflict ecomomic damage on the lottery through collator unstaking
- /// and the user causing this damage must be subjected to economic consequences for doing so
- ///
- /// The withdrawal is paid from [`SurplusUnstakingBalance`]
- /// If this balance is too low to handle the request, another collator is unstaked
- ///
- /// # Arguments
- ///
- /// * `amount` - the amount of funds to withdraw
- ///
- /// # Errors
- ///
- /// Returns an error if:
- /// * `amount` is below the minimum withdraw amount
- /// * `amount` is larger than the user's total deposit
- /// * It is too close to the drawing
- /// * The user has no or not enough active funds
- /// * There are any arithmetic underflows
- #[pallet::call_index(1)]
- #[pallet::weight(::WeightInfo::request_withdraw(Pallet::::total_users(), pallet_parachain_staking::Pallet::::selected_candidates().len() as u32))]
- pub fn request_withdraw(origin: OriginFor, amount: BalanceOf) -> DispatchResult {
- let caller = ensure_signed(origin)?;
-
- ensure!(
- amount >= Self::min_withdraw(),
- Error::::WithdrawBelowMinAmount
- );
- ensure!(
- Self::not_in_drawing_freezeout(),
- Error::::TooCloseToDrawing
- );
- let now = >::block_number();
- log::debug!("Requesting withdraw of {:?} tokens", amount);
- // Ensure user has enough funds active and mark them as offboarding (remove from `ActiveBalancePerUser`)
- ActiveBalancePerUser::::try_mutate_exists(caller.clone(), |maybe_balance| {
- match maybe_balance {
- None => Err(Error::::NoDepositForAccount),
- Some(balance) => {
- // Withdraw only what's active
- ensure!(*balance >= amount, Error::::WithdrawAboveDeposit);
- // Mark funds as offboarding
- WithdrawalRequestQueue::::mutate(|withdraw_vec| {
- withdraw_vec.push(Request {
- user: caller.clone(),
- block: now,
- balance: amount,
- })
- });
- // store reduced balance
- *maybe_balance = match balance
- .checked_sub(&amount)
- .ok_or(Error::::ArithmeticUnderflow)?
- {
- new_balance if new_balance.is_zero() => {
- // remove user if this was his last remaining funds
- TotalUsers::::try_mutate(|users| {
- *users = (*users)
- .checked_sub(1u32)
- .ok_or(Error::::ArithmeticUnderflow)?;
- Ok(())
- })?;
- None
- }
- new_balance => Some(new_balance),
- };
- TotalPot::::try_mutate(|pot| {
- *pot = (*pot)
- .checked_sub(&amount)
- .ok_or(Error::::ArithmeticUnderflow)?;
- Ok(())
- })?;
- Ok(())
- }
- }
- })?;
-
- // Unstaking workflow
- // 1. See if this withdrawal can be serviced with left-over balance from an already unstaking collator, if so deduct remaining balance and schedule the request
- // 2. If it can't, find the collator with the smallest delegation that is able to handle this withdrawal request and fully unstake it
- // 3. Add balance overshoot to "remaining balance" to handle further requests from
-
- // If the withdrawal fits in the currently unstaking funds, do nothing else
- SurplusUnstakingBalance::::try_mutate(|remaining_balance| {
- match (*remaining_balance).checked_sub(&amount){
- Some(subtracted) => {
- *remaining_balance = subtracted;
- Ok(())
- }
- _ => {
- Err("not enough left to handle this request from current unstaking funds")
- }
- }
- })
- .or_else(|_| {
- // Withdrawal needs extra collators to unstake to have enough funds to serve withdrawals, do it
- let reserve = SurplusUnstakingBalance::::get();
- let mut remaining_to_withdraw = amount - reserve;
-
- // unstake collators as necessary. This updates `SurplusUnstakingBalance`
- for collator_to_unstake in Self::calculate_withdrawal_distribution(remaining_to_withdraw){
- let our_stake = StakedCollators::::get(collator_to_unstake.clone());
- remaining_to_withdraw = remaining_to_withdraw.saturating_sub(our_stake);
- // The following call updates `SurplusUnstakingBalance` with newly unstaked funds
- Self::do_unstake_collator(now,collator_to_unstake)?;
- }
- if !remaining_to_withdraw.is_zero() {
- return Err("FATAL: Didn't unstake the full requested balance (or more)");
- }
- SurplusUnstakingBalance::::try_mutate(|remaining_balance| {
- match (*remaining_balance).checked_sub(&amount){
- Some(subtracted) => {
- *remaining_balance = subtracted;
- Ok(())
- }
- _ => {
- Err("not enough unstaking balance to handle request after unstaking additional collators")
- }
- }
- })
- })?;
- // END UNSTAKING SECTION
- Self::deposit_event(Event::ScheduledWithdraw {
- account: caller,
- amount,
- });
- Ok(())
- }
-
- /// Allows the caller to transfer any of the account's previously unclaimed winnings to his their wallet
- ///
- /// # Errors
- ///
- /// CannotLookup: The caller has no unclaimed winnings.
- #[pallet::call_index(2)]
- #[pallet::weight(::WeightInfo::claim_my_winnings(pallet_parachain_staking::Pallet::::selected_candidates().len() as u32))]
- pub fn claim_my_winnings(origin: OriginFor) -> DispatchResult {
- let caller = ensure_signed(origin)?;
- match UnclaimedWinningsByAccount::::take(caller.clone()) {
- Some(winnings) => {
- // Never pay out winnings if it would reduce pallet funds below total number of user's deposits
- let all_funds_in_pallet =
- ::Currency::total_balance(
- &Self::account_id(),
- );
- ensure!(
- // Sanity check: Never pay out funds that would draw on user deposits
- all_funds_in_pallet.saturating_sub(winnings) >= Self::sum_of_deposits(),
- Error::::PotBalanceTooLow
- );
-
- TotalUnclaimedWinnings::::try_mutate(|old| {
- *old = (*old)
- .checked_sub(&winnings)
- .ok_or(ArithmeticError::Underflow)?;
- Ok::<(), ArithmeticError>(())
- })?;
- ::Currency::transfer(
- &Self::account_id(),
- &caller,
- winnings,
- KeepAlive,
- )?; // NOTE: If the transfer fails, the TXN get rolled back and the winnings stay in the map for claiming later
- Self::deposit_event(Event::Claimed {
- account: caller,
- amount: winnings,
- });
- Ok(())
- }
- None => Err(DispatchError::CannotLookup),
- }
- }
-
- /// Maximizes staking APY and thus accrued winnings by removing staked tokens from overallocated/inactive
- /// collators and adding to underallocated ones.
- ///
- /// Can only be called by the account set as [`Config::ManageOrigin`]
- ///
- /// This function should be called when the pallet's staked tokens are staked with overweight collators
- /// or collators that became inactive or left the staking set.
- /// This will withdraw the tokens from overallocated and inactive collators and wait until the funds are unlocked,
- /// then re-allocate them to underallocated collators.
- ///
- /// Note that this operation can run in parallel with a drawing, but it will reduce the staking revenue
- /// generated in that drawing by the amount of funds being rebalanced.
- ///
- /// # Errors
- ///
- /// * BadOrigin: Caller is not ManageOrigin
- /// * TODO: Amount of tokens to be rebalanced would be too low.
- #[pallet::call_index(3)]
- #[pallet::weight(0)]
- pub fn rebalance_stake(origin: OriginFor) -> DispatchResult {
- T::ManageOrigin::ensure_origin(origin.clone())?;
- Err(crate::pallet::DispatchError::Other(
- Error::::NotImplemented.into(),
- ))
-
- // withdraw from overallocated collators, wait until funds unlock, re-allocate to underallocated collators
- // TODO: find some balancing algorithm that does this or just reuse the one we use for deposits
-
- // Self::deposit_event(Event::StartedRebalance(amount));
- // Ok(())
- }
-
- /// Starts the lottery by scheduling a [`Call::draw_lottery`] call
- ///
- /// Can only be called by the account set as [`Config::ManageOrigin`]
- ///
- /// # Errors
- ///
- /// Returns an error if:
- /// * BadOrigin: Caller is not ManageOrigin
- /// * The pallet does not have enough funds to pay for gas fees for at least the first drawing.
- /// * The drawing interval is zero or negative.
- /// * The Scheduler implementation failed to schedule the [`Call::draw_lottery`] call.
- ///
- /// # Details
- ///
- /// This function schedules a [`Call::draw_lottery`] call with a delay specified by the [`Config::DrawingInterval`] configuration
- /// using [`frame_support::traits::schedule::Named`] with the lottery pallet's pallet ID configured with [`Config::LotteryPot`] as identifier.
- /// If the lottery is already started, this function will fail.
- ///
- /// You can always learn what block the next drawing - if any - will happen by calling [`Self::next_drawing_at`]
- #[pallet::call_index(4)]
- #[pallet::weight(::WeightInfo::start_lottery())]
- pub fn start_lottery(origin: OriginFor) -> DispatchResult {
- T::ManageOrigin::ensure_origin(origin.clone())?;
- ensure!(
- Self::next_drawing_at().is_none(),
- Error::::LotteryIsRunning
- );
- // Pallet has enough funds to pay gas fees for at least the first drawing
- ensure!(
- Self::surplus_funds() >= Self::gas_reserve(),
- Error::::PotBalanceBelowGasReserve
- );
- // NOTE: If more than gas_reserve is in the pallet, the full excess will be paid out to the winner of the next drawing! This is intended to dope the winning balance with extra rewards
-
- let drawing_interval = ::DrawingInterval::get();
- ensure!(
- drawing_interval > 0u32.into(),
- Error::::PalletMisconfigured
- );
- let lottery_drawing_call: CallOf = Call::draw_lottery {}.into();
- T::Scheduler::schedule_named(
- Self::lottery_schedule_id(),
- DispatchTime::After(drawing_interval),
- Some((drawing_interval, u32::MAX)), // XXX: Seems scheduler has no way to schedule infinite amount
- LOWEST_PRIORITY,
- frame_support::dispatch::RawOrigin::Root.into(),
- MaybeHashed::Value(lottery_drawing_call),
- )
- .map_err(|_| Error::::CouldNotSchedule)?;
-
- Self::deposit_event(Event::LotteryStarted);
- Ok(())
- }
-
- /// Stops the ongoing lottery and cancels the scheduled and any future drawings.
- ///
- /// This function cancels the scheduled drawing. Does not prevent users from interacting with the pallet
- ///
- /// Can only be called by the account set as [`Config::ManageOrigin`]
- ///
- /// # Errors
- ///
- /// * BadOrigin: Caller is not manager
- /// * LotteryNotStarted: Nothing to stop
- ///
- #[pallet::call_index(5)]
- #[pallet::weight(::WeightInfo::stop_lottery())]
- pub fn stop_lottery(origin: OriginFor) -> DispatchResult {
- T::ManageOrigin::ensure_origin(origin.clone())?;
- T::Scheduler::cancel_named(Self::lottery_schedule_id())
- .map_err(|_| Error::::LotteryNotStarted)?;
- Self::deposit_event(Event::LotteryStopped);
- Ok(())
- }
-
- /// Draws a lottery winner and allows them to claim their winnings later. Only the [`Config::ManageOrigin`] can execute this function.
- ///
- /// Can only be called by the account set as [`Config::ManageOrigin`]
- ///
- /// # Errors
- ///
- /// ## Operational
- /// * BadOrigin: Caller is not ManageOrigin
- /// * PotBalanceBelowGasReserve: The balance of the pot is below the gas reserve so no winner will be paid out
- ///
- /// ## Fatal
- /// * ArithmeticError::Underflow: An underflow occurred when calculating the payout.
- /// * PotBalanceTooLow: The balance of the pot is too low.
- /// * NoWinnerFound: Nobody was selected as winner
- #[pallet::call_index(6)]
- #[pallet::weight(::WeightInfo::draw_lottery(Pallet::::total_users(), pallet_parachain_staking::Pallet::::selected_candidates().len() as u32))]
- pub fn draw_lottery(origin: OriginFor) -> DispatchResult {
- T::ManageOrigin::ensure_origin(origin.clone())?;
- let now = >::block_number();
- log::trace!("Drawing lottery called at block {:?}", now.clone());
-
- let total_funds_in_pallet =
- ::Currency::total_balance(
- &Self::account_id(),
- );
- // all surplus tokens accrued at this point can be paid out to the winner
- let winning_claim = Self::current_prize_pool();
- let participating_funds = Self::total_pot();
- log::debug!(
- "drawing: total funds: {:?}, participating funds: {:?}, surplus funds/winner payout: {:?}",
- total_funds_in_pallet.clone(),
- participating_funds.clone(),
- winning_claim.clone()
- );
- // If there's nothing to win or nobody is playing we skip the drawing logic
- if !winning_claim.is_zero() && !participating_funds.is_zero() {
- ensure!(
- // Sanity check: Prevent allocating funds as winnings to a user that would have to be paid from user deposits
- Self::sum_of_deposits() // all users' deposits (staked and unstaking)
- .saturating_add(Self::total_unclaimed_winnings()) // all prior winnings
- .saturating_add(winning_claim) // and the current winner's new claim
- <= total_funds_in_pallet, // don't exceed funds in the pallet
- Error::::PotBalanceTooLow
- );
- Self::select_winner(winning_claim)?;
- } else {
- log::debug!(
- "drawing: skipped due to zero winning claim {:?} or participating funds {:?}",
- winning_claim,
- participating_funds,
- );
- }
- // unstake, pay out tokens due for withdrawals and restake excess funds
- // At this point, all excess funds except for `gas_reserve` have been reserved for the current winner
- Self::process_matured_withdrawals(origin)?;
- Ok(())
- }
-
- /// This function transfers all withdrawals to user's wallets that are payable from unstaked collators whose timelock expired
- ///
- /// Can only be called by the account set as [`Config::ManageOrigin`]
- ///
- /// # Errors
- ///
- /// * BadOrigin: Caller is not ManageOrigin
- /// * errors defined by the do_process_matured_withdrawals function.
- #[pallet::call_index(7)]
- #[pallet::weight(::WeightInfo::process_matured_withdrawals())]
- pub fn process_matured_withdrawals(origin: OriginFor) -> DispatchResult {
- log::trace!("process_matured_withdrawals");
- T::ManageOrigin::ensure_origin(origin.clone())?;
- Self::finish_unstaking_collators();
- Self::do_process_matured_withdrawals()?;
- Self::do_rebalance_remaining_funds()?;
- Ok(())
- }
-
- /// Liquidates all funds held in the lottery pallet, unstaking collators, returning user deposits and paying out winnings
- ///
- /// Can only be called by the account set as [`Config::ManageOrigin`]
- ///
- /// Due to staking timelock, this schedules the payout of user deposits after timelock has expired.
- /// NOTE: TODO: Any interaction with this pallet is disallowed while a liquidation is ongoing
- ///
- /// # Errors
- ///
- /// * BadOrigin: Caller is not ManageOrigin
- /// * Fails if a lottery has not been stopped and a drawing is ongoing
- #[pallet::call_index(8)]
- #[pallet::weight(0)]
- pub fn liquidate_lottery(origin: OriginFor) -> DispatchResult {
- T::ManageOrigin::ensure_origin(origin.clone())?;
-
- ensure!(
- Self::next_drawing_at().is_none(),
- Error::::LotteryIsRunning
- );
-
- Err(crate::pallet::DispatchError::Other(
- Error::::NotImplemented.into(),
- ))
-
- // TODO: Unstake all collators, schedule return of all user deposits
- // for collator in collators_we_staked_to {
- // do_unstake(collator);
- // }
- // TODO: Lock everything until this process is finished
-
- // TODO: return user deposits and pay out winnings, deposit event
-
- // Ok(())
- }
- #[pallet::call_index(9)]
- #[pallet::weight(::WeightInfo::set_min_deposit())]
- pub fn set_min_deposit(origin: OriginFor, min_deposit: BalanceOf