mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 22:11:06 +00:00
Gilts Pallet (#8139)
* Initial draft * Enlarge function drafted. * Thaw draft * Retract_bid draft * Final bits of draft impl. * Test mockup * Tests * Docs * Add benchmark scaffold * Integrate weights * All benchmarks done * Missing file * Remove stale comments * Fixes * Fixes * Allow for priority queuing. * Another test and a fix * Fixes * Fixes * cargo run --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_gilt --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/gilt/src/weights.rs --template=./.maintain/frame-weight-template.hbs * Grumble * Update frame/gilt/src/tests.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Update frame/gilt/src/tests.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Grumble * Update frame/gilt/src/tests.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Update frame/gilt/src/lib.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Update frame/gilt/src/lib.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Fix unreserve ordering * Grumble * Fixes Co-authored-by: Parity Benchmarking Bot <admin@parity.io> Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
[package]
|
||||
name = "pallet-gilt"
|
||||
version = "3.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://substrate.dev"
|
||||
repository = "https://github.com/paritytech/substrate/"
|
||||
description = "FRAME pallet for rewarding account freezing."
|
||||
readme = "README.md"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.101", optional = true }
|
||||
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] }
|
||||
sp-std = { version = "3.0.0", default-features = false, path = "../../primitives/std" }
|
||||
sp-runtime = { version = "3.0.0", default-features = false, path = "../../primitives/runtime" }
|
||||
sp-arithmetic = { version = "3.0.0", default-features = false, path = "../../primitives/arithmetic" }
|
||||
frame-benchmarking = { version = "3.0.0", default-features = false, path = "../benchmarking", optional = true }
|
||||
frame-support = { version = "3.0.0", default-features = false, path = "../support" }
|
||||
frame-system = { version = "3.0.0", default-features = false, path = "../system" }
|
||||
|
||||
[dev-dependencies]
|
||||
sp-io = { version = "3.0.0", path = "../../primitives/io" }
|
||||
sp-core = { version = "3.0.0", path = "../../primitives/core" }
|
||||
pallet-balances = { version = "3.0.0", path = "../balances" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"serde",
|
||||
"codec/std",
|
||||
"sp-std/std",
|
||||
"sp-runtime/std",
|
||||
"sp-arithmetic/std",
|
||||
"frame-benchmarking/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"frame-benchmarking",
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
]
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
License: Apache-2.0
|
||||
@@ -0,0 +1,136 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2021 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Benchmarks for Gilt Pallet
|
||||
|
||||
#![cfg(feature = "runtime-benchmarks")]
|
||||
|
||||
use sp_std::prelude::*;
|
||||
use super::*;
|
||||
use sp_runtime::traits::{Zero, Bounded};
|
||||
use sp_arithmetic::Perquintill;
|
||||
use frame_system::RawOrigin;
|
||||
use frame_benchmarking::{benchmarks, whitelisted_caller, impl_benchmark_test_suite};
|
||||
use frame_support::{traits::{Currency, Get, EnsureOrigin}, dispatch::UnfilteredDispatchable};
|
||||
|
||||
use crate::Pallet as Gilt;
|
||||
|
||||
type BalanceOf<T> = <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
||||
|
||||
benchmarks! {
|
||||
place_bid {
|
||||
let l in 0..(T::MaxQueueLen::get() - 1);
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
|
||||
for i in 0..l {
|
||||
Gilt::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?;
|
||||
}
|
||||
}: _(RawOrigin::Signed(caller.clone()), T::MinFreeze::get() * BalanceOf::<T>::from(2u32), 1)
|
||||
verify {
|
||||
assert_eq!(QueueTotals::<T>::get()[0], (l + 1, T::MinFreeze::get() * BalanceOf::<T>::from(l + 2)));
|
||||
}
|
||||
|
||||
place_bid_max {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
|
||||
for i in 0..T::MaxQueueLen::get() {
|
||||
Gilt::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?;
|
||||
}
|
||||
}: {
|
||||
Gilt::<T>::place_bid(
|
||||
RawOrigin::Signed(caller.clone()).into(),
|
||||
T::MinFreeze::get() * BalanceOf::<T>::from(2u32),
|
||||
1,
|
||||
)?
|
||||
}
|
||||
verify {
|
||||
assert_eq!(QueueTotals::<T>::get()[0], (
|
||||
T::MaxQueueLen::get(),
|
||||
T::MinFreeze::get() * BalanceOf::<T>::from(T::MaxQueueLen::get() + 1),
|
||||
));
|
||||
}
|
||||
|
||||
retract_bid {
|
||||
let l in 1..T::MaxQueueLen::get();
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
|
||||
for i in 0..l {
|
||||
Gilt::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?;
|
||||
}
|
||||
}: _(RawOrigin::Signed(caller.clone()), T::MinFreeze::get(), 1)
|
||||
verify {
|
||||
assert_eq!(QueueTotals::<T>::get()[0], (l - 1, T::MinFreeze::get() * BalanceOf::<T>::from(l - 1)));
|
||||
}
|
||||
|
||||
set_target {
|
||||
let call = Call::<T>::set_target(Default::default());
|
||||
let origin = T::AdminOrigin::successful_origin();
|
||||
}: { call.dispatch_bypass_filter(origin)? }
|
||||
|
||||
thaw {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
T::Currency::make_free_balance_be(&caller, T::MinFreeze::get() * BalanceOf::<T>::from(3u32));
|
||||
Gilt::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?;
|
||||
Gilt::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?;
|
||||
Gilt::<T>::enlarge(T::MinFreeze::get() * BalanceOf::<T>::from(2u32), 2);
|
||||
Active::<T>::mutate(0, |m_g| if let Some(ref mut g) = m_g { g.expiry = Zero::zero() });
|
||||
}: _(RawOrigin::Signed(caller.clone()), 0)
|
||||
verify {
|
||||
assert!(Active::<T>::get(0).is_none());
|
||||
}
|
||||
|
||||
pursue_target_noop {
|
||||
}: { Gilt::<T>::pursue_target(0) }
|
||||
|
||||
pursue_target_per_item {
|
||||
// bids taken
|
||||
let b in 1..T::MaxQueueLen::get();
|
||||
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
T::Currency::make_free_balance_be(&caller, T::MinFreeze::get() * BalanceOf::<T>::from(b + 1));
|
||||
|
||||
for _ in 0..b {
|
||||
Gilt::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?;
|
||||
}
|
||||
|
||||
Call::<T>::set_target(Perquintill::from_percent(100))
|
||||
.dispatch_bypass_filter(T::AdminOrigin::successful_origin())?;
|
||||
|
||||
}: { Gilt::<T>::pursue_target(b) }
|
||||
|
||||
pursue_target_per_queue {
|
||||
// total queues hit
|
||||
let q in 1..T::QueueCount::get();
|
||||
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
T::Currency::make_free_balance_be(&caller, T::MinFreeze::get() * BalanceOf::<T>::from(q + 1));
|
||||
|
||||
for i in 0..q {
|
||||
Gilt::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), i + 1)?;
|
||||
}
|
||||
|
||||
Call::<T>::set_target(Perquintill::from_percent(100))
|
||||
.dispatch_bypass_filter(T::AdminOrigin::successful_origin())?;
|
||||
|
||||
}: { Gilt::<T>::pursue_target(q) }
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(
|
||||
Gilt,
|
||||
crate::mock::new_test_ext(),
|
||||
crate::mock::Test,
|
||||
);
|
||||
@@ -0,0 +1,582 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! # Gilt Pallet
|
||||
//! A pallet allowing accounts to auction for being frozen and receive open-ended
|
||||
//! inflation-protection in return.
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! Lock up tokens, for at least as long as you offer, and be free from both inflation and
|
||||
//! intermediate reward or exchange until the tokens become unlocked.
|
||||
//!
|
||||
//! ## Design
|
||||
//!
|
||||
//! Queues for each of 1-`QueueCount` periods, given in blocks (`Period`). Queues are limited in
|
||||
//! size to something sensible, `MaxQueueLen`. A secondary storage item with `QueueCount` x `u32`
|
||||
//! elements with the number of items in each queue.
|
||||
//!
|
||||
//! Queues are split into two parts. The first part is a priority queue based on bid size. The
|
||||
//! second part is just a FIFO (the size of the second part is set with `FifoQueueLen`). Items are
|
||||
//! always prepended so that removal is always O(1) since removal often happens many times under a
|
||||
//! single weighed function (`on_initialize`) yet placing bids only ever happens once per weighed
|
||||
//! function (`place_bid`). If the queue has a priority portion, then it remains sorted in order of
|
||||
//! bid size so that smaller bids fall off as it gets too large.
|
||||
//!
|
||||
//! Account may enqueue a balance with some number of `Period`s lock up, up to a maximum of
|
||||
//! `QueueCount`. The balance gets reserved. There's a minimum of `MinFreeze` to avoid dust.
|
||||
//!
|
||||
//! Until your bid is turned into an issued gilt you can retract it instantly and the funds are
|
||||
//! unreserved.
|
||||
//!
|
||||
//! There's a target proportion of effective total issuance (i.e. accounting for existing gilts)
|
||||
//! which the we attempt to have frozen at any one time. It will likely be gradually increased over
|
||||
//! time by governance.
|
||||
//!
|
||||
//! As the total funds frozen under gilts drops below `FrozenFraction` of the total effective
|
||||
//! issuance, then bids are taken from queues, with the queue of the greatest period taking
|
||||
//! priority. If the item in the queue's locked amount is greater than the amount left to be
|
||||
//! frozen, then it is split up into multiple bids and becomes partially frozen under gilt.
|
||||
//!
|
||||
//! Once an account's balance is frozen, it remains frozen until the owner thaws the balance of the
|
||||
//! account. This may happen no earlier than queue's period after the point at which the gilt is
|
||||
//! issued.
|
||||
//!
|
||||
//! ## Suggested Values
|
||||
//!
|
||||
//! - `QueueCount`: 300
|
||||
//! - `Period`: 432,000
|
||||
//! - `MaxQueueLen`: 1000
|
||||
//! - `MinFreeze`: Around CHF 100 in value.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod benchmarking;
|
||||
pub mod weights;
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use sp_std::prelude::*;
|
||||
use sp_arithmetic::{Perquintill, PerThing};
|
||||
use sp_runtime::traits::{Zero, Saturating, SaturatedConversion};
|
||||
use frame_support::traits::{Currency, OnUnbalanced, ReservableCurrency};
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
pub use crate::weights::WeightInfo;
|
||||
|
||||
type BalanceOf<T> = <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
||||
type PositiveImbalanceOf<T> =
|
||||
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::PositiveImbalance;
|
||||
type NegativeImbalanceOf<T> =
|
||||
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
/// Overarching event type.
|
||||
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
|
||||
|
||||
/// Currency type that this works on.
|
||||
type Currency: ReservableCurrency<Self::AccountId>;
|
||||
|
||||
/// Origin required for setting the target proportion to be under gilt.
|
||||
type AdminOrigin: EnsureOrigin<Self::Origin>;
|
||||
|
||||
/// Unbalanced handler to account for funds created (in case of a higher total issuance over
|
||||
/// freezing period).
|
||||
type Deficit: OnUnbalanced<PositiveImbalanceOf<Self>>;
|
||||
|
||||
/// Unbalanced handler to account for funds destroyed (in case of a lower total issuance
|
||||
/// over freezing period).
|
||||
type Surplus: OnUnbalanced<NegativeImbalanceOf<Self>>;
|
||||
|
||||
/// Number of duration queues in total. This sets the maximum duration supported, which is
|
||||
/// this value multiplied by `Period`.
|
||||
#[pallet::constant]
|
||||
type QueueCount: Get<u32>;
|
||||
|
||||
/// Maximum number of items that may be in each duration queue.
|
||||
#[pallet::constant]
|
||||
type MaxQueueLen: Get<u32>;
|
||||
|
||||
/// Portion of the queue which is free from ordering and just a FIFO.
|
||||
///
|
||||
/// Must be no greater than `MaxQueueLen`.
|
||||
#[pallet::constant]
|
||||
type FifoQueueLen: Get<u32>;
|
||||
|
||||
/// The base period for the duration queues. This is the common multiple across all
|
||||
/// supported freezing durations that can be bid upon.
|
||||
#[pallet::constant]
|
||||
type Period: Get<Self::BlockNumber>;
|
||||
|
||||
/// The minimum amount of funds that may be offered to freeze for a gilt. Note that this
|
||||
/// does not actually limit the amount which may be frozen in a gilt since gilts may be
|
||||
/// split up in order to satisfy the desired amount of funds under gilts.
|
||||
///
|
||||
/// It should be at least big enough to ensure that there is no possible storage spam attack
|
||||
/// or queue-filling attack.
|
||||
#[pallet::constant]
|
||||
type MinFreeze: Get<BalanceOf<Self>>;
|
||||
|
||||
/// The number of blocks between consecutive attempts to issue more gilts in an effort to
|
||||
/// get to the target amount to be frozen.
|
||||
///
|
||||
/// A larger value results in fewer storage hits each block, but a slower period to get to
|
||||
/// the target.
|
||||
#[pallet::constant]
|
||||
type IntakePeriod: Get<Self::BlockNumber>;
|
||||
|
||||
/// The maximum amount of bids that can be turned into issued gilts each block. A larger
|
||||
/// value here means less of the block available for transactions should there be a glut of
|
||||
/// bids to make into gilts to reach the target.
|
||||
#[pallet::constant]
|
||||
type MaxIntakeBids: Get<u32>;
|
||||
|
||||
/// Information on runtime weights.
|
||||
type WeightInfo: WeightInfo;
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
#[pallet::generate_store(pub(super) trait Store)]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
/// A single bid on a gilt, an item of a *queue* in `Queues`.
|
||||
#[derive(Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug)]
|
||||
pub struct GiltBid<Balance, AccountId> {
|
||||
/// The amount bid.
|
||||
pub amount: Balance,
|
||||
/// The owner of the bid.
|
||||
pub who: AccountId,
|
||||
}
|
||||
|
||||
/// Information representing an active gilt.
|
||||
#[derive(Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug)]
|
||||
pub struct ActiveGilt<Balance, AccountId, BlockNumber> {
|
||||
/// The proportion of the effective total issuance (i.e. accounting for any eventual gilt
|
||||
/// expansion or contraction that may eventually be claimed).
|
||||
pub proportion: Perquintill,
|
||||
/// The amount reserved under this gilt.
|
||||
pub amount: Balance,
|
||||
/// The account to whom this gilt belongs.
|
||||
pub who: AccountId,
|
||||
/// The time after which this gilt can be redeemed for the proportional amount of balance.
|
||||
pub expiry: BlockNumber,
|
||||
}
|
||||
|
||||
/// An index for a gilt.
|
||||
pub type ActiveIndex = u32;
|
||||
|
||||
/// Overall information package on the active gilts.
|
||||
///
|
||||
/// The way of determining the net issuance (i.e. after factoring in all maturing frozen funds)
|
||||
/// is:
|
||||
///
|
||||
/// `total_issuance - frozen + proportion * total_issuance`
|
||||
#[derive(Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug)]
|
||||
pub struct ActiveGiltsTotal<Balance> {
|
||||
/// The total amount of funds held in reserve for all active gilts.
|
||||
pub frozen: Balance,
|
||||
/// The proportion of funds that the `frozen` balance represents to total issuance.
|
||||
pub proportion: Perquintill,
|
||||
/// The total number of gilts issued so far.
|
||||
pub index: ActiveIndex,
|
||||
/// The target proportion of gilts within total issuance.
|
||||
pub target: Perquintill,
|
||||
}
|
||||
|
||||
/// The totals of items and balances within each queue. Saves a lot of storage reads in the
|
||||
/// case of sparsely packed queues.
|
||||
///
|
||||
/// The vector is indexed by duration in `Period`s, offset by one, so information on the queue
|
||||
/// whose duration is one `Period` would be storage `0`.
|
||||
#[pallet::storage]
|
||||
pub type QueueTotals<T> = StorageValue<_, Vec<(u32, BalanceOf<T>)>, ValueQuery>;
|
||||
|
||||
/// The queues of bids ready to become gilts. Indexed by duration (in `Period`s).
|
||||
#[pallet::storage]
|
||||
pub type Queues<T: Config> = StorageMap<
|
||||
_,
|
||||
Blake2_128Concat,
|
||||
u32,
|
||||
Vec<GiltBid<BalanceOf<T>, T::AccountId>>,
|
||||
ValueQuery,
|
||||
>;
|
||||
|
||||
/// Information relating to the gilts currently active.
|
||||
#[pallet::storage]
|
||||
pub type ActiveTotal<T> = StorageValue<_, ActiveGiltsTotal<BalanceOf<T>>, ValueQuery>;
|
||||
|
||||
/// The currently active gilts, indexed according to the order of creation.
|
||||
#[pallet::storage]
|
||||
pub type Active<T> = StorageMap<
|
||||
_,
|
||||
Blake2_128Concat,
|
||||
ActiveIndex,
|
||||
ActiveGilt<BalanceOf<T>, <T as frame_system::Config>::AccountId, <T as frame_system::Config>::BlockNumber>,
|
||||
OptionQuery,
|
||||
>;
|
||||
|
||||
#[pallet::genesis_config]
|
||||
#[derive(Default)]
|
||||
pub struct GenesisConfig;
|
||||
|
||||
#[pallet::genesis_build]
|
||||
impl<T: Config> GenesisBuild<T> for GenesisConfig {
|
||||
fn build(&self) {
|
||||
QueueTotals::<T>::put(vec![(0, BalanceOf::<T>::zero()); T::QueueCount::get() as usize]);
|
||||
}
|
||||
}
|
||||
|
||||
#[pallet::event]
|
||||
#[pallet::metadata(T::AccountId = "AccountId")]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
/// A bid was successfully placed.
|
||||
/// \[ who, amount, duration \]
|
||||
BidPlaced(T::AccountId, BalanceOf<T>, u32),
|
||||
/// A bid was successfully removed (before being accepted as a gilt).
|
||||
/// \[ who, amount, duration \]
|
||||
BidRetracted(T::AccountId, BalanceOf<T>, u32),
|
||||
/// A bid was accepted as a gilt. The balance may not be released until expiry.
|
||||
/// \[ index, expiry, who, amount \]
|
||||
GiltIssued(ActiveIndex, T::BlockNumber, T::AccountId, BalanceOf<T>),
|
||||
/// An expired gilt has been thawed.
|
||||
/// \[ index, who, original_amount, additional_amount \]
|
||||
GiltThawed(ActiveIndex, T::AccountId, BalanceOf<T>, BalanceOf<T>),
|
||||
}
|
||||
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
/// The duration of the bid is less than one.
|
||||
DurationTooSmall,
|
||||
/// The duration is the bid is greater than the number of queues.
|
||||
DurationTooBig,
|
||||
/// The amount of the bid is less than the minimum allowed.
|
||||
AmountTooSmall,
|
||||
/// The queue for the bid's duration is full and the amount bid is too low to get in through
|
||||
/// replacing an existing bid.
|
||||
BidTooLow,
|
||||
/// Gilt index is unknown.
|
||||
Unknown,
|
||||
/// Not the owner of the gilt.
|
||||
NotOwner,
|
||||
/// Gilt not yet at expiry date.
|
||||
NotExpired,
|
||||
/// The given bid for retraction is not found.
|
||||
NotFound,
|
||||
}
|
||||
|
||||
#[pallet::hooks]
|
||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
||||
fn on_initialize(n: T::BlockNumber) -> Weight {
|
||||
if (n % T::IntakePeriod::get()).is_zero() {
|
||||
Self::pursue_target(T::MaxIntakeBids::get())
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pallet::call]
|
||||
impl<T:Config> Pallet<T> {
|
||||
/// Place a bid for a gilt to be issued.
|
||||
///
|
||||
/// Origin must be Signed, and account must have at least `amount` in free balance.
|
||||
///
|
||||
/// - `amount`: The amount of the bid; these funds will be reserved. If the bid is
|
||||
/// successfully elevated into an issued gilt, then these funds will continue to be
|
||||
/// reserved until the gilt expires. Must be at least `MinFreeze`.
|
||||
/// - `duration`: The number of periods for which the funds will be locked if the gilt is
|
||||
/// issued. It will expire only after this period has elapsed after the point of issuance.
|
||||
/// Must be greater than 1 and no more than `QueueCount`.
|
||||
///
|
||||
/// Complexities:
|
||||
/// - `Queues[duration].len()` (just take max).
|
||||
#[pallet::weight(T::WeightInfo::place_bid_max())]
|
||||
pub fn place_bid(
|
||||
origin: OriginFor<T>,
|
||||
#[pallet::compact] amount: BalanceOf<T>,
|
||||
duration: u32,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
ensure!(amount >= T::MinFreeze::get(), Error::<T>::AmountTooSmall);
|
||||
let queue_count = T::QueueCount::get() as usize;
|
||||
let queue_index = duration.checked_sub(1)
|
||||
.ok_or(Error::<T>::DurationTooSmall)? as usize;
|
||||
ensure!(queue_index < queue_count, Error::<T>::DurationTooBig);
|
||||
|
||||
let net = Queues::<T>::try_mutate(duration, |q|
|
||||
-> Result<(u32, BalanceOf::<T>), DispatchError>
|
||||
{
|
||||
let queue_full = q.len() == T::MaxQueueLen::get() as usize;
|
||||
ensure!(!queue_full || q[0].amount < amount, Error::<T>::BidTooLow);
|
||||
T::Currency::reserve(&who, amount)?;
|
||||
|
||||
// queue is <Ordered: Lowest ... Highest><Fifo: Last ... First>
|
||||
let mut bid = GiltBid { amount, who: who.clone() };
|
||||
let net = if queue_full {
|
||||
sp_std::mem::swap(&mut q[0], &mut bid);
|
||||
T::Currency::unreserve(&bid.who, bid.amount);
|
||||
(0, amount - bid.amount)
|
||||
} else {
|
||||
q.insert(0, bid);
|
||||
(1, amount)
|
||||
};
|
||||
|
||||
let sorted_item_count = q.len().saturating_sub(T::FifoQueueLen::get() as usize);
|
||||
if sorted_item_count > 1 {
|
||||
q[0..sorted_item_count].sort_by_key(|x| x.amount);
|
||||
}
|
||||
|
||||
Ok(net)
|
||||
})?;
|
||||
QueueTotals::<T>::mutate(|qs| {
|
||||
qs.resize(queue_count, (0, Zero::zero()));
|
||||
qs[queue_index].0 += net.0;
|
||||
qs[queue_index].1 = qs[queue_index].1.saturating_add(net.1);
|
||||
});
|
||||
Self::deposit_event(Event::BidPlaced(who.clone(), amount, duration));
|
||||
|
||||
Ok(().into())
|
||||
}
|
||||
|
||||
/// Retract a previously placed bid.
|
||||
///
|
||||
/// Origin must be Signed, and the account should have previously issued a still-active bid
|
||||
/// of `amount` for `duration`.
|
||||
///
|
||||
/// - `amount`: The amount of the previous bid.
|
||||
/// - `duration`: The duration of the previous bid.
|
||||
#[pallet::weight(T::WeightInfo::place_bid(T::MaxQueueLen::get()))]
|
||||
pub fn retract_bid(
|
||||
origin: OriginFor<T>,
|
||||
#[pallet::compact] amount: BalanceOf<T>,
|
||||
duration: u32,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
let queue_count = T::QueueCount::get() as usize;
|
||||
let queue_index = duration.checked_sub(1)
|
||||
.ok_or(Error::<T>::DurationTooSmall)? as usize;
|
||||
ensure!(queue_index < queue_count, Error::<T>::DurationTooBig);
|
||||
|
||||
let bid = GiltBid { amount, who };
|
||||
let new_len = Queues::<T>::try_mutate(duration, |q| -> Result<u32, DispatchError> {
|
||||
let pos = q.iter().position(|i| i == &bid).ok_or(Error::<T>::NotFound)?;
|
||||
q.remove(pos);
|
||||
Ok(q.len() as u32)
|
||||
})?;
|
||||
|
||||
QueueTotals::<T>::mutate(|qs| {
|
||||
qs.resize(queue_count, (0, Zero::zero()));
|
||||
qs[queue_index].0 = new_len;
|
||||
qs[queue_index].1 = qs[queue_index].1.saturating_sub(bid.amount);
|
||||
});
|
||||
|
||||
T::Currency::unreserve(&bid.who, bid.amount);
|
||||
Self::deposit_event(Event::BidRetracted(bid.who, bid.amount, duration));
|
||||
|
||||
Ok(().into())
|
||||
}
|
||||
|
||||
/// Set target proportion of gilt-funds.
|
||||
///
|
||||
/// Origin must be `AdminOrigin`.
|
||||
///
|
||||
/// - `target`: The target proportion of effective issued funds that should be under gilts
|
||||
/// at any one time.
|
||||
#[pallet::weight(T::WeightInfo::set_target())]
|
||||
pub fn set_target(
|
||||
origin: OriginFor<T>,
|
||||
#[pallet::compact] target: Perquintill,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
T::AdminOrigin::ensure_origin(origin)?;
|
||||
ActiveTotal::<T>::mutate(|totals| totals.target = target);
|
||||
Ok(().into())
|
||||
}
|
||||
|
||||
/// Remove an active but expired gilt. Reserved funds under gilt are freed and balance is
|
||||
/// adjusted to ensure that the funds grow or shrink to maintain the equivalent proportion
|
||||
/// of effective total issued funds.
|
||||
///
|
||||
/// Origin must be Signed and the account must be the owner of the gilt of the given index.
|
||||
///
|
||||
/// - `index`: The index of the gilt to be thawed.
|
||||
#[pallet::weight(T::WeightInfo::thaw())]
|
||||
pub fn thaw(
|
||||
origin: OriginFor<T>,
|
||||
#[pallet::compact] index: ActiveIndex,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
// Look for `index`
|
||||
let gilt = Active::<T>::get(index).ok_or(Error::<T>::Unknown)?;
|
||||
// If found, check the owner is `who`.
|
||||
ensure!(gilt.who == who, Error::<T>::NotOwner);
|
||||
let now = frame_system::Module::<T>::block_number();
|
||||
ensure!(now >= gilt.expiry, Error::<T>::NotExpired);
|
||||
// Remove it
|
||||
Active::<T>::remove(index);
|
||||
|
||||
// Multiply the proportion it is by the total issued.
|
||||
let total_issuance = T::Currency::total_issuance();
|
||||
ActiveTotal::<T>::mutate(|totals| {
|
||||
let nongilt_issuance: u128 = total_issuance.saturating_sub(totals.frozen)
|
||||
.saturated_into();
|
||||
let effective_issuance = totals.proportion.left_from_one()
|
||||
.saturating_reciprocal_mul(nongilt_issuance);
|
||||
let gilt_value: BalanceOf<T> = (gilt.proportion * effective_issuance).saturated_into();
|
||||
|
||||
totals.frozen = totals.frozen.saturating_sub(gilt.amount);
|
||||
totals.proportion = totals.proportion.saturating_sub(gilt.proportion);
|
||||
|
||||
// Remove or mint the additional to the amount using `Deficit`/`Surplus`.
|
||||
if gilt_value > gilt.amount {
|
||||
// Unreserve full amount.
|
||||
T::Currency::unreserve(&gilt.who, gilt.amount);
|
||||
let amount = gilt_value - gilt.amount;
|
||||
let deficit = T::Currency::deposit_creating(&gilt.who, amount);
|
||||
T::Deficit::on_unbalanced(deficit);
|
||||
} else {
|
||||
if gilt_value < gilt.amount {
|
||||
// We take anything reserved beyond the gilt's final value.
|
||||
let rest = gilt.amount - gilt_value;
|
||||
// `slash` might seem a little aggressive, but it's the only way to do it
|
||||
// in case it's locked into the staking system.
|
||||
let surplus = T::Currency::slash_reserved(&gilt.who, rest).0;
|
||||
T::Surplus::on_unbalanced(surplus);
|
||||
}
|
||||
// Unreserve only its new value (less than the amount reserved). Everything
|
||||
// should add up, but (defensive) in case it doesn't, unreserve takes lower
|
||||
// priority over the funds.
|
||||
let err_amt = T::Currency::unreserve(&gilt.who, gilt_value);
|
||||
debug_assert!(err_amt.is_zero());
|
||||
}
|
||||
|
||||
let e = Event::GiltThawed(index, gilt.who, gilt.amount, gilt_value);
|
||||
Self::deposit_event(e);
|
||||
});
|
||||
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Attempt to enlarge our gilt-set from bids in order to satisfy our desired target amount
|
||||
/// of funds frozen into gilts.
|
||||
pub fn pursue_target(max_bids: u32) -> Weight {
|
||||
let totals = ActiveTotal::<T>::get();
|
||||
if totals.proportion < totals.target {
|
||||
let missing = totals.target.saturating_sub(totals.proportion);
|
||||
|
||||
let total_issuance = T::Currency::total_issuance();
|
||||
let nongilt_issuance: u128 = total_issuance.saturating_sub(totals.frozen)
|
||||
.saturated_into();
|
||||
let effective_issuance = totals.proportion.left_from_one()
|
||||
.saturating_reciprocal_mul(nongilt_issuance);
|
||||
let intake: BalanceOf<T> = (missing * effective_issuance).saturated_into();
|
||||
|
||||
let (bids_taken, queues_hit) = Self::enlarge(intake, max_bids);
|
||||
let first_from_each_queue = T::WeightInfo::pursue_target_per_queue(queues_hit);
|
||||
let rest_from_each_queue = T::WeightInfo::pursue_target_per_item(bids_taken)
|
||||
.saturating_sub(T::WeightInfo::pursue_target_per_item(queues_hit));
|
||||
first_from_each_queue + rest_from_each_queue
|
||||
} else {
|
||||
T::WeightInfo::pursue_target_noop()
|
||||
}
|
||||
}
|
||||
|
||||
/// Freeze additional funds from queue of bids up to `amount`. Use at most `max_bids`
|
||||
/// from the queue.
|
||||
///
|
||||
/// Return the number of bids taken and the number of distinct queues taken from.
|
||||
pub fn enlarge(
|
||||
amount: BalanceOf<T>,
|
||||
max_bids: u32,
|
||||
) -> (u32, u32) {
|
||||
let total_issuance = T::Currency::total_issuance();
|
||||
let mut remaining = amount;
|
||||
let mut bids_taken = 0;
|
||||
let mut queues_hit = 0;
|
||||
let now = frame_system::Module::<T>::block_number();
|
||||
|
||||
ActiveTotal::<T>::mutate(|totals| {
|
||||
QueueTotals::<T>::mutate(|qs| {
|
||||
for duration in (1..=T::QueueCount::get()).rev() {
|
||||
if qs[duration as usize - 1].0 == 0 {
|
||||
continue
|
||||
}
|
||||
let queue_index = duration as usize - 1;
|
||||
let expiry = now.saturating_add(T::Period::get().saturating_mul(duration.into()));
|
||||
Queues::<T>::mutate(duration, |q| {
|
||||
while let Some(mut bid) = q.pop() {
|
||||
if remaining < bid.amount {
|
||||
let overflow = bid.amount - remaining;
|
||||
bid.amount = remaining;
|
||||
q.push(GiltBid { amount: overflow, who: bid.who.clone() });
|
||||
}
|
||||
let amount = bid.amount;
|
||||
// Can never overflow due to block above.
|
||||
remaining -= amount;
|
||||
// Should never underflow since it should track the total of the bids
|
||||
// exactly, but we'll be defensive.
|
||||
qs[queue_index].1 = qs[queue_index].1.saturating_sub(bid.amount);
|
||||
|
||||
// Now to activate the bid...
|
||||
let nongilt_issuance: u128 = total_issuance.saturating_sub(totals.frozen)
|
||||
.saturated_into();
|
||||
let effective_issuance = totals.proportion.left_from_one()
|
||||
.saturating_reciprocal_mul(nongilt_issuance);
|
||||
let n: u128 = amount.saturated_into();
|
||||
let d = effective_issuance;
|
||||
let proportion = Perquintill::from_rational_approximation(n, d);
|
||||
let who = bid.who;
|
||||
let index = totals.index;
|
||||
totals.frozen += bid.amount;
|
||||
totals.proportion = totals.proportion.saturating_add(proportion);
|
||||
totals.index += 1;
|
||||
let e = Event::GiltIssued(index, expiry, who.clone(), amount);
|
||||
Self::deposit_event(e);
|
||||
let gilt = ActiveGilt { amount, proportion, who, expiry };
|
||||
Active::<T>::insert(index, gilt);
|
||||
|
||||
bids_taken += 1;
|
||||
|
||||
if remaining.is_zero() || bids_taken == max_bids {
|
||||
break;
|
||||
}
|
||||
}
|
||||
queues_hit += 1;
|
||||
qs[queue_index].0 = q.len() as u32;
|
||||
});
|
||||
if remaining.is_zero() || bids_taken == max_bids {
|
||||
break
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
(bids_taken, queues_hit)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Test environment for Gilt pallet.
|
||||
|
||||
use crate as pallet_gilt;
|
||||
|
||||
use frame_support::{
|
||||
parameter_types, ord_parameter_types, traits::{OnInitialize, OnFinalize, GenesisBuild},
|
||||
};
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{traits::{BlakeTwo256, IdentityLookup}, testing::Header};
|
||||
|
||||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
|
||||
// Configure a mock runtime to test the pallet.
|
||||
frame_support::construct_runtime!(
|
||||
pub enum Test where
|
||||
Block = Block,
|
||||
NodeBlock = Block,
|
||||
UncheckedExtrinsic = UncheckedExtrinsic,
|
||||
{
|
||||
System: frame_system::{Module, Call, Config, Storage, Event<T>},
|
||||
Balances: pallet_balances::{Module, Call, Config<T>, Storage, Event<T>},
|
||||
Gilt: pallet_gilt::{Module, Call, Config, Storage, Event<T>},
|
||||
}
|
||||
);
|
||||
|
||||
parameter_types! {
|
||||
pub const BlockHashCount: u64 = 250;
|
||||
pub const SS58Prefix: u8 = 42;
|
||||
}
|
||||
|
||||
impl frame_system::Config for Test {
|
||||
type BaseCallFilter = ();
|
||||
type BlockWeights = ();
|
||||
type BlockLength = ();
|
||||
type DbWeight = ();
|
||||
type Origin = Origin;
|
||||
type Call = Call;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type Hashing = BlakeTwo256;
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type Event = Event;
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type Version = ();
|
||||
type PalletInfo = PalletInfo;
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
type SystemWeightInfo = ();
|
||||
type SS58Prefix = SS58Prefix;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
}
|
||||
|
||||
impl pallet_balances::Config for Test {
|
||||
type MaxLocks = ();
|
||||
type Balance = u64;
|
||||
type DustRemoval = ();
|
||||
type Event = Event;
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type AccountStore = System;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const QueueCount: u32 = 3;
|
||||
pub const MaxQueueLen: u32 = 3;
|
||||
pub const FifoQueueLen: u32 = 1;
|
||||
pub const Period: u64 = 3;
|
||||
pub const MinFreeze: u64 = 2;
|
||||
pub const IntakePeriod: u64 = 2;
|
||||
pub const MaxIntakeBids: u32 = 2;
|
||||
}
|
||||
ord_parameter_types! {
|
||||
pub const One: u64 = 1;
|
||||
}
|
||||
|
||||
impl pallet_gilt::Config for Test {
|
||||
type Event = Event;
|
||||
type Currency = Balances;
|
||||
type AdminOrigin = frame_system::EnsureSignedBy<One, Self::AccountId>;
|
||||
type Deficit = ();
|
||||
type Surplus = ();
|
||||
type QueueCount = QueueCount;
|
||||
type MaxQueueLen = MaxQueueLen;
|
||||
type FifoQueueLen = FifoQueueLen;
|
||||
type Period = Period;
|
||||
type MinFreeze = MinFreeze;
|
||||
type IntakePeriod = IntakePeriod;
|
||||
type MaxIntakeBids = MaxIntakeBids;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
// This function basically just builds a genesis storage key/value store according to
|
||||
// our desired mockup.
|
||||
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||
let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
||||
pallet_balances::GenesisConfig::<Test>{
|
||||
balances: vec![(1, 100), (2, 100), (3, 100), (4, 100)],
|
||||
}.assimilate_storage(&mut t).unwrap();
|
||||
GenesisBuild::<Test>::assimilate_storage(&crate::GenesisConfig, &mut t).unwrap();
|
||||
t.into()
|
||||
}
|
||||
|
||||
pub fn run_to_block(n: u64) {
|
||||
while System::block_number() < n {
|
||||
Gilt::on_finalize(System::block_number());
|
||||
Balances::on_finalize(System::block_number());
|
||||
System::on_finalize(System::block_number());
|
||||
System::set_block_number(System::block_number() + 1);
|
||||
System::on_initialize(System::block_number());
|
||||
Balances::on_initialize(System::block_number());
|
||||
Gilt::on_initialize(System::block_number());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,499 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Tests for Gilt pallet.
|
||||
|
||||
use super::*;
|
||||
use crate::{Error, mock::*};
|
||||
use frame_support::{assert_ok, assert_noop, dispatch::DispatchError, traits::Currency};
|
||||
use sp_arithmetic::Perquintill;
|
||||
use pallet_balances::Error as BalancesError;
|
||||
|
||||
#[test]
|
||||
fn basic_setup_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
|
||||
for q in 0..3 {
|
||||
assert!(Queues::<Test>::get(q).is_empty());
|
||||
}
|
||||
assert_eq!(ActiveTotal::<Test>::get(), ActiveGiltsTotal {
|
||||
frozen: 0,
|
||||
proportion: Perquintill::zero(),
|
||||
index: 0,
|
||||
target: Perquintill::zero(),
|
||||
});
|
||||
assert_eq!(QueueTotals::<Test>::get(), vec![(0, 0); 3]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_target_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
let e = DispatchError::BadOrigin;
|
||||
assert_noop!(Gilt::set_target(Origin::signed(2), Perquintill::from_percent(50)), e);
|
||||
assert_ok!(Gilt::set_target(Origin::signed(1), Perquintill::from_percent(50)));
|
||||
|
||||
assert_eq!(ActiveTotal::<Test>::get(), ActiveGiltsTotal {
|
||||
frozen: 0,
|
||||
proportion: Perquintill::zero(),
|
||||
index: 0,
|
||||
target: Perquintill::from_percent(50),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn place_bid_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
assert_noop!(Gilt::place_bid(Origin::signed(1), 1, 2), Error::<Test>::AmountTooSmall);
|
||||
assert_noop!(Gilt::place_bid(Origin::signed(1), 101, 2), BalancesError::<Test>::InsufficientBalance);
|
||||
assert_noop!(Gilt::place_bid(Origin::signed(1), 10, 4), Error::<Test>::DurationTooBig);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 10, 2));
|
||||
assert_eq!(Balances::reserved_balance(1), 10);
|
||||
assert_eq!(Queues::<Test>::get(2), vec![GiltBid { amount: 10, who: 1 }]);
|
||||
assert_eq!(QueueTotals::<Test>::get(), vec![(0, 0), (1, 10), (0, 0)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn place_bid_queuing_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 20, 2));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 10, 2));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 5, 2));
|
||||
assert_noop!(Gilt::place_bid(Origin::signed(1), 5, 2), Error::<Test>::BidTooLow);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 15, 2));
|
||||
assert_eq!(Balances::reserved_balance(1), 45);
|
||||
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 25, 2));
|
||||
assert_eq!(Balances::reserved_balance(1), 60);
|
||||
assert_noop!(Gilt::place_bid(Origin::signed(1), 10, 2), Error::<Test>::BidTooLow);
|
||||
assert_eq!(Queues::<Test>::get(2), vec![
|
||||
GiltBid { amount: 15, who: 1 },
|
||||
GiltBid { amount: 25, who: 1 },
|
||||
GiltBid { amount: 20, who: 1 },
|
||||
]);
|
||||
assert_eq!(QueueTotals::<Test>::get(), vec![(0, 0), (3, 60), (0, 0)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn place_bid_fails_when_queue_full() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 10, 2));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(2), 10, 2));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(3), 10, 2));
|
||||
assert_noop!(Gilt::place_bid(Origin::signed(4), 10, 2), Error::<Test>::BidTooLow);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(4), 10, 3));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_place_bids_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 10, 1));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 10, 2));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 10, 2));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 10, 3));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(2), 10, 2));
|
||||
|
||||
assert_eq!(Balances::reserved_balance(1), 40);
|
||||
assert_eq!(Balances::reserved_balance(2), 10);
|
||||
assert_eq!(Queues::<Test>::get(1), vec![
|
||||
GiltBid { amount: 10, who: 1 },
|
||||
]);
|
||||
assert_eq!(Queues::<Test>::get(2), vec![
|
||||
GiltBid { amount: 10, who: 2 },
|
||||
GiltBid { amount: 10, who: 1 },
|
||||
GiltBid { amount: 10, who: 1 },
|
||||
]);
|
||||
assert_eq!(Queues::<Test>::get(3), vec![
|
||||
GiltBid { amount: 10, who: 1 },
|
||||
]);
|
||||
assert_eq!(QueueTotals::<Test>::get(), vec![(1, 10), (3, 30), (1, 10)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retract_single_item_queue_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 10, 1));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 10, 2));
|
||||
assert_ok!(Gilt::retract_bid(Origin::signed(1), 10, 1));
|
||||
|
||||
assert_eq!(Balances::reserved_balance(1), 10);
|
||||
assert_eq!(Queues::<Test>::get(1), vec![]);
|
||||
assert_eq!(Queues::<Test>::get(2), vec![ GiltBid { amount: 10, who: 1 } ]);
|
||||
assert_eq!(QueueTotals::<Test>::get(), vec![(0, 0), (1, 10), (0, 0)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retract_with_other_and_duplicate_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 10, 1));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 10, 2));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 10, 2));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(2), 10, 2));
|
||||
|
||||
assert_ok!(Gilt::retract_bid(Origin::signed(1), 10, 2));
|
||||
assert_eq!(Balances::reserved_balance(1), 20);
|
||||
assert_eq!(Balances::reserved_balance(2), 10);
|
||||
assert_eq!(Queues::<Test>::get(1), vec![
|
||||
GiltBid { amount: 10, who: 1 },
|
||||
]);
|
||||
assert_eq!(Queues::<Test>::get(2), vec![
|
||||
GiltBid { amount: 10, who: 2 },
|
||||
GiltBid { amount: 10, who: 1 },
|
||||
]);
|
||||
assert_eq!(QueueTotals::<Test>::get(), vec![(1, 10), (2, 20), (0, 0)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retract_non_existent_item_fails() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
assert_noop!(Gilt::retract_bid(Origin::signed(1), 10, 1), Error::<Test>::NotFound);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 10, 1));
|
||||
assert_noop!(Gilt::retract_bid(Origin::signed(1), 20, 1), Error::<Test>::NotFound);
|
||||
assert_noop!(Gilt::retract_bid(Origin::signed(1), 10, 2), Error::<Test>::NotFound);
|
||||
assert_noop!(Gilt::retract_bid(Origin::signed(2), 10, 1), Error::<Test>::NotFound);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_enlarge_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 40, 1));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(2), 40, 2));
|
||||
Gilt::enlarge(40, 2);
|
||||
|
||||
// Takes 2/2, then stopped because it reaches its max amount
|
||||
assert_eq!(Balances::reserved_balance(1), 40);
|
||||
assert_eq!(Balances::reserved_balance(2), 40);
|
||||
assert_eq!(Queues::<Test>::get(1), vec![ GiltBid { amount: 40, who: 1 } ]);
|
||||
assert_eq!(Queues::<Test>::get(2), vec![]);
|
||||
assert_eq!(QueueTotals::<Test>::get(), vec![(1, 40), (0, 0), (0, 0)]);
|
||||
|
||||
assert_eq!(ActiveTotal::<Test>::get(), ActiveGiltsTotal {
|
||||
frozen: 40,
|
||||
proportion: Perquintill::from_percent(10),
|
||||
index: 1,
|
||||
target: Perquintill::zero(),
|
||||
});
|
||||
assert_eq!(Active::<Test>::get(0).unwrap(), ActiveGilt {
|
||||
proportion: Perquintill::from_percent(10),
|
||||
amount: 40,
|
||||
who: 2,
|
||||
expiry: 7,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enlarge_respects_bids_limit() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 40, 1));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(2), 40, 2));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(3), 40, 2));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(4), 40, 3));
|
||||
Gilt::enlarge(100, 2);
|
||||
|
||||
// Should have taken 4/3 and 2/2, then stopped because it's only allowed 2.
|
||||
assert_eq!(Queues::<Test>::get(1), vec![ GiltBid { amount: 40, who: 1 } ]);
|
||||
assert_eq!(Queues::<Test>::get(2), vec![ GiltBid { amount: 40, who: 3 } ]);
|
||||
assert_eq!(Queues::<Test>::get(3), vec![]);
|
||||
assert_eq!(QueueTotals::<Test>::get(), vec![(1, 40), (1, 40), (0, 0)]);
|
||||
|
||||
assert_eq!(Active::<Test>::get(0).unwrap(), ActiveGilt {
|
||||
proportion: Perquintill::from_percent(10),
|
||||
amount: 40,
|
||||
who: 4,
|
||||
expiry: 10,
|
||||
});
|
||||
assert_eq!(Active::<Test>::get(1).unwrap(), ActiveGilt {
|
||||
proportion: Perquintill::from_percent(10),
|
||||
amount: 40,
|
||||
who: 2,
|
||||
expiry: 7,
|
||||
});
|
||||
assert_eq!(ActiveTotal::<Test>::get(), ActiveGiltsTotal {
|
||||
frozen: 80,
|
||||
proportion: Perquintill::from_percent(20),
|
||||
index: 2,
|
||||
target: Perquintill::zero(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enlarge_respects_amount_limit_and_will_split() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 80, 1));
|
||||
Gilt::enlarge(40, 2);
|
||||
|
||||
// Takes 2/2, then stopped because it reaches its max amount
|
||||
assert_eq!(Queues::<Test>::get(1), vec![ GiltBid { amount: 40, who: 1 } ]);
|
||||
assert_eq!(QueueTotals::<Test>::get(), vec![(1, 40), (0, 0), (0, 0)]);
|
||||
|
||||
assert_eq!(Active::<Test>::get(0).unwrap(), ActiveGilt {
|
||||
proportion: Perquintill::from_percent(10),
|
||||
amount: 40,
|
||||
who: 1,
|
||||
expiry: 4,
|
||||
});
|
||||
assert_eq!(ActiveTotal::<Test>::get(), ActiveGiltsTotal {
|
||||
frozen: 40,
|
||||
proportion: Perquintill::from_percent(10),
|
||||
index: 1,
|
||||
target: Perquintill::zero(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_thaw_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 40, 1));
|
||||
Gilt::enlarge(40, 1);
|
||||
run_to_block(3);
|
||||
assert_noop!(Gilt::thaw(Origin::signed(1), 0), Error::<Test>::NotExpired);
|
||||
run_to_block(4);
|
||||
assert_noop!(Gilt::thaw(Origin::signed(1), 1), Error::<Test>::Unknown);
|
||||
assert_noop!(Gilt::thaw(Origin::signed(2), 0), Error::<Test>::NotOwner);
|
||||
assert_ok!(Gilt::thaw(Origin::signed(1), 0));
|
||||
|
||||
assert_eq!(ActiveTotal::<Test>::get(), ActiveGiltsTotal {
|
||||
frozen: 0,
|
||||
proportion: Perquintill::zero(),
|
||||
index: 1,
|
||||
target: Perquintill::zero(),
|
||||
});
|
||||
assert_eq!(Active::<Test>::get(0), None);
|
||||
assert_eq!(Balances::free_balance(1), 100);
|
||||
assert_eq!(Balances::reserved_balance(1), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn thaw_when_issuance_higher_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 100, 1));
|
||||
Gilt::enlarge(100, 1);
|
||||
|
||||
// Everybody else's balances goes up by 50%
|
||||
Balances::make_free_balance_be(&2, 150);
|
||||
Balances::make_free_balance_be(&3, 150);
|
||||
Balances::make_free_balance_be(&4, 150);
|
||||
|
||||
run_to_block(4);
|
||||
assert_ok!(Gilt::thaw(Origin::signed(1), 0));
|
||||
|
||||
assert_eq!(Balances::free_balance(1), 150);
|
||||
assert_eq!(Balances::reserved_balance(1), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn thaw_when_issuance_lower_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 100, 1));
|
||||
Gilt::enlarge(100, 1);
|
||||
|
||||
// Everybody else's balances goes down by 25%
|
||||
Balances::make_free_balance_be(&2, 75);
|
||||
Balances::make_free_balance_be(&3, 75);
|
||||
Balances::make_free_balance_be(&4, 75);
|
||||
|
||||
run_to_block(4);
|
||||
assert_ok!(Gilt::thaw(Origin::signed(1), 0));
|
||||
|
||||
assert_eq!(Balances::free_balance(1), 75);
|
||||
assert_eq!(Balances::reserved_balance(1), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_thaws_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 40, 1));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 60, 1));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(2), 50, 1));
|
||||
Gilt::enlarge(200, 3);
|
||||
|
||||
// Double everyone's free balances.
|
||||
Balances::make_free_balance_be(&2, 100);
|
||||
Balances::make_free_balance_be(&3, 200);
|
||||
Balances::make_free_balance_be(&4, 200);
|
||||
|
||||
run_to_block(4);
|
||||
assert_ok!(Gilt::thaw(Origin::signed(1), 0));
|
||||
assert_ok!(Gilt::thaw(Origin::signed(1), 1));
|
||||
assert_ok!(Gilt::thaw(Origin::signed(2), 2));
|
||||
|
||||
assert_eq!(Balances::free_balance(1), 200);
|
||||
assert_eq!(Balances::free_balance(2), 200);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_thaws_works_in_alternative_thaw_order() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(1);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 40, 1));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 60, 1));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(2), 50, 1));
|
||||
Gilt::enlarge(200, 3);
|
||||
|
||||
// Double everyone's free balances.
|
||||
Balances::make_free_balance_be(&2, 100);
|
||||
Balances::make_free_balance_be(&3, 200);
|
||||
Balances::make_free_balance_be(&4, 200);
|
||||
|
||||
run_to_block(4);
|
||||
assert_ok!(Gilt::thaw(Origin::signed(2), 2));
|
||||
assert_ok!(Gilt::thaw(Origin::signed(1), 1));
|
||||
assert_ok!(Gilt::thaw(Origin::signed(1), 0));
|
||||
|
||||
assert_eq!(Balances::free_balance(1), 200);
|
||||
assert_eq!(Balances::free_balance(2), 200);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enlargement_to_target_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
run_to_block(2);
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 40, 1));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(1), 40, 2));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(2), 40, 2));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(2), 40, 3));
|
||||
assert_ok!(Gilt::place_bid(Origin::signed(3), 40, 3));
|
||||
assert_ok!(Gilt::set_target(Origin::signed(1), Perquintill::from_percent(40)));
|
||||
|
||||
run_to_block(3);
|
||||
assert_eq!(Queues::<Test>::get(1), vec![
|
||||
GiltBid { amount: 40, who: 1 },
|
||||
]);
|
||||
assert_eq!(Queues::<Test>::get(2), vec![
|
||||
GiltBid { amount: 40, who: 2 },
|
||||
GiltBid { amount: 40, who: 1 },
|
||||
]);
|
||||
assert_eq!(Queues::<Test>::get(3), vec![
|
||||
GiltBid { amount: 40, who: 3 },
|
||||
GiltBid { amount: 40, who: 2 },
|
||||
]);
|
||||
assert_eq!(QueueTotals::<Test>::get(), vec![(1, 40), (2, 80), (2, 80)]);
|
||||
|
||||
run_to_block(4);
|
||||
// Two new gilts should have been issued to 2 & 3 for 40 each & duration of 3.
|
||||
assert_eq!(Active::<Test>::get(0).unwrap(), ActiveGilt {
|
||||
proportion: Perquintill::from_percent(10),
|
||||
amount: 40,
|
||||
who: 2,
|
||||
expiry: 13,
|
||||
});
|
||||
assert_eq!(Active::<Test>::get(1).unwrap(), ActiveGilt {
|
||||
proportion: Perquintill::from_percent(10),
|
||||
amount: 40,
|
||||
who: 3,
|
||||
expiry: 13,
|
||||
|
||||
});
|
||||
assert_eq!(ActiveTotal::<Test>::get(), ActiveGiltsTotal {
|
||||
frozen: 80,
|
||||
proportion: Perquintill::from_percent(20),
|
||||
index: 2,
|
||||
target: Perquintill::from_percent(40),
|
||||
});
|
||||
|
||||
run_to_block(5);
|
||||
// No change
|
||||
assert_eq!(ActiveTotal::<Test>::get(), ActiveGiltsTotal {
|
||||
frozen: 80,
|
||||
proportion: Perquintill::from_percent(20),
|
||||
index: 2,
|
||||
target: Perquintill::from_percent(40),
|
||||
});
|
||||
|
||||
run_to_block(6);
|
||||
// Two new gilts should have been issued to 1 & 2 for 40 each & duration of 2.
|
||||
assert_eq!(Active::<Test>::get(2).unwrap(), ActiveGilt {
|
||||
proportion: Perquintill::from_percent(10),
|
||||
amount: 40,
|
||||
who: 1,
|
||||
expiry: 12,
|
||||
});
|
||||
assert_eq!(Active::<Test>::get(3).unwrap(), ActiveGilt {
|
||||
proportion: Perquintill::from_percent(10),
|
||||
amount: 40,
|
||||
who: 2,
|
||||
expiry: 12,
|
||||
|
||||
});
|
||||
assert_eq!(ActiveTotal::<Test>::get(), ActiveGiltsTotal {
|
||||
frozen: 160,
|
||||
proportion: Perquintill::from_percent(40),
|
||||
index: 4,
|
||||
target: Perquintill::from_percent(40),
|
||||
});
|
||||
|
||||
run_to_block(8);
|
||||
// No change now.
|
||||
assert_eq!(ActiveTotal::<Test>::get(), ActiveGiltsTotal {
|
||||
frozen: 160,
|
||||
proportion: Perquintill::from_percent(40),
|
||||
index: 4,
|
||||
target: Perquintill::from_percent(40),
|
||||
});
|
||||
|
||||
// Set target a bit higher to use up the remaining bid.
|
||||
assert_ok!(Gilt::set_target(Origin::signed(1), Perquintill::from_percent(60)));
|
||||
run_to_block(10);
|
||||
|
||||
// Two new gilts should have been issued to 1 & 2 for 40 each & duration of 2.
|
||||
assert_eq!(Active::<Test>::get(4).unwrap(), ActiveGilt {
|
||||
proportion: Perquintill::from_percent(10),
|
||||
amount: 40,
|
||||
who: 1,
|
||||
expiry: 13,
|
||||
});
|
||||
|
||||
assert_eq!(ActiveTotal::<Test>::get(), ActiveGiltsTotal {
|
||||
frozen: 200,
|
||||
proportion: Perquintill::from_percent(50),
|
||||
index: 5,
|
||||
target: Perquintill::from_percent(60),
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2021 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Autogenerated weights for pallet_gilt
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0
|
||||
//! DATE: 2021-02-23, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: []
|
||||
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128
|
||||
|
||||
// Executed Command:
|
||||
// target/release/substrate
|
||||
// benchmark
|
||||
// --chain=dev
|
||||
// --steps=50
|
||||
// --repeat=20
|
||||
// --pallet=pallet_gilt
|
||||
// --extrinsic=*
|
||||
// --execution=wasm
|
||||
// --wasm-execution=compiled
|
||||
// --heap-pages=4096
|
||||
// --output=./frame/gilt/src/weights.rs
|
||||
// --template=./.maintain/frame-weight-template.hbs
|
||||
|
||||
|
||||
#![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_gilt.
|
||||
pub trait WeightInfo {
|
||||
fn place_bid(l: u32, ) -> Weight;
|
||||
fn place_bid_max() -> Weight;
|
||||
fn retract_bid(l: u32, ) -> Weight;
|
||||
fn set_target() -> Weight;
|
||||
fn thaw() -> Weight;
|
||||
fn pursue_target_noop() -> Weight;
|
||||
fn pursue_target_per_item(b: u32, ) -> Weight;
|
||||
fn pursue_target_per_queue(q: u32, ) -> Weight;
|
||||
}
|
||||
|
||||
/// Weights for pallet_gilt using the Substrate node and recommended hardware.
|
||||
pub struct SubstrateWeight<T>(PhantomData<T>);
|
||||
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
fn place_bid(l: u32, ) -> Weight {
|
||||
(79_274_000 as Weight)
|
||||
// Standard Error: 0
|
||||
.saturating_add((289_000 as Weight).saturating_mul(l as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
fn place_bid_max() -> Weight {
|
||||
(297_825_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
fn retract_bid(l: u32, ) -> Weight {
|
||||
(79_731_000 as Weight)
|
||||
// Standard Error: 0
|
||||
.saturating_add((231_000 as Weight).saturating_mul(l as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
fn set_target() -> Weight {
|
||||
(6_113_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
fn thaw() -> Weight {
|
||||
(74_792_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
fn pursue_target_noop() -> Weight {
|
||||
(3_468_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
}
|
||||
fn pursue_target_per_item(b: u32, ) -> Weight {
|
||||
(65_792_000 as Weight)
|
||||
// Standard Error: 2_000
|
||||
.saturating_add((11_402_000 as Weight).saturating_mul(b as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(3 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(3 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(b as Weight)))
|
||||
}
|
||||
fn pursue_target_per_queue(q: u32, ) -> Weight {
|
||||
(32_391_000 as Weight)
|
||||
// Standard Error: 7_000
|
||||
.saturating_add((18_500_000 as Weight).saturating_mul(q as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(q as Weight)))
|
||||
.saturating_add(T::DbWeight::get().writes(2 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(q as Weight)))
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests
|
||||
impl WeightInfo for () {
|
||||
fn place_bid(l: u32, ) -> Weight {
|
||||
(79_274_000 as Weight)
|
||||
// Standard Error: 0
|
||||
.saturating_add((289_000 as Weight).saturating_mul(l as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
fn place_bid_max() -> Weight {
|
||||
(297_825_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
fn retract_bid(l: u32, ) -> Weight {
|
||||
(79_731_000 as Weight)
|
||||
// Standard Error: 0
|
||||
.saturating_add((231_000 as Weight).saturating_mul(l as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
fn set_target() -> Weight {
|
||||
(6_113_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
fn thaw() -> Weight {
|
||||
(74_792_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
|
||||
}
|
||||
fn pursue_target_noop() -> Weight {
|
||||
(3_468_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
}
|
||||
fn pursue_target_per_item(b: u32, ) -> Weight {
|
||||
(65_792_000 as Weight)
|
||||
// Standard Error: 2_000
|
||||
.saturating_add((11_402_000 as Weight).saturating_mul(b as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(b as Weight)))
|
||||
}
|
||||
fn pursue_target_per_queue(q: u32, ) -> Weight {
|
||||
(32_391_000 as Weight)
|
||||
// Standard Error: 7_000
|
||||
.saturating_add((18_500_000 as Weight).saturating_mul(q as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(q as Weight)))
|
||||
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(q as Weight)))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user