From 18f52d2d034d086b20241cadac6cedf29736cb1a Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 17 Nov 2023 12:00:46 +0100 Subject: [PATCH 01/90] initial referral pallet structure --- Cargo.lock | 18 ++++++++++++++++++ Cargo.toml | 2 ++ 2 files changed, 20 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 797017b51..fd9b6e3e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7799,6 +7799,24 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-referrals" +version = "1.0.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "hydradx-traits", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-tracing", +] + [[package]] name = "pallet-relaychain-info" version = "0.3.3" diff --git a/Cargo.toml b/Cargo.toml index 1f82b1d07..fed18acff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ members = [ 'pallets/staking', 'pallets/democracy', 'runtime/hydradx/src/evm/evm-utility/macro', + 'pallets/referrals', ] [workspace.dependencies] @@ -76,6 +77,7 @@ warehouse-liquidity-mining = { package = "pallet-liquidity-mining", path = "pall pallet-bonds = { path = "pallets/bonds", default-features = false} pallet-lbp = { path = "pallets/lbp", default-features = false} pallet-xyk = { path = "pallets/xyk", default-features = false} +pallet-referrals = { path = "pallets/referrals", default-features = false} hydra-dx-build-script-utils = { path = "utils/build-script-utils", default-features = false } scraper = { path = "scraper", default-features = false } From e32dfb0b8ba0b9fe5d762365fbfd82ba9d91012e Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 17 Nov 2023 12:00:50 +0100 Subject: [PATCH 02/90] initial referral pallet structure --- pallets/referrals/Cargo.toml | 58 ++++++++++++++++++++++++++++++ pallets/referrals/README.md | 3 ++ pallets/referrals/src/lib.rs | 68 ++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 pallets/referrals/Cargo.toml create mode 100644 pallets/referrals/README.md create mode 100644 pallets/referrals/src/lib.rs diff --git a/pallets/referrals/Cargo.toml b/pallets/referrals/Cargo.toml new file mode 100644 index 000000000..7336dd6c7 --- /dev/null +++ b/pallets/referrals/Cargo.toml @@ -0,0 +1,58 @@ +[package] +name = "pallet-referrals" +version = "1.0.0" +authors = ['GalacticCouncil'] +edition = "2021" +license = "Apache-2.0" +homepage = '/~https://github.com/galacticcouncil/hydradx-node' +repository = '/~https://github.com/galacticcouncil/hydradx-node' +description = "HydraDX Bonds pallet" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +# parity +scale-info = { version = "2.3.1", default-features = false, features = ["derive"] } +codec = { default-features = false, features = ["derive"], package = "parity-scale-codec", version = "3.4.0" } + +# HydraDX +hydradx-traits = { workspace = true } + +# primitives +sp-runtime = { workspace = true } +sp-std = { workspace = true } +sp-core = { workspace = true } + +# FRAME +frame-support = { workspace = true } +frame-system = { workspace = true } + +# Optional imports for benchmarking +frame-benchmarking = { workspace = true, optional = true } +sp-io = { workspace = true, optional = true } +pallet-timestamp = { workspace = true, optional = true } + +[dev-dependencies] +sp-io = { workspace = true } +sp-tracing = { workspace = true } +frame-benchmarking = { workspace = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "sp-runtime/std", + "sp-std/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "sp-io", +] +try-runtime = [ "frame-support/try-runtime" ] diff --git a/pallets/referrals/README.md b/pallets/referrals/README.md new file mode 100644 index 000000000..cc037d0a8 --- /dev/null +++ b/pallets/referrals/README.md @@ -0,0 +1,3 @@ +# Referrals pallet + +## Overview diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs new file mode 100644 index 000000000..6ac9353d1 --- /dev/null +++ b/pallets/referrals/src/lib.rs @@ -0,0 +1,68 @@ +// Copyright (C) 2020-2023 Intergalactic, Limited (GIB). +// 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. + +//! # Referrals pallet +//! + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{ + ensure, + pallet_prelude::{DispatchResult, Get}, + sp_runtime::{ + traits::{AccountIdConversion, AtLeast32BitUnsigned, CheckedAdd, CheckedSub}, + DispatchError, Permill, Saturating, + }, + traits::{Contains, Time}, + PalletId, +}; +use frame_system::{ensure_signed, pallet_prelude::OriginFor}; +use sp_core::MaxEncodedLen; + +use hydradx_traits::{AssetKind, CreateRegistry, Registry}; +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::pallet] + #[pallet::generate_store(pub(crate) trait Store)] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event { + } + + #[pallet::error] + #[cfg_attr(test, derive(PartialEq, Eq))] + pub enum Error { + } + + #[pallet::call] + impl Pallet { + } +} + +impl Pallet { +} From bbcc9a643b7148da5e4468ae92bfaa3293aaf7da Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 17 Nov 2023 14:56:09 +0100 Subject: [PATCH 03/90] add register code with tests --- Cargo.lock | 2 +- pallets/referrals/Cargo.toml | 14 ++- pallets/referrals/src/lib.rs | 89 ++++++++++++--- pallets/referrals/src/tests.rs | 145 ++++++++++++++++++++++++ pallets/referrals/src/tests/register.rs | 116 +++++++++++++++++++ pallets/referrals/src/weights.rs | 68 +++++++++++ 6 files changed, 413 insertions(+), 21 deletions(-) create mode 100644 pallets/referrals/src/tests.rs create mode 100644 pallets/referrals/src/tests/register.rs create mode 100644 pallets/referrals/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index fd9b6e3e7..386c5a4da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7803,12 +7803,12 @@ dependencies = [ name = "pallet-referrals" version = "1.0.0" dependencies = [ - "frame-benchmarking", "frame-support", "frame-system", "hydradx-traits", "pallet-timestamp", "parity-scale-codec", + "pretty_assertions", "scale-info", "sp-core", "sp-io", diff --git a/pallets/referrals/Cargo.toml b/pallets/referrals/Cargo.toml index 7336dd6c7..b40a5002a 100644 --- a/pallets/referrals/Cargo.toml +++ b/pallets/referrals/Cargo.toml @@ -30,14 +30,15 @@ frame-support = { workspace = true } frame-system = { workspace = true } # Optional imports for benchmarking -frame-benchmarking = { workspace = true, optional = true } +#frame-benchmarking = { workspace = true, optional = true } sp-io = { workspace = true, optional = true } pallet-timestamp = { workspace = true, optional = true } [dev-dependencies] sp-io = { workspace = true } sp-tracing = { workspace = true } -frame-benchmarking = { workspace = true } +#frame-benchmarking = { workspace = true } +pretty_assertions = "1.2.1" [features] default = ["std"] @@ -51,8 +52,9 @@ std = [ "sp-core/std", "sp-io/std", ] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "sp-io", -] + +#runtime-benchmarks = [ +# "frame-benchmarking/runtime-benchmarks", +# "sp-io", +#] try-runtime = [ "frame-support/try-runtime" ] diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 6ac9353d1..14f62fe92 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -18,22 +18,23 @@ #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::{ - ensure, - pallet_prelude::{DispatchResult, Get}, - sp_runtime::{ - traits::{AccountIdConversion, AtLeast32BitUnsigned, CheckedAdd, CheckedSub}, - DispatchError, Permill, Saturating, - }, - traits::{Contains, Time}, - PalletId, -}; +mod weights; + +#[cfg(test)] +mod tests; + +use frame_support::pallet_prelude::{DispatchResult, Get}; use frame_system::{ensure_signed, pallet_prelude::OriginFor}; -use sp_core::MaxEncodedLen; +use sp_core::bounded::BoundedVec; -use hydradx_traits::{AssetKind, CreateRegistry, Registry}; pub use pallet::*; +use weights::WeightInfo; + +pub type ReferralCode = BoundedVec; + +const MIN_CODE_LENGTH: usize = 3; + #[frame_support::pallet] pub mod pallet { use super::*; @@ -47,22 +48,82 @@ pub mod pallet { pub trait Config: frame_system::Config { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// Maximuem referrral code length. + type CodeLength: Get; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; } + #[pallet::storage] + /// Referral codes + /// Maps an account to a referral code. + #[pallet::getter(fn referral_account)] + pub(super) type ReferralCodes = + StorageMap<_, Blake2_128Concat, ReferralCode, T::AccountId>; + #[pallet::event] #[pallet::generate_deposit(pub(crate) fn deposit_event)] pub enum Event { + CodeRegistered { + code: ReferralCode, + account: T::AccountId, + }, } #[pallet::error] #[cfg_attr(test, derive(PartialEq, Eq))] pub enum Error { + TooLong, + TooShort, + InvalidCharacter, + AlreadyExists, } #[pallet::call] impl Pallet { + /// Register new referral code. + /// + /// `origin` pays the registration fee. + /// `code` is assigned to the given `account`. + /// + /// Length of the `code` must be at least `MIN_CODE_LENGTH`. + /// Maximum length is limited to `T::CodeLength`. + /// `code` must contain only alfa-numeric characters and all characters will be converted to upper case. + /// + /// /// Parameters: + /// - `origin`: + /// - `code`: + /// - `account`: + /// + /// Emits `CodeRegistered` event when successful. + /// + #[pallet::call_index(0)] + #[pallet::weight(::WeightInfo::register_code())] + pub fn register_code(origin: OriginFor, code: Vec, account: T::AccountId) -> DispatchResult { + let who = ensure_signed(origin)?; + let code: ReferralCode = code.try_into().map_err(|_| Error::::TooLong)?; + + ensure!(code.len() >= MIN_CODE_LENGTH, Error::::TooShort); + + //TODO: can we do without cloning ? + ensure!( + code.clone() + .into_inner() + .iter() + .all(|c| char::is_alphanumeric(*c as char)), + Error::::InvalidCharacter + ); + + ReferralCodes::::mutate(code.clone(), |v| -> DispatchResult { + ensure!(v.is_none(), Error::::AlreadyExists); + *v = Some(account.clone()); + Self::deposit_event(Event::CodeRegistered { code, account }); + Ok(()) + }) + } } } -impl Pallet { -} +impl Pallet {} diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs new file mode 100644 index 000000000..e2ae10ab9 --- /dev/null +++ b/pallets/referrals/src/tests.rs @@ -0,0 +1,145 @@ +// This file is part of HydraDX. + +// Copyright (C) 2020-2023 Intergalactic, Limited (GIB). +// 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. + +mod register; + +use crate as pallet_referrals; +use crate::*; + +use frame_support::{ + construct_runtime, parameter_types, + sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, + }, + traits::{ConstU32, ConstU64, Everything, GenesisBuild, SortedMembers}, +}; +use sp_core::H256; + +use frame_support::{assert_noop, assert_ok}; +//pub(crate) use pretty_assertions::{assert_eq}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +pub(crate) type AccountId = u64; +pub(crate) type Balance = u128; +pub(crate) type AssetId = u32; + +pub const HDX: AssetId = 0; + +pub const ALICE: AccountId = 1; +pub const BOB: AccountId = 2; +pub const TREASURY: AccountId = 400; + +construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + Referrals: pallet_referrals, + } +); + +parameter_types! { + pub const CodeLength: u32 = 7; +} + +impl Config for Test { + type RuntimeEvent = RuntimeEvent; + type CodeLength = CodeLength; + type WeightInfo = (); +} + +impl frame_system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +pub struct ExtBuilder { + endowed_accounts: Vec<(AccountId, AssetId, Balance)>, +} + +impl Default for ExtBuilder { + fn default() -> Self { + Self { + endowed_accounts: vec![(ALICE, HDX, 1_000 * 1_000_000)], + } + } +} + +impl ExtBuilder { + pub fn add_endowed_accounts(mut self, accounts: Vec<(u64, AssetId, Balance)>) -> Self { + for entry in accounts { + self.endowed_accounts.push(entry); + } + self + } + + pub fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + /* + orml_tokens::GenesisConfig:: { + balances: self + .endowed_accounts + .iter() + .flat_map(|(x, asset, amount)| vec![(*x, *asset, *amount)]) + .collect(), + } + .assimilate_storage(&mut t) + .unwrap(); + + */ + + let mut r: sp_io::TestExternalities = t.into(); + + r.execute_with(|| { + System::set_block_number(1); + }); + + r + } +} + +pub fn expect_events(e: Vec) { + e.into_iter().for_each(frame_system::Pallet::::assert_has_event); +} diff --git a/pallets/referrals/src/tests/register.rs b/pallets/referrals/src/tests/register.rs new file mode 100644 index 000000000..d4dcc41bc --- /dev/null +++ b/pallets/referrals/src/tests/register.rs @@ -0,0 +1,116 @@ +use crate::tests::*; +use pretty_assertions::assert_eq; + +#[test] +fn register_code_should_work_when_code_is_max_length() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + BOB + )); + }); +} + +#[test] +fn register_code_should_work_when_code_is_min_length() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"ABC".to_vec(), + BOB + )); + }); +} + +#[test] +fn register_code_should_fail_when_code_is_too_long() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"TOOMANYBALLS69".to_vec(), BOB), + Error::::TooLong + ); + }); +} + +#[test] +fn register_code_should_fail_when_code_is_too_short() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"".to_vec(), BOB), + Error::::TooShort + ); + assert_noop!( + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"A".to_vec(), BOB), + Error::::TooShort + ); + assert_noop!( + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"AB".to_vec(), BOB), + Error::::TooShort + ); + }); +} + +#[test] +fn register_code_should_fail_when_code_already_exists() { + ExtBuilder::default().build().execute_with(|| { + // Arrange + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + BOB + )); + // Act + assert_noop!( + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), BOB), + Error::::AlreadyExists + ); + }); +} + +#[test] +fn register_code_should_fail_when_code_contains_invalid_char() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"ABC?".to_vec(), BOB), + Error::::InvalidCharacter + ); + }); +} + +#[test] +fn register_code_should_store_account_mapping_to_code_correctly() { + ExtBuilder::default().build().execute_with(|| { + // Arrange + let code = b"BALLS69".to_vec(); + // Act + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + code.clone(), + BOB + )); + // Assert + let entry = Pallet::::referral_account::>(code.try_into().unwrap()); + assert_eq!(entry, Some(BOB)); + }); +} + +#[test] +fn register_code_should_emit_event_when_successful() { + ExtBuilder::default().build().execute_with(|| { + // Arrange + let code = b"BALLS69".to_vec(); + // Act + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + code.clone(), + BOB + )); + // Assert + expect_events(vec![Event::CodeRegistered { + code: code.try_into().unwrap(), + account: BOB, + } + .into()]); + }); +} diff --git a/pallets/referrals/src/weights.rs b/pallets/referrals/src/weights.rs new file mode 100644 index 000000000..ef5a762dd --- /dev/null +++ b/pallets/referrals/src/weights.rs @@ -0,0 +1,68 @@ +// This file is part of HydraDX. + +// Copyright (C) 2020-2023 Intergalactic, Limited (GIB). +// 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_bonds +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-08-11, STEPS: 10, REPEAT: 30, LOW RANGE: [], HIGH RANGE: [] +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/hydradx +// benchmark +// pallet +// --chain=dev +// --steps=10 +// --repeat=30 +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --template=.maintain/pallet-weight-template.hbs +// --pallet=pallet-bonds +// --output=bonds.rs +// --extrinsic=* + +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(clippy::unnecessary_cast)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_bonds. +pub trait WeightInfo { + fn register_code() -> Weight; +} + +/// Weights for pallet_bonds using the hydraDX node and recommended hardware. +pub struct HydraWeight(PhantomData); + +impl WeightInfo for HydraWeight { + fn register_code() -> Weight { + Weight::zero() + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn register_code() -> Weight { + Weight::zero() + } +} From 47b6652a59c0699b9cdf0489cce7cf3bbd61e528 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 17 Nov 2023 15:45:05 +0100 Subject: [PATCH 04/90] add registration fee payment --- Cargo.lock | 2 + pallets/referrals/Cargo.toml | 3 ++ pallets/referrals/src/lib.rs | 39 +++++++++++++++++-- pallets/referrals/src/tests.rs | 51 +++++++++++++++++++++---- pallets/referrals/src/tests/register.rs | 38 ++++++++++++++++++ 5 files changed, 123 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 386c5a4da..9dbcebdea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7806,6 +7806,8 @@ dependencies = [ "frame-support", "frame-system", "hydradx-traits", + "orml-tokens", + "orml-traits", "pallet-timestamp", "parity-scale-codec", "pretty_assertions", diff --git a/pallets/referrals/Cargo.toml b/pallets/referrals/Cargo.toml index b40a5002a..1a83fe6ab 100644 --- a/pallets/referrals/Cargo.toml +++ b/pallets/referrals/Cargo.toml @@ -39,6 +39,8 @@ sp-io = { workspace = true } sp-tracing = { workspace = true } #frame-benchmarking = { workspace = true } pretty_assertions = "1.2.1" +orml-tokens = { workspace = true } +orml-traits = { workspace = true } [features] default = ["std"] @@ -51,6 +53,7 @@ std = [ "scale-info/std", "sp-core/std", "sp-io/std", + "orml-tokens/std", ] #runtime-benchmarks = [ diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 14f62fe92..2965bebea 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -26,11 +26,13 @@ mod tests; use frame_support::pallet_prelude::{DispatchResult, Get}; use frame_system::{ensure_signed, pallet_prelude::OriginFor}; use sp_core::bounded::BoundedVec; +use sp_runtime::traits::AccountIdConversion; pub use pallet::*; use weights::WeightInfo; +pub type Balance = u128; pub type ReferralCode = BoundedVec; const MIN_CODE_LENGTH: usize = 3; @@ -39,6 +41,8 @@ const MIN_CODE_LENGTH: usize = 3; pub mod pallet { use super::*; use frame_support::pallet_prelude::*; + use frame_support::traits::fungibles::Transfer; + use frame_support::PalletId; #[pallet::pallet] #[pallet::generate_store(pub(crate) trait Store)] @@ -49,7 +53,21 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Maximuem referrral code length. + type AssetId: frame_support::traits::tokens::AssetId + MaybeSerializeDeserialize; + + type Currency: Transfer; + + /// Pallet id. Determines account which holds accumulated rewards in various assets. + #[pallet::constant] + type PalletId: Get; + + /// Registration fee details. + /// (ID of an asset which fee is to be paid in, Amount, Beneficiary account) + #[pallet::constant] + type RegistrationFee: Get<(Self::AssetId, Balance, Self::AccountId)>; + + /// Maximum referral code length. + #[pallet::constant] type CodeLength: Get; /// Weight information for extrinsics in this pallet. @@ -107,7 +125,7 @@ pub mod pallet { ensure!(code.len() >= MIN_CODE_LENGTH, Error::::TooShort); - //TODO: can we do without cloning ? + //TODO: can we do without cloning ? or perhaps merge with normalization ensure!( code.clone() .into_inner() @@ -116,8 +134,14 @@ pub mod pallet { Error::::InvalidCharacter ); + let code = Self::normalize_code(code); + ReferralCodes::::mutate(code.clone(), |v| -> DispatchResult { ensure!(v.is_none(), Error::::AlreadyExists); + + let (fee_asset, fee_amount, beneficiary) = T::RegistrationFee::get(); + T::Currency::transfer(fee_asset, &who, &beneficiary, fee_amount, true)?; + *v = Some(account.clone()); Self::deposit_event(Event::CodeRegistered { code, account }); Ok(()) @@ -126,4 +150,13 @@ pub mod pallet { } } -impl Pallet {} +impl Pallet { + pub fn pot_account_id() -> T::AccountId { + T::PalletId::get().into_account_truncating() + } + + pub(crate) fn normalize_code(code: ReferralCode) -> ReferralCode { + let r = code.into_inner().iter().map(|v| v.to_ascii_uppercase()).collect(); + ReferralCode::::truncate_from(r) + } +} diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index e2ae10ab9..896d793cd 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -26,26 +26,31 @@ use frame_support::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }, - traits::{ConstU32, ConstU64, Everything, GenesisBuild, SortedMembers}, + traits::{ConstU32, ConstU64, Everything, GenesisBuild}, + PalletId, }; use sp_core::H256; use frame_support::{assert_noop, assert_ok}; -//pub(crate) use pretty_assertions::{assert_eq}; +use orml_traits::parameter_type_with_key; +use orml_traits::MultiCurrency; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; pub(crate) type AccountId = u64; -pub(crate) type Balance = u128; pub(crate) type AssetId = u32; +pub(crate) const ONE: Balance = 1_000_000_000_000; + pub const HDX: AssetId = 0; pub const ALICE: AccountId = 1; pub const BOB: AccountId = 2; pub const TREASURY: AccountId = 400; +pub(crate) const INITIAL_ALICE_BALANCE: Balance = 1_000 * ONE; + construct_runtime!( pub enum Test where Block = Block, @@ -54,15 +59,23 @@ construct_runtime!( { System: frame_system, Referrals: pallet_referrals, + Tokens: orml_tokens, } ); parameter_types! { + pub const RefarralPalletId: PalletId = PalletId(*b"test_ref"); pub const CodeLength: u32 = 7; + pub const RegistrationFee: (AssetId,Balance, AccountId) = (HDX, 222 * 1_000_000_000_000, TREASURY) ; + pub const FeeAsset: AssetId = HDX; } impl Config for Test { type RuntimeEvent = RuntimeEvent; + type AssetId = AssetId; + type Currency = Tokens; + type PalletId = RefarralPalletId; + type RegistrationFee = RegistrationFee; type CodeLength = CodeLength; type WeightInfo = (); } @@ -94,6 +107,26 @@ impl frame_system::Config for Test { type MaxConsumers = ConstU32<16>; } +parameter_type_with_key! { + pub ExistentialDeposits: |_asset_id: AssetId| -> Balance { + 0 + }; +} + +impl orml_tokens::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type Amount = i128; + type CurrencyId = AssetId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type CurrencyHooks = (); + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = (); + type DustRemovalWhitelist = Everything; +} + pub struct ExtBuilder { endowed_accounts: Vec<(AccountId, AssetId, Balance)>, } @@ -101,7 +134,7 @@ pub struct ExtBuilder { impl Default for ExtBuilder { fn default() -> Self { Self { - endowed_accounts: vec![(ALICE, HDX, 1_000 * 1_000_000)], + endowed_accounts: vec![(ALICE, HDX, INITIAL_ALICE_BALANCE)], } } } @@ -117,7 +150,6 @@ impl ExtBuilder { pub fn build(self) -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - /* orml_tokens::GenesisConfig:: { balances: self .endowed_accounts @@ -128,8 +160,6 @@ impl ExtBuilder { .assimilate_storage(&mut t) .unwrap(); - */ - let mut r: sp_io::TestExternalities = t.into(); r.execute_with(|| { @@ -143,3 +173,10 @@ impl ExtBuilder { pub fn expect_events(e: Vec) { e.into_iter().for_each(frame_system::Pallet::::assert_has_event); } + +#[macro_export] +macro_rules! assert_balance { + ( $x:expr, $y:expr, $z:expr) => {{ + assert_eq!(Tokens::free_balance($y, &$x), $z); + }}; +} diff --git a/pallets/referrals/src/tests/register.rs b/pallets/referrals/src/tests/register.rs index d4dcc41bc..eebe21c0b 100644 --- a/pallets/referrals/src/tests/register.rs +++ b/pallets/referrals/src/tests/register.rs @@ -95,6 +95,26 @@ fn register_code_should_store_account_mapping_to_code_correctly() { }); } +#[test] +fn register_code_should_convert_to_upper_case_when_code_is_lower_case() { + ExtBuilder::default().build().execute_with(|| { + // Arrange + let code = b"balls69".to_vec(); + // Act + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + code.clone(), + BOB + )); + // Assert + let entry = Pallet::::referral_account::>(code.clone().try_into().unwrap()); + assert_eq!(entry, None); + let normalized = Pallet::::normalize_code(code.try_into().unwrap()); + let entry = Pallet::::referral_account::>(normalized); + assert_eq!(entry, Some(BOB)); + }); +} + #[test] fn register_code_should_emit_event_when_successful() { ExtBuilder::default().build().execute_with(|| { @@ -114,3 +134,21 @@ fn register_code_should_emit_event_when_successful() { .into()]); }); } + +#[test] +fn singer_should_pay_the_registration_fee() { + ExtBuilder::default().build().execute_with(|| { + // Arrange + let code = b"BALLS69".to_vec(); + // Act + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + code.clone(), + BOB + )); + // Assert + let (fee_asset, amount, beneficiary) = RegistrationFee::get(); + assert_balance!(ALICE, fee_asset, INITIAL_ALICE_BALANCE - amount); + assert_balance!(beneficiary, fee_asset, amount); + }); +} From f6f9350a5786a8b2977d86fe74e1963e60877db5 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 17 Nov 2023 15:48:18 +0100 Subject: [PATCH 05/90] Add test to check existence if lowercase --- pallets/referrals/src/lib.rs | 2 ++ pallets/referrals/src/tests/register.rs | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 2965bebea..d031963ae 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -53,8 +53,10 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Asset type type AssetId: frame_support::traits::tokens::AssetId + MaybeSerializeDeserialize; + /// Support for transfers. type Currency: Transfer; /// Pallet id. Determines account which holds accumulated rewards in various assets. diff --git a/pallets/referrals/src/tests/register.rs b/pallets/referrals/src/tests/register.rs index eebe21c0b..63bb343e5 100644 --- a/pallets/referrals/src/tests/register.rs +++ b/pallets/referrals/src/tests/register.rs @@ -68,6 +68,23 @@ fn register_code_should_fail_when_code_already_exists() { }); } +#[test] +fn register_code_should_fail_when_code_is_lowercase_and_already_exists() { + ExtBuilder::default().build().execute_with(|| { + // Arrange + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + BOB + )); + // Act + assert_noop!( + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"balls69".to_vec(), BOB), + Error::::AlreadyExists + ); + }); +} + #[test] fn register_code_should_fail_when_code_contains_invalid_char() { ExtBuilder::default().build().execute_with(|| { From 26c39a600fcd2e8eab42a1bc7ee91f91245a1ca7 Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 18 Nov 2023 08:36:31 +0100 Subject: [PATCH 06/90] add todoreminder --- pallets/referrals/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index d031963ae..1c8a7bf48 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -153,6 +153,7 @@ pub mod pallet { } impl Pallet { + //TODO: when added to runtime, make sure the account is added to the whitelist of account that cannot be dusted pub fn pot_account_id() -> T::AccountId { T::PalletId::get().into_account_truncating() } From 753745b87cef6f0f06cef4d93771fce19cf9c513 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 20 Nov 2023 11:52:48 +0100 Subject: [PATCH 07/90] add link account and testS --- pallets/referrals/src/lib.rs | 43 ++++++++++- pallets/referrals/src/tests.rs | 1 + pallets/referrals/src/tests/link.rs | 115 ++++++++++++++++++++++++++++ pallets/referrals/src/weights.rs | 9 +++ 4 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 pallets/referrals/src/tests/link.rs diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 1c8a7bf48..497691df4 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -83,6 +83,12 @@ pub mod pallet { pub(super) type ReferralCodes = StorageMap<_, Blake2_128Concat, ReferralCode, T::AccountId>; + #[pallet::storage] + /// Linked accounts. + /// Maps an account to a referral account. + #[pallet::getter(fn linked_referral_account)] + pub(super) type LinkedAccounts = StorageMap<_, Blake2_128Concat, T::AccountId, T::AccountId>; + #[pallet::event] #[pallet::generate_deposit(pub(crate) fn deposit_event)] pub enum Event { @@ -90,6 +96,11 @@ pub mod pallet { code: ReferralCode, account: T::AccountId, }, + CodeLinked { + account: T::AccountId, + code: ReferralCode, + referral_account: T::AccountId, + }, } #[pallet::error] @@ -99,6 +110,8 @@ pub mod pallet { TooShort, InvalidCharacter, AlreadyExists, + InvalidCode, + AlreadyLinked, } #[pallet::call] @@ -118,7 +131,6 @@ pub mod pallet { /// - `account`: /// /// Emits `CodeRegistered` event when successful. - /// #[pallet::call_index(0)] #[pallet::weight(::WeightInfo::register_code())] pub fn register_code(origin: OriginFor, code: Vec, account: T::AccountId) -> DispatchResult { @@ -149,6 +161,35 @@ pub mod pallet { Ok(()) }) } + + /// Link a code to an account. + /// + /// /// Parameters: + /// - `origin`: + /// - `code`: + /// + /// Emits `CodeLinked` event when successful. + #[pallet::call_index(1)] + #[pallet::weight(::WeightInfo::link_code())] + pub fn link_code(origin: OriginFor, code: Vec) -> DispatchResult { + let who = ensure_signed(origin)?; + let code: ReferralCode = code.try_into().map_err(|_| Error::::InvalidCode)?; + let code = Self::normalize_code(code); + let ref_account = Self::referral_account(&code).ok_or(Error::::InvalidCode)?; + + LinkedAccounts::::mutate(who.clone(), |v| -> DispatchResult { + ensure!(v.is_none(), Error::::AlreadyLinked); + + *v = Some(ref_account.clone()); + Self::deposit_event(Event::CodeLinked { + account: who, + code, + referral_account: ref_account, + }); + Ok(()) + })?; + Ok(()) + } } } diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 896d793cd..027d973bb 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -15,6 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod link; mod register; use crate as pallet_referrals; diff --git a/pallets/referrals/src/tests/link.rs b/pallets/referrals/src/tests/link.rs new file mode 100644 index 000000000..90472194e --- /dev/null +++ b/pallets/referrals/src/tests/link.rs @@ -0,0 +1,115 @@ +use crate::tests::*; +use pretty_assertions::assert_eq; + +#[test] +fn link_code_should_work_when_code_is_valid() { + ExtBuilder::default().build().execute_with(|| { + // Arrange + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE, + )); + // ACT + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec())); + }); +} + +#[test] +fn link_code_should_fail_when_code_is_too_long() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Referrals::link_code(RuntimeOrigin::signed(ALICE), b"TOOMANYBALLS69".to_vec(),), + Error::::InvalidCode + ); + }); +} + +#[test] +fn link_code_should_fail_when_code_does_not_exist() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Referrals::link_code(RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(),), + Error::::InvalidCode + ); + }); +} + +#[test] +fn link_code_should_link_correctly_when_code_is_valid() { + ExtBuilder::default().build().execute_with(|| { + // ARRANGE + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE + )); + + // ACT + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + + // ASSERT + let entry = Pallet::::linked_referral_account::(BOB); + assert_eq!(entry, Some(ALICE)); + }); +} + +#[test] +fn link_code_should_link_correctly_when_code_is_lowercase() { + ExtBuilder::default().build().execute_with(|| { + // ARRANGE + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE + )); + + // ACT + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"balls69".to_vec())); + + // ASSERT + let entry = Pallet::::linked_referral_account::(BOB); + assert_eq!(entry, Some(ALICE)); + }); +} + +#[test] +fn link_code_should_fail_when_account_is_already_linked() { + ExtBuilder::default().build().execute_with(|| { + // ARRANGE + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + + // ACT + assert_noop!( + Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec()), + Error::::AlreadyLinked + ); + }); +} + +#[test] +fn link_code_should_emit_event_when_successful() { + ExtBuilder::default().build().execute_with(|| { + //ARRANGE + let code = b"BALLS69".to_vec(); + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + code.clone(), + ALICE + )); + // ACT + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code.clone())); + // ASSERT + expect_events(vec![Event::CodeLinked { + account: BOB, + code: code.try_into().unwrap(), + referral_account: ALICE, + } + .into()]); + }); +} diff --git a/pallets/referrals/src/weights.rs b/pallets/referrals/src/weights.rs index ef5a762dd..7afdd9fd8 100644 --- a/pallets/referrals/src/weights.rs +++ b/pallets/referrals/src/weights.rs @@ -49,6 +49,7 @@ use sp_std::marker::PhantomData; /// Weight functions needed for pallet_bonds. pub trait WeightInfo { fn register_code() -> Weight; + fn link_code() -> Weight; } /// Weights for pallet_bonds using the hydraDX node and recommended hardware. @@ -58,6 +59,10 @@ impl WeightInfo for HydraWeight { fn register_code() -> Weight { Weight::zero() } + + fn link_code() -> Weight { + Weight::zero() + } } // For backwards compatibility and tests @@ -65,4 +70,8 @@ impl WeightInfo for () { fn register_code() -> Weight { Weight::zero() } + + fn link_code() -> Weight { + Weight::zero() + } } From 55e7b036934ae926aef495f303c6bf65d2070651 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 20 Nov 2023 17:44:05 +0100 Subject: [PATCH 08/90] Rewards storage --- pallets/referrals/src/lib.rs | 24 ++++++++++++++++++++++-- pallets/referrals/src/weights.rs | 9 +++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 497691df4..961d4bf49 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -76,19 +76,26 @@ pub mod pallet { type WeightInfo: WeightInfo; } - #[pallet::storage] /// Referral codes /// Maps an account to a referral code. + #[pallet::storage] #[pallet::getter(fn referral_account)] pub(super) type ReferralCodes = StorageMap<_, Blake2_128Concat, ReferralCode, T::AccountId>; - #[pallet::storage] /// Linked accounts. /// Maps an account to a referral account. + #[pallet::storage] #[pallet::getter(fn linked_referral_account)] pub(super) type LinkedAccounts = StorageMap<_, Blake2_128Concat, T::AccountId, T::AccountId>; + /// Accumulated rewards + /// List of accumulated rewards per asset. + #[pallet::storage] + #[pallet::getter(fn account_rewards)] + pub(super) type Rewards = StorageDoubleMap<_, Blake2_128Concat, T::AccountId, Blake2_128Concat, T::AssetId, Balance, ValueQuery>; + + #[pallet::event] #[pallet::generate_deposit(pub(crate) fn deposit_event)] pub enum Event { @@ -190,6 +197,19 @@ pub mod pallet { })?; Ok(()) } + + /// Claim accumulated rewards + /// + /// /// Parameters: + /// - `origin`: + /// + /// Emits `CodeLinked` event when successful. + #[pallet::call_index(2)] + #[pallet::weight(::WeightInfo::claim_rewards())] + pub fn claim_rewards(origin: OriginFor, code: Vec) -> DispatchResult { + let who = ensure_signed(origin)?; + Ok(()) + } } } diff --git a/pallets/referrals/src/weights.rs b/pallets/referrals/src/weights.rs index 7afdd9fd8..118ad8842 100644 --- a/pallets/referrals/src/weights.rs +++ b/pallets/referrals/src/weights.rs @@ -50,6 +50,7 @@ use sp_std::marker::PhantomData; pub trait WeightInfo { fn register_code() -> Weight; fn link_code() -> Weight; + fn claim_rewards() -> Weight; } /// Weights for pallet_bonds using the hydraDX node and recommended hardware. @@ -63,6 +64,10 @@ impl WeightInfo for HydraWeight { fn link_code() -> Weight { Weight::zero() } + + fn claim_rewards() -> Weight { + Weight::zero() + } } // For backwards compatibility and tests @@ -74,4 +79,8 @@ impl WeightInfo for () { fn link_code() -> Weight { Weight::zero() } + + fn claim_rewards() -> Weight { + Weight::zero() + } } From 49956895b58396b3b137ec2853b6c619ca010e2c Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 21 Nov 2023 12:17:42 +0100 Subject: [PATCH 09/90] add convert initial stub and tests --- pallets/referrals/src/lib.rs | 33 +++++-- pallets/referrals/src/tests.rs | 39 ++++++-- pallets/referrals/src/tests/convert.rs | 118 +++++++++++++++++++++++++ pallets/referrals/src/weights.rs | 9 ++ 4 files changed, 188 insertions(+), 11 deletions(-) create mode 100644 pallets/referrals/src/tests/convert.rs diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 961d4bf49..eb1ac191d 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -89,12 +89,18 @@ pub mod pallet { #[pallet::getter(fn linked_referral_account)] pub(super) type LinkedAccounts = StorageMap<_, Blake2_128Concat, T::AccountId, T::AccountId>; + /// Accrued amounts of an asset from trading activity. + /// Maps an amount to asset and account. This amount needs to be converted to native currency prior to claiming as a reward. + #[pallet::storage] + #[pallet::getter(fn accrued)] + pub(super) type Accrued = + StorageDoubleMap<_, Blake2_128Concat, T::AssetId, Blake2_128Concat, T::AccountId, Balance, ValueQuery>; + /// Accumulated rewards - /// List of accumulated rewards per asset. + /// Reward amount of native asset per account. #[pallet::storage] #[pallet::getter(fn account_rewards)] - pub(super) type Rewards = StorageDoubleMap<_, Blake2_128Concat, T::AccountId, Blake2_128Concat, T::AssetId, Balance, ValueQuery>; - + pub(super) type Rewards = StorageMap<_, Blake2_128Concat, T::AccountId, Balance, ValueQuery>; #[pallet::event] #[pallet::generate_deposit(pub(crate) fn deposit_event)] @@ -119,6 +125,7 @@ pub mod pallet { AlreadyExists, InvalidCode, AlreadyLinked, + ZeroAmount, } #[pallet::call] @@ -198,15 +205,29 @@ pub mod pallet { Ok(()) } - /// Claim accumulated rewards + /// Convert accrued asset amount to native currency. /// /// /// Parameters: /// - `origin`: + /// - `asset_id`: /// - /// Emits `CodeLinked` event when successful. + /// Emits `Converted` event when successful. #[pallet::call_index(2)] + #[pallet::weight(::WeightInfo::convert())] + pub fn convert(origin: OriginFor, asset_id: T::AssetId) -> DispatchResult { + let who = ensure_signed(origin)?; + Ok(()) + } + + /// Claim accumulated rewards + /// + /// /// Parameters: + /// - `origin`: + /// + /// Emits `Claimed` event when successful. + #[pallet::call_index(3)] #[pallet::weight(::WeightInfo::claim_rewards())] - pub fn claim_rewards(origin: OriginFor, code: Vec) -> DispatchResult { + pub fn claim_rewards(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; Ok(()) } diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 027d973bb..81b8ad46f 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -15,12 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod convert; mod link; mod register; use crate as pallet_referrals; use crate::*; +use std::cell::RefCell; +use std::collections::HashMap; + use frame_support::{ construct_runtime, parameter_types, sp_runtime::{ @@ -33,8 +37,9 @@ use frame_support::{ use sp_core::H256; use frame_support::{assert_noop, assert_ok}; -use orml_traits::parameter_type_with_key; use orml_traits::MultiCurrency; +use orml_traits::{parameter_type_with_key, MultiCurrencyExtended}; +use sp_runtime::FixedU128; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -45,13 +50,19 @@ pub(crate) type AssetId = u32; pub(crate) const ONE: Balance = 1_000_000_000_000; pub const HDX: AssetId = 0; +pub const DAI: AssetId = 2; pub const ALICE: AccountId = 1; pub const BOB: AccountId = 2; +pub const CHARLIE: AccountId = 3; pub const TREASURY: AccountId = 400; pub(crate) const INITIAL_ALICE_BALANCE: Balance = 1_000 * ONE; +thread_local! { + pub static CONVERSION_RATE: RefCell> = RefCell::new(HashMap::default()); +} + construct_runtime!( pub enum Test where Block = Block, @@ -130,21 +141,32 @@ impl orml_tokens::Config for Test { pub struct ExtBuilder { endowed_accounts: Vec<(AccountId, AssetId, Balance)>, + trades: Vec<(AccountId, AssetId, Balance)>, } impl Default for ExtBuilder { fn default() -> Self { + CONVERSION_RATE.with(|v| { + v.borrow_mut().clear(); + }); Self { endowed_accounts: vec![(ALICE, HDX, INITIAL_ALICE_BALANCE)], + trades: vec![], } } } impl ExtBuilder { - pub fn add_endowed_accounts(mut self, accounts: Vec<(u64, AssetId, Balance)>) -> Self { - for entry in accounts { - self.endowed_accounts.push(entry); - } + pub fn with_trade_activity(mut self, trades: Vec<(AccountId, AssetId, Balance)>) -> Self { + self.trades.extend(trades); + self + } + + pub fn with_conversion_price(self, pair: (AssetId, AssetId), price: FixedU128) -> Self { + CONVERSION_RATE.with(|v| { + let mut m = v.borrow_mut(); + m.insert(pair, price); + }); self } @@ -163,6 +185,13 @@ impl ExtBuilder { let mut r: sp_io::TestExternalities = t.into(); + r.execute_with(|| { + for (acc, asset, amount) in self.trades.iter() { + Accrued::::insert(asset, acc, amount); + Tokens::update_balance(*asset, &Pallet::::pot_account_id(), *amount as i128).unwrap(); + } + }); + r.execute_with(|| { System::set_block_number(1); }); diff --git a/pallets/referrals/src/tests/convert.rs b/pallets/referrals/src/tests/convert.rs new file mode 100644 index 000000000..491c9643b --- /dev/null +++ b/pallets/referrals/src/tests/convert.rs @@ -0,0 +1,118 @@ +use crate::tests::*; +use pretty_assertions::assert_eq; + +#[test] +fn convert_should_fail_when_amount_is_zero() { + ExtBuilder::default().build().execute_with(|| { + // Arrange + assert_noop!( + Referrals::convert(RuntimeOrigin::signed(ALICE), DAI), + Error::::ZeroAmount + ); + }); +} + +#[test] +fn convert_should_convert_all_entries() { + ExtBuilder::default() + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_trade_activity(vec![(BOB, DAI, 1_000_000_000_000_000_000)]) + .build() + .execute_with(|| { + // Arrange + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI,)); + // Assert + let length = Accrued::::iter().count(); + assert_eq!(length, 0); + }); +} + +#[test] +fn convert_should_not_have_converted_asset_when_successful() { + ExtBuilder::default() + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_trade_activity(vec![(BOB, DAI, 1_000_000_000_000_000_000)]) + .build() + .execute_with(|| { + // Arrange + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI,)); + // Assert + let balance = Tokens::free_balance(DAI, &Pallet::::pot_account_id()); + assert_eq!(balance, 0); + }); +} + +#[test] +fn convert_should_update_account_rewards() { + ExtBuilder::default() + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_trade_activity(vec![(BOB, DAI, 1_000_000_000_000_000_000)]) + .build() + .execute_with(|| { + // Arrange + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI,)); + // Assert + let rewards = Rewards::::get(&BOB); + assert_eq!(rewards, 1_000_000_000_000); + }); +} + +#[test] +fn convert_should_distribute_native_amount_correct_when_there_is_multiple_entries() { + ExtBuilder::default() + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_trade_activity(vec![ + (BOB, DAI, 1_000_000_000_000_000_000), + (CHARLIE, DAI, 2_000_000_000_000_000_000), + ]) + .build() + .execute_with(|| { + // Arrange + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI,)); + // Assert + let rewards = Rewards::::get(&BOB); + assert_eq!(rewards, 1_000_000_000_000); + + let rewards = Rewards::::get(&CHARLIE); + assert_eq!(rewards, 2_000_000_000_000); + }); +} + +#[test] +fn convert_should_transfer_leftovers_to_registration_fee_beneficiary() { + ExtBuilder::default() + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_333_333_333_333, 1_333_333_333_333_333_333), + ) + .with_trade_activity(vec![ + (BOB, DAI, 1_333_333_333_333_333_333), + (CHARLIE, DAI, 2_333_333_333_333_333_333), + ]) + .build() + .execute_with(|| { + // Arrange + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI,)); + // Assert + let rewards = Rewards::::get(&BOB); + assert_eq!(rewards, 1_000_000_000_000); + + let rewards = Rewards::::get(&CHARLIE); + assert_eq!(rewards, 2_000_000_000_000); + + let treasury = Rewards::::get(&TREASURY); + assert_eq!(treasury, 2_000_000_000_000); + }); +} diff --git a/pallets/referrals/src/weights.rs b/pallets/referrals/src/weights.rs index 118ad8842..c174db4b2 100644 --- a/pallets/referrals/src/weights.rs +++ b/pallets/referrals/src/weights.rs @@ -50,6 +50,7 @@ use sp_std::marker::PhantomData; pub trait WeightInfo { fn register_code() -> Weight; fn link_code() -> Weight; + fn convert() -> Weight; fn claim_rewards() -> Weight; } @@ -65,6 +66,10 @@ impl WeightInfo for HydraWeight { Weight::zero() } + fn convert() -> Weight { + Weight::zero() + } + fn claim_rewards() -> Weight { Weight::zero() } @@ -80,6 +85,10 @@ impl WeightInfo for () { Weight::zero() } + fn convert() -> Weight { + Weight::zero() + } + fn claim_rewards() -> Weight { Weight::zero() } From 7f1ac11d7cf02d322558c9ba057e1e43c2d27793 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 21 Nov 2023 12:21:39 +0100 Subject: [PATCH 10/90] link same account is not allowed --- pallets/referrals/src/lib.rs | 4 ++++ pallets/referrals/src/tests/link.rs | 20 +++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index eb1ac191d..8bd84a974 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -126,6 +126,8 @@ pub mod pallet { InvalidCode, AlreadyLinked, ZeroAmount, + /// Linking an account to the same referral account is not allowed. + LinkNotAllowed, } #[pallet::call] @@ -194,6 +196,8 @@ pub mod pallet { LinkedAccounts::::mutate(who.clone(), |v| -> DispatchResult { ensure!(v.is_none(), Error::::AlreadyLinked); + ensure!(who != ref_account, Error::::LinkNotAllowed); + *v = Some(ref_account.clone()); Self::deposit_event(Event::CodeLinked { account: who, diff --git a/pallets/referrals/src/tests/link.rs b/pallets/referrals/src/tests/link.rs index 90472194e..ef6850051 100644 --- a/pallets/referrals/src/tests/link.rs +++ b/pallets/referrals/src/tests/link.rs @@ -11,7 +11,7 @@ fn link_code_should_work_when_code_is_valid() { ALICE, )); // ACT - assert_ok!(Referrals::link_code(RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec())); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); }); } @@ -54,6 +54,24 @@ fn link_code_should_link_correctly_when_code_is_valid() { }); } +#[test] +fn link_code_should_fail_when_linking_to_same_acccount() { + ExtBuilder::default().build().execute_with(|| { + // ARRANGE + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE + )); + + // ACT + assert_noop!( + Referrals::link_code(RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec()), + Error::::LinkNotAllowed + ); + }); +} + #[test] fn link_code_should_link_correctly_when_code_is_lowercase() { ExtBuilder::default().build().execute_with(|| { From 420fc60a5720eec1d38c1e39547c023e812ae631 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 21 Nov 2023 12:41:45 +0100 Subject: [PATCH 11/90] add conversion support --- pallets/referrals/src/lib.rs | 5 +++++ pallets/referrals/src/tests.rs | 25 ++++++++++++++++++++++++- pallets/referrals/src/traits.rs | 6 ++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 pallets/referrals/src/traits.rs diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 8bd84a974..4d3187969 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -22,6 +22,7 @@ mod weights; #[cfg(test)] mod tests; +pub mod traits; use frame_support::pallet_prelude::{DispatchResult, Get}; use frame_system::{ensure_signed, pallet_prelude::OriginFor}; @@ -40,6 +41,7 @@ const MIN_CODE_LENGTH: usize = 3; #[frame_support::pallet] pub mod pallet { use super::*; + use crate::traits::Convert; use frame_support::pallet_prelude::*; use frame_support::traits::fungibles::Transfer; use frame_support::PalletId; @@ -59,6 +61,9 @@ pub mod pallet { /// Support for transfers. type Currency: Transfer; + /// Support for asset conversion. + type Convert: Convert; + /// Pallet id. Determines account which holds accumulated rewards in various assets. #[pallet::constant] type PalletId: Get; diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 81b8ad46f..1f86b466e 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -36,10 +36,11 @@ use frame_support::{ }; use sp_core::H256; +use crate::traits::Convert; use frame_support::{assert_noop, assert_ok}; use orml_traits::MultiCurrency; use orml_traits::{parameter_type_with_key, MultiCurrencyExtended}; -use sp_runtime::FixedU128; +use sp_runtime::{DispatchError, FixedPointNumber, FixedU128}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -86,6 +87,7 @@ impl Config for Test { type RuntimeEvent = RuntimeEvent; type AssetId = AssetId; type Currency = Tokens; + type Convert = AssetConvert; type PalletId = RefarralPalletId; type RegistrationFee = RegistrationFee; type CodeLength = CodeLength; @@ -204,6 +206,27 @@ pub fn expect_events(e: Vec) { e.into_iter().for_each(frame_system::Pallet::::assert_has_event); } +pub struct AssetConvert; + +impl Convert for AssetConvert { + type Error = DispatchError; + + fn convert( + who: AccountId, + asset_from: AssetId, + asset_to: AssetId, + amount: Balance, + ) -> Result { + let price = CONVERSION_RATE + .with(|v| v.borrow().get(&(asset_from, asset_to)).copied()) + .ok_or(Error::::InvalidCode)?; + let result = price.saturating_mul_int(amount); + Tokens::update_balance(asset_from, &who, -(amount as i128)).unwrap(); + Tokens::update_balance(asset_to, &who, result as i128).unwrap(); + Ok(result) + } +} + #[macro_export] macro_rules! assert_balance { ( $x:expr, $y:expr, $z:expr) => {{ diff --git a/pallets/referrals/src/traits.rs b/pallets/referrals/src/traits.rs new file mode 100644 index 000000000..c3ddbdd01 --- /dev/null +++ b/pallets/referrals/src/traits.rs @@ -0,0 +1,6 @@ +pub trait Convert { + type Error; + + fn convert(who: AccountId, asset_from: AssetId, asset_to: AssetId, amount: Balance) + -> Result; +} From 41b63e668186571543e64a0f7aadd3884da0dcd6 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 21 Nov 2023 13:02:23 +0100 Subject: [PATCH 12/90] add reward asset config param --- pallets/referrals/src/lib.rs | 10 ++++++++++ pallets/referrals/src/tests.rs | 3 ++- pallets/referrals/src/tests/convert.rs | 22 ++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 4d3187969..3d62c941b 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -64,6 +64,10 @@ pub mod pallet { /// Support for asset conversion. type Convert: Convert; + /// ID of an asset that is used to distribute rewards in. + #[pallet::constant] + type RewardAsset: Get; + /// Pallet id. Determines account which holds accumulated rewards in various assets. #[pallet::constant] type PalletId: Get; @@ -119,6 +123,12 @@ pub mod pallet { code: ReferralCode, referral_account: T::AccountId, }, + Converted { + from: T::AccountId, + to: T::AccountId, + amount: Balance, + received: Balance, + }, } #[pallet::error] diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 1f86b466e..45d311d71 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -80,7 +80,7 @@ parameter_types! { pub const RefarralPalletId: PalletId = PalletId(*b"test_ref"); pub const CodeLength: u32 = 7; pub const RegistrationFee: (AssetId,Balance, AccountId) = (HDX, 222 * 1_000_000_000_000, TREASURY) ; - pub const FeeAsset: AssetId = HDX; + pub const RewardAsset: AssetId = HDX; } impl Config for Test { @@ -88,6 +88,7 @@ impl Config for Test { type AssetId = AssetId; type Currency = Tokens; type Convert = AssetConvert; + type RewardAsset = RewardAsset; type PalletId = RefarralPalletId; type RegistrationFee = RegistrationFee; type CodeLength = CodeLength; diff --git a/pallets/referrals/src/tests/convert.rs b/pallets/referrals/src/tests/convert.rs index 491c9643b..31dc660b0 100644 --- a/pallets/referrals/src/tests/convert.rs +++ b/pallets/referrals/src/tests/convert.rs @@ -65,6 +65,28 @@ fn convert_should_update_account_rewards() { assert_eq!(rewards, 1_000_000_000_000); }); } +#[test] +fn convert_should_emit_event_when_successful() { + ExtBuilder::default() + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_trade_activity(vec![(BOB, DAI, 1_000_000_000_000_000_000)]) + .build() + .execute_with(|| { + // Arrange + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI,)); + // Assert + expect_events(vec![Event::Converted { + from: DAI.into(), + to: RewardAsset::get().into(), + amount: 1_000_000_000_000_000_000, + received: 1_000_000_000_000, + } + .into()]); + }); +} #[test] fn convert_should_distribute_native_amount_correct_when_there_is_multiple_entries() { From 1fb2955e5252417f434a88db18724a9f80583521 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 21 Nov 2023 13:45:33 +0100 Subject: [PATCH 13/90] implement convert --- pallets/referrals/src/lib.rs | 42 ++++++++++++++++++++++++-- pallets/referrals/src/tests.rs | 3 +- pallets/referrals/src/tests/convert.rs | 42 +++++++++++++++++++++----- 3 files changed, 76 insertions(+), 11 deletions(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 3d62c941b..f468e6991 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -43,7 +43,8 @@ pub mod pallet { use super::*; use crate::traits::Convert; use frame_support::pallet_prelude::*; - use frame_support::traits::fungibles::Transfer; + use frame_support::sp_runtime::ArithmeticError; + use frame_support::traits::fungibles::{Inspect, Transfer}; use frame_support::PalletId; #[pallet::pallet] @@ -143,6 +144,8 @@ pub mod pallet { ZeroAmount, /// Linking an account to the same referral account is not allowed. LinkNotAllowed, + /// More rewards have been distributed than allowed after conversion. This is a bug. + IncorrectRewardDistribution, } #[pallet::call] @@ -234,7 +237,42 @@ pub mod pallet { #[pallet::call_index(2)] #[pallet::weight(::WeightInfo::convert())] pub fn convert(origin: OriginFor, asset_id: T::AssetId) -> DispatchResult { - let who = ensure_signed(origin)?; + ensure_signed(origin)?; + let asset_balance = T::Currency::balance(asset_id, &Self::pot_account_id()); + ensure!(asset_balance > 0, Error::::ZeroAmount); + + let total_reward_amount = + T::Convert::convert(Self::pot_account_id(), asset_id, T::RewardAsset::get(), asset_balance)?; + + // Keep track of amount rewarded, in case of a a leftover (due to rounding) + let mut rewarded: Balance = 0; + for (account, amount) in Accrued::::drain_prefix(asset_id) { + // We need to figure out how much of the reward should be assigned to the individual recipients. + // Price = reward_asset_amount / asset_balance + // rewarded = price * account balance + let reward_amount = (total_reward_amount * amount) / asset_balance; // TODO: U256 and safe math please! + + Rewards::::try_mutate(account, |v| -> DispatchResult { + *v = v.checked_add(reward_amount).ok_or(ArithmeticError::Overflow)?; + Ok(()) + })?; + rewarded = rewarded.saturating_add(reward_amount); + } + + // Should not really happy, but let's be safe and ensure that we have not distributed more than allowed. + ensure!(rewarded <= total_reward_amount, Error::::IncorrectRewardDistribution); + + let remainder = total_reward_amount.saturating_sub(rewarded); + if remainder > 0 { + T::Currency::transfer( + T::RewardAsset::get(), + &Self::pot_account_id(), + &T::RegistrationFee::get().2, + remainder, + true, + )?; + } + Ok(()) } diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 45d311d71..245537045 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -52,6 +52,7 @@ pub(crate) const ONE: Balance = 1_000_000_000_000; pub const HDX: AssetId = 0; pub const DAI: AssetId = 2; +pub const DOT: AssetId = 5; pub const ALICE: AccountId = 1; pub const BOB: AccountId = 2; @@ -219,7 +220,7 @@ impl Convert for AssetConvert { amount: Balance, ) -> Result { let price = CONVERSION_RATE - .with(|v| v.borrow().get(&(asset_from, asset_to)).copied()) + .with(|v| v.borrow().get(&(asset_to, asset_from)).copied()) .ok_or(Error::::InvalidCode)?; let result = price.saturating_mul_int(amount); Tokens::update_balance(asset_from, &who, -(amount as i128)).unwrap(); diff --git a/pallets/referrals/src/tests/convert.rs b/pallets/referrals/src/tests/convert.rs index 31dc660b0..4c52ce3ba 100644 --- a/pallets/referrals/src/tests/convert.rs +++ b/pallets/referrals/src/tests/convert.rs @@ -13,25 +13,27 @@ fn convert_should_fail_when_amount_is_zero() { } #[test] -fn convert_should_convert_all_entries() { +fn convert_should_convert_all_asset_entries() { ExtBuilder::default() .with_conversion_price( (HDX, DAI), FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), ) - .with_trade_activity(vec![(BOB, DAI, 1_000_000_000_000_000_000)]) + .with_trade_activity(vec![(BOB, DAI, 1_000_000_000_000_000_000), (BOB, DOT, 10_000_000_000)]) .build() .execute_with(|| { // Arrange assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI,)); // Assert - let length = Accrued::::iter().count(); + let length = Accrued::::iter_prefix(DAI).count(); assert_eq!(length, 0); + let length = Accrued::::iter_prefix(DOT).count(); + assert_eq!(length, 1); }); } #[test] -fn convert_should_not_have_converted_asset_when_successful() { +fn convert_should_convert_all_asset_amount_when_successful() { ExtBuilder::default() .with_conversion_price( (HDX, DAI), @@ -45,6 +47,8 @@ fn convert_should_not_have_converted_asset_when_successful() { // Assert let balance = Tokens::free_balance(DAI, &Pallet::::pot_account_id()); assert_eq!(balance, 0); + let balance = Tokens::free_balance(HDX, &Pallet::::pot_account_id()); + assert_eq!(balance, 1_000_000_000_000); }); } @@ -129,12 +133,34 @@ fn convert_should_transfer_leftovers_to_registration_fee_beneficiary() { assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI,)); // Assert let rewards = Rewards::::get(&BOB); - assert_eq!(rewards, 1_000_000_000_000); + assert_eq!(rewards, 1_333_333_333_333); let rewards = Rewards::::get(&CHARLIE); - assert_eq!(rewards, 2_000_000_000_000); + assert_eq!(rewards, 2_333_333_333_332); + + let treasury = Tokens::free_balance(HDX, &TREASURY); + assert_eq!(treasury, 1); + }); +} - let treasury = Rewards::::get(&TREASURY); - assert_eq!(treasury, 2_000_000_000_000); +#[test] +fn convert_should_have_correct_reward_balance() { + ExtBuilder::default() + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_333_333_333_333, 1_333_333_333_333_333_333), + ) + .with_trade_activity(vec![ + (BOB, DAI, 1_333_333_333_333_333_333), + (CHARLIE, DAI, 2_333_333_333_333_333_333), + ]) + .build() + .execute_with(|| { + // Arrange + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); + // Assert + let distributed = Rewards::::iter_values().sum::(); + let reserve = Tokens::free_balance(HDX, &Pallet::::pot_account_id()); + assert_eq!(reserve, distributed); }); } From 06cc30592c499a74a5f35abe37616cbd2a47645f Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 21 Nov 2023 13:51:29 +0100 Subject: [PATCH 14/90] emit converted event --- pallets/referrals/src/lib.rs | 11 +++++++++-- pallets/referrals/src/tests/convert.rs | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index f468e6991..23bbce2c9 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -125,8 +125,8 @@ pub mod pallet { referral_account: T::AccountId, }, Converted { - from: T::AccountId, - to: T::AccountId, + from: T::AssetId, + to: T::AssetId, amount: Balance, received: Balance, }, @@ -273,6 +273,13 @@ pub mod pallet { )?; } + Self::deposit_event(Event::Converted { + from: asset_id, + to: T::RewardAsset::get(), + amount: asset_balance, + received: total_reward_amount, + }); + Ok(()) } diff --git a/pallets/referrals/src/tests/convert.rs b/pallets/referrals/src/tests/convert.rs index 4c52ce3ba..7d63316b5 100644 --- a/pallets/referrals/src/tests/convert.rs +++ b/pallets/referrals/src/tests/convert.rs @@ -83,8 +83,8 @@ fn convert_should_emit_event_when_successful() { assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI,)); // Assert expect_events(vec![Event::Converted { - from: DAI.into(), - to: RewardAsset::get().into(), + from: DAI, + to: RewardAsset::get(), amount: 1_000_000_000_000_000_000, received: 1_000_000_000_000, } From 0e3a789f112cf46372d37a4e6b6b91c2ad484933 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Tue, 21 Nov 2023 21:43:56 +0100 Subject: [PATCH 15/90] add claim implementation and tests --- pallets/referrals/src/lib.rs | 7 ++++ pallets/referrals/src/tests.rs | 15 ++++++++ pallets/referrals/src/tests/claim.rs | 56 ++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 pallets/referrals/src/tests/claim.rs diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 23bbce2c9..df96a0a56 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -130,6 +130,10 @@ pub mod pallet { amount: Balance, received: Balance, }, + Claimed { + who: T::AccountId, + amount: Balance, + }, } #[pallet::error] @@ -293,6 +297,9 @@ pub mod pallet { #[pallet::weight(::WeightInfo::claim_rewards())] pub fn claim_rewards(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; + let amount = Rewards::::take(&who); + T::Currency::transfer(T::RewardAsset::get(), &Self::pot_account_id(), &who, amount, true)?; + Self::deposit_event(Event::Claimed { who, amount }); Ok(()) } } diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 245537045..3e5c12738 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -15,6 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod claim; mod convert; mod link; mod register; @@ -146,6 +147,7 @@ impl orml_tokens::Config for Test { pub struct ExtBuilder { endowed_accounts: Vec<(AccountId, AssetId, Balance)>, trades: Vec<(AccountId, AssetId, Balance)>, + rewards: Vec<(AccountId, Balance)>, } impl Default for ExtBuilder { @@ -156,6 +158,7 @@ impl Default for ExtBuilder { Self { endowed_accounts: vec![(ALICE, HDX, INITIAL_ALICE_BALANCE)], trades: vec![], + rewards: vec![], } } } @@ -166,6 +169,11 @@ impl ExtBuilder { self } + pub fn with_rewards(mut self, rewards: Vec<(AccountId, Balance)>) -> Self { + self.rewards.extend(rewards); + self + } + pub fn with_conversion_price(self, pair: (AssetId, AssetId), price: FixedU128) -> Self { CONVERSION_RATE.with(|v| { let mut m = v.borrow_mut(); @@ -196,6 +204,13 @@ impl ExtBuilder { } }); + r.execute_with(|| { + for (acc, amount) in self.rewards.iter() { + Rewards::::insert(acc, amount); + Tokens::update_balance(HDX, &Pallet::::pot_account_id(), *amount as i128).unwrap(); + } + }); + r.execute_with(|| { System::set_block_number(1); }); diff --git a/pallets/referrals/src/tests/claim.rs b/pallets/referrals/src/tests/claim.rs new file mode 100644 index 000000000..33b2ed0b0 --- /dev/null +++ b/pallets/referrals/src/tests/claim.rs @@ -0,0 +1,56 @@ +use crate::tests::*; +use pretty_assertions::assert_eq; + +#[test] +fn claim_rewards_should_work_when_amount_is_zero() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB))); + }); +} + +#[test] +fn claim_rewards_should_transfer_rewards() { + ExtBuilder::default() + .with_rewards(vec![(BOB, 1_000_000_000_000)]) + .build() + .execute_with(|| { + assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB))); + // Assert + let reserve = Tokens::free_balance(HDX, &BOB); + assert_eq!(reserve, 1_000_000_000_000); + + let reserve = Tokens::free_balance(HDX, &Pallet::::pot_account_id()); + assert_eq!(reserve, 0); + }); +} + +#[test] +fn claim_rewards_should_reset_rewards_amount() { + ExtBuilder::default() + .with_rewards(vec![(BOB, 1_000_000_000_000)]) + .build() + .execute_with(|| { + // Act + assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB))); + // Assert + let rewards = Rewards::::get(&BOB); + assert_eq!(rewards, 0); + }); +} + +#[test] +fn claim_rewards_should_emit_event_when_successful() { + ExtBuilder::default() + .with_rewards(vec![(BOB, 1_000_000_000_000)]) + .build() + .execute_with(|| { + // Act + assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB))); + // Assert + expect_events(vec![Event::Claimed { + who: BOB, + amount: 1_000_000_000_000, + } + .into()]); + }); +} From c2bd576b1ffb7232a0da9e349ba4f0e199f0067d Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 22 Nov 2023 13:44:03 +0100 Subject: [PATCH 16/90] add referrer level info --- pallets/referrals/src/lib.rs | 52 ++++++++- pallets/referrals/src/tests/convert.rs | 145 ++++++++++++++++++++++++ pallets/referrals/src/tests/register.rs | 18 +++ 3 files changed, 214 insertions(+), 1 deletion(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index df96a0a56..76fd83964 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -24,10 +24,13 @@ mod weights; mod tests; pub mod traits; +use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::pallet_prelude::{DispatchResult, Get}; +use frame_support::RuntimeDebug; use frame_system::{ensure_signed, pallet_prelude::OriginFor}; use sp_core::bounded::BoundedVec; use sp_runtime::traits::AccountIdConversion; +use sp_runtime::Permill; pub use pallet::*; @@ -38,6 +41,26 @@ pub type ReferralCode = BoundedVec; const MIN_CODE_LENGTH: usize = 3; +use scale_info::TypeInfo; + +#[derive(Clone, Default, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub enum Level { + #[default] + Novice, + Advanced, + Expert, +} + +#[derive(Clone, Default, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub struct Tier { + /// Percentage of the fee that goes to the referrer. + referrer: Permill, + /// Percentage of the fee that goes back to the trader. + trader: Permill, + /// Amount of accumulated rewards to unlock next tier. If None, this is the last tier. + next_tier: Option, +} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -46,6 +69,7 @@ pub mod pallet { use frame_support::sp_runtime::ArithmeticError; use frame_support::traits::fungibles::{Inspect, Transfer}; use frame_support::PalletId; + use sp_runtime::traits::Zero; #[pallet::pallet] #[pallet::generate_store(pub(crate) trait Store)] @@ -112,6 +136,20 @@ pub mod pallet { #[pallet::getter(fn account_rewards)] pub(super) type Rewards = StorageMap<_, Blake2_128Concat, T::AccountId, Balance, ValueQuery>; + /// Referer level and total accumulated rewards over time. + /// Maps referrer account to (Level, Balance). Level indicates current reward tier and Balance is used to unlock next tier level. + /// Dev note: we use OptionQuery here because this helps to easily determine that an account if referrer account. + #[pallet::storage] + #[pallet::getter(fn referrer_level)] + pub(super) type Referrer = StorageMap<_, Blake2_128Concat, T::AccountId, (Level, Balance), OptionQuery>; + + /// + /// + #[pallet::storage] + #[pallet::getter(fn asset_tier)] + pub(super) type AssetTier = + StorageDoubleMap<_, Blake2_128Concat, T::AssetId, Blake2_128Concat, Level, Tier, OptionQuery>; + #[pallet::event] #[pallet::generate_deposit(pub(crate) fn deposit_event)] pub enum Event { @@ -195,6 +233,7 @@ pub mod pallet { T::Currency::transfer(fee_asset, &who, &beneficiary, fee_amount, true)?; *v = Some(account.clone()); + Referrer::::insert(&account, (Level::default(), Balance::zero())); Self::deposit_event(Event::CodeRegistered { code, account }); Ok(()) }) @@ -256,8 +295,18 @@ pub mod pallet { // rewarded = price * account balance let reward_amount = (total_reward_amount * amount) / asset_balance; // TODO: U256 and safe math please! - Rewards::::try_mutate(account, |v| -> DispatchResult { + Rewards::::try_mutate(account.clone(), |v| -> DispatchResult { *v = v.checked_add(reward_amount).ok_or(ArithmeticError::Overflow)?; + + Referrer::::mutate(account, |d| { + // You might ask why do we need to have an OptionQuery here? it would be simpler to just have value query and update the account + // However, in Rewards, not all accounts are necessarily Referrer accounts. + // We heep there trader account which earn back some percentage of the fee. And for those, no levels! + if let Some((level, total)) = d { + *total = total.saturating_add(reward_amount); + // TODO: update level ? + } + }); Ok(()) })?; rewarded = rewarded.saturating_add(reward_amount); @@ -266,6 +315,7 @@ pub mod pallet { // Should not really happy, but let's be safe and ensure that we have not distributed more than allowed. ensure!(rewarded <= total_reward_amount, Error::::IncorrectRewardDistribution); + // Due to rounding, there can be something left, let's just transfer it to treasury. let remainder = total_reward_amount.saturating_sub(rewarded); if remainder > 0 { T::Currency::transfer( diff --git a/pallets/referrals/src/tests/convert.rs b/pallets/referrals/src/tests/convert.rs index 7d63316b5..41d8c6ff4 100644 --- a/pallets/referrals/src/tests/convert.rs +++ b/pallets/referrals/src/tests/convert.rs @@ -164,3 +164,148 @@ fn convert_should_have_correct_reward_balance() { assert_eq!(reserve, distributed); }); } + +#[test] +fn convert_should_update_total_accumulated_rewards_for_referrer() { + ExtBuilder::default() + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_333_333_333_333, 1_333_333_333_333_333_333), + ) + .with_trade_activity(vec![ + (BOB, DAI, 1_333_333_333_333_333_333), + (CHARLIE, DAI, 2_333_333_333_333_333_333), + ]) + .build() + .execute_with(|| { + // Arrange + let code = b"BALLS69".to_vec(); + // Act + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + code.clone(), + BOB + )); + // Act + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); + // Assert + let (_, total_rewards) = Referrer::::get(&BOB).unwrap(); + assert_eq!(total_rewards, 1_333_333_333_333); + }); +} + +#[test] +fn convert_should_update_referrer_level_when_next_tier_is_reached() { + ExtBuilder::default() + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_333_333_333_333, 1_333_333_333_333_333_333), + ) + .with_trade_activity(vec![ + (BOB, DAI, 1_333_333_333_333_333_333), + (CHARLIE, DAI, 2_333_333_333_333_333_333), + ]) + .build() + .execute_with(|| { + // Arrange + let code = b"BALLS69".to_vec(); + // Act + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + code.clone(), + BOB + )); + // Act + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); + // Assert + let (level, total_rewards) = Referrer::::get(&BOB).unwrap(); + assert_eq!(total_rewards, 1_333_333_333_333); + assert_eq!(level, Level::Advanced); + }); +} + +#[test] +fn convert_should_update_referrer_level_to_top_one_when_next_tier_is_reached_and_when_one_level_has_to_be_skipper() { + ExtBuilder::default() + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_333_333_333_333, 1_333_333_333_333_333_333), + ) + .with_trade_activity(vec![ + (BOB, DAI, 1_333_333_333_333_333_333), + (CHARLIE, DAI, 2_333_333_333_333_333_333), + ]) + .build() + .execute_with(|| { + // Arrange + let code = b"BALLS69".to_vec(); + // Act + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + code.clone(), + BOB + )); + // Act + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); + // Assert + let (level, total_rewards) = Referrer::::get(&BOB).unwrap(); + assert_eq!(total_rewards, 1_333_333_333_333); + assert_eq!(level, Level::Expert); + }); +} + +#[test] +fn convert_should_only_update_total_amount_when_top_tier_is_reached() { + ExtBuilder::default() + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_conversion_price( + (HDX, DOT), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_trade_activity(vec![ + (BOB, DAI, 1_333_333_333_333_333_333), + (BOB, DOT, 2_333_333_333_333_333_333), + ]) + .build() + .execute_with(|| { + // Arrange + let code = b"BALLS69".to_vec(); + // Act + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + code.clone(), + BOB + )); + // Act + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DOT)); + // Assert + let (level, total_rewards) = Referrer::::get(&BOB).unwrap(); + assert_eq!(total_rewards, 3_666_666_666_666); + assert_eq!(level, Level::Expert); + }); +} + +#[test] +fn convert_should_not_update_total_rewards_when_account_is_not_referral_account() { + ExtBuilder::default() + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_333_333_333_333, 1_333_333_333_333_333_333), + ) + .with_trade_activity(vec![ + (BOB, DAI, 1_333_333_333_333_333_333), + (CHARLIE, DAI, 2_333_333_333_333_333_333), + ]) + .build() + .execute_with(|| { + // Act + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); + // Assert + let entry = Referrer::::get(&BOB); + assert_eq!(entry, None); + }); +} diff --git a/pallets/referrals/src/tests/register.rs b/pallets/referrals/src/tests/register.rs index 63bb343e5..1f9e9db94 100644 --- a/pallets/referrals/src/tests/register.rs +++ b/pallets/referrals/src/tests/register.rs @@ -1,5 +1,6 @@ use crate::tests::*; use pretty_assertions::assert_eq; +use sp_runtime::traits::Zero; #[test] fn register_code_should_work_when_code_is_max_length() { @@ -169,3 +170,20 @@ fn singer_should_pay_the_registration_fee() { assert_balance!(beneficiary, fee_asset, amount); }); } + +#[test] +fn singer_should_set_default_level_for_referrer() { + ExtBuilder::default().build().execute_with(|| { + // Arrange + let code = b"BALLS69".to_vec(); + // Act + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + code.clone(), + BOB + )); + // Assert + let entry = Pallet::::referrer_level(BOB); + assert_eq!(entry, Some((Level::default(), Balance::zero()))); + }); +} From 5ae5e07c2bec11be31a8992476601819bdf16bb2 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 23 Nov 2023 10:51:28 +0100 Subject: [PATCH 17/90] implement level ups --- pallets/referrals/Cargo.toml | 2 +- pallets/referrals/src/lib.rs | 33 +++++++++++++++++++++--- pallets/referrals/src/tests.rs | 22 +++++++++++++++- pallets/referrals/src/tests/convert.rs | 35 ++++++++++++++++++++------ 4 files changed, 79 insertions(+), 13 deletions(-) diff --git a/pallets/referrals/Cargo.toml b/pallets/referrals/Cargo.toml index 1a83fe6ab..3fd752adb 100644 --- a/pallets/referrals/Cargo.toml +++ b/pallets/referrals/Cargo.toml @@ -33,6 +33,7 @@ frame-system = { workspace = true } #frame-benchmarking = { workspace = true, optional = true } sp-io = { workspace = true, optional = true } pallet-timestamp = { workspace = true, optional = true } +orml-traits = { workspace = true } [dev-dependencies] sp-io = { workspace = true } @@ -40,7 +41,6 @@ sp-tracing = { workspace = true } #frame-benchmarking = { workspace = true } pretty_assertions = "1.2.1" orml-tokens = { workspace = true } -orml-traits = { workspace = true } [features] default = ["std"] diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 76fd83964..e649dbacc 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -28,6 +28,7 @@ use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::pallet_prelude::{DispatchResult, Get}; use frame_support::RuntimeDebug; use frame_system::{ensure_signed, pallet_prelude::OriginFor}; +use orml_traits::GetByKey; use sp_core::bounded::BoundedVec; use sp_runtime::traits::AccountIdConversion; use sp_runtime::Permill; @@ -43,7 +44,7 @@ const MIN_CODE_LENGTH: usize = 3; use scale_info::TypeInfo; -#[derive(Clone, Default, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +#[derive(Hash, Clone, Default, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] pub enum Level { #[default] Novice, @@ -51,14 +52,22 @@ pub enum Level { Expert, } +impl Level { + pub fn next_level(&self) -> Self { + match self { + Self::Novice => Self::Advanced, + Self::Advanced => Self::Expert, + Self::Expert => Self::Expert, + } + } +} + #[derive(Clone, Default, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] pub struct Tier { /// Percentage of the fee that goes to the referrer. referrer: Permill, /// Percentage of the fee that goes back to the trader. trader: Permill, - /// Amount of accumulated rewards to unlock next tier. If None, this is the last tier. - next_tier: Option, } #[frame_support::pallet] @@ -106,6 +115,9 @@ pub mod pallet { #[pallet::constant] type CodeLength: Get; + /// Volume needed to next tier. If None returned, it is the last tier. + type TierVolume: GetByKey>; + /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; } @@ -304,7 +316,20 @@ pub mod pallet { // We heep there trader account which earn back some percentage of the fee. And for those, no levels! if let Some((level, total)) = d { *total = total.saturating_add(reward_amount); - // TODO: update level ? + + let next_tier = T::TierVolume::get(&level); + if let Some(amount_needed) = next_tier { + if *total >= amount_needed { + *level = level.next_level(); + // let's check if we can skip two levels + let next_tier = T::TierVolume::get(&level); + if let Some(amount_needed) = next_tier { + if *total >= amount_needed { + *level = level.next_level(); + } + } + } + } } }); Ok(()) diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 3e5c12738..4f6b7f5b5 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -23,8 +23,9 @@ mod register; use crate as pallet_referrals; use crate::*; -use std::cell::RefCell; +use std::cell::{RefCell, RefMut}; use std::collections::HashMap; +use std::mem; use frame_support::{ construct_runtime, parameter_types, @@ -64,6 +65,7 @@ pub(crate) const INITIAL_ALICE_BALANCE: Balance = 1_000 * ONE; thread_local! { pub static CONVERSION_RATE: RefCell> = RefCell::new(HashMap::default()); + pub static TIER_VOLUME: RefCell>> = RefCell::new(HashMap::default()); } construct_runtime!( @@ -85,6 +87,14 @@ parameter_types! { pub const RewardAsset: AssetId = HDX; } +pub struct Volume; + +impl GetByKey> for Volume { + fn get(level: &Level) -> Option { + TIER_VOLUME.with(|v| v.borrow().get(level).copied()).unwrap_or_default() + } +} + impl Config for Test { type RuntimeEvent = RuntimeEvent; type AssetId = AssetId; @@ -94,6 +104,7 @@ impl Config for Test { type PalletId = RefarralPalletId; type RegistrationFee = RegistrationFee; type CodeLength = CodeLength; + type TierVolume = Volume; type WeightInfo = (); } @@ -163,6 +174,8 @@ impl Default for ExtBuilder { } } +use std::borrow::BorrowMut; + impl ExtBuilder { pub fn with_trade_activity(mut self, trades: Vec<(AccountId, AssetId, Balance)>) -> Self { self.trades.extend(trades); @@ -182,6 +195,13 @@ impl ExtBuilder { self } + pub fn with_tier_volumes(self, volumes: HashMap>) -> Self { + TIER_VOLUME.with(|v| { + v.swap(&RefCell::new(volumes)); + }); + self + } + pub fn build(self) -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); diff --git a/pallets/referrals/src/tests/convert.rs b/pallets/referrals/src/tests/convert.rs index 41d8c6ff4..e7c5271fc 100644 --- a/pallets/referrals/src/tests/convert.rs +++ b/pallets/referrals/src/tests/convert.rs @@ -196,6 +196,11 @@ fn convert_should_update_total_accumulated_rewards_for_referrer() { #[test] fn convert_should_update_referrer_level_when_next_tier_is_reached() { + let mut volumes: HashMap> = HashMap::new(); + volumes.insert(Level::Novice, Some(1_000_000_000_000)); + volumes.insert(Level::Advanced, Some(2_000_000_000_000)); + volumes.insert(Level::Expert, None); + ExtBuilder::default() .with_conversion_price( (HDX, DAI), @@ -205,6 +210,7 @@ fn convert_should_update_referrer_level_when_next_tier_is_reached() { (BOB, DAI, 1_333_333_333_333_333_333), (CHARLIE, DAI, 2_333_333_333_333_333_333), ]) + .with_tier_volumes(volumes) .build() .execute_with(|| { // Arrange @@ -226,15 +232,18 @@ fn convert_should_update_referrer_level_when_next_tier_is_reached() { #[test] fn convert_should_update_referrer_level_to_top_one_when_next_tier_is_reached_and_when_one_level_has_to_be_skipper() { + let mut volumes: HashMap> = HashMap::new(); + volumes.insert(Level::Novice, Some(1_000_000_000_000)); + volumes.insert(Level::Advanced, Some(2_000_000_000_000)); + volumes.insert(Level::Expert, None); + ExtBuilder::default() .with_conversion_price( (HDX, DAI), - FixedU128::from_rational(1_333_333_333_333, 1_333_333_333_333_333_333), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), ) - .with_trade_activity(vec![ - (BOB, DAI, 1_333_333_333_333_333_333), - (CHARLIE, DAI, 2_333_333_333_333_333_333), - ]) + .with_trade_activity(vec![(BOB, DAI, 2_333_333_333_333_333_333)]) + .with_tier_volumes(volumes) .build() .execute_with(|| { // Arrange @@ -249,13 +258,18 @@ fn convert_should_update_referrer_level_to_top_one_when_next_tier_is_reached_and assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); // Assert let (level, total_rewards) = Referrer::::get(&BOB).unwrap(); - assert_eq!(total_rewards, 1_333_333_333_333); + assert_eq!(total_rewards, 2_333_333_333_333); assert_eq!(level, Level::Expert); }); } #[test] fn convert_should_only_update_total_amount_when_top_tier_is_reached() { + let mut volumes: HashMap> = HashMap::new(); + volumes.insert(Level::Novice, Some(1_000_000_000_000)); + volumes.insert(Level::Advanced, Some(2_000_000_000_000)); + volumes.insert(Level::Expert, None); + ExtBuilder::default() .with_conversion_price( (HDX, DAI), @@ -265,10 +279,16 @@ fn convert_should_only_update_total_amount_when_top_tier_is_reached() { (HDX, DOT), FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), ) + .with_conversion_price( + (HDX, 1234), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) .with_trade_activity(vec![ (BOB, DAI, 1_333_333_333_333_333_333), (BOB, DOT, 2_333_333_333_333_333_333), + (BOB, 1234, 1_000_000_000_000_000_000), ]) + .with_tier_volumes(volumes) .build() .execute_with(|| { // Arrange @@ -282,9 +302,10 @@ fn convert_should_only_update_total_amount_when_top_tier_is_reached() { // Act assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DOT)); + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), 1234)); // Assert let (level, total_rewards) = Referrer::::get(&BOB).unwrap(); - assert_eq!(total_rewards, 3_666_666_666_666); + assert_eq!(total_rewards, 4_666_666_666_666); assert_eq!(level, Level::Expert); }); } From d857a4ee4587be5cff795f461b3922a221c31f40 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 23 Nov 2023 12:27:25 +0100 Subject: [PATCH 18/90] remove unused stuff --- pallets/referrals/src/tests.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 4f6b7f5b5..b3c1d4411 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -23,9 +23,8 @@ mod register; use crate as pallet_referrals; use crate::*; -use std::cell::{RefCell, RefMut}; +use std::cell::RefCell; use std::collections::HashMap; -use std::mem; use frame_support::{ construct_runtime, parameter_types, @@ -174,8 +173,6 @@ impl Default for ExtBuilder { } } -use std::borrow::BorrowMut; - impl ExtBuilder { pub fn with_trade_activity(mut self, trades: Vec<(AccountId, AssetId, Balance)>) -> Self { self.trades.extend(trades); From f268cb8052ea1278928ce9d766b367cd00e94395 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 23 Nov 2023 12:27:59 +0100 Subject: [PATCH 19/90] clippy --- pallets/referrals/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index e649dbacc..e0c1aa3fd 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -317,12 +317,12 @@ pub mod pallet { if let Some((level, total)) = d { *total = total.saturating_add(reward_amount); - let next_tier = T::TierVolume::get(&level); + let next_tier = T::TierVolume::get(level); if let Some(amount_needed) = next_tier { if *total >= amount_needed { *level = level.next_level(); // let's check if we can skip two levels - let next_tier = T::TierVolume::get(&level); + let next_tier = T::TierVolume::get(level); if let Some(amount_needed) = next_tier { if *total >= amount_needed { *level = level.next_level(); From 6173fbecef92c9f1ff29c22bb10d759e385908a1 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 23 Nov 2023 16:13:31 +0100 Subject: [PATCH 20/90] add prcess fee --- pallets/referrals/src/lib.rs | 50 ++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index e0c1aa3fd..eeb4ce3db 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -26,12 +26,13 @@ pub mod traits; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::pallet_prelude::{DispatchResult, Get}; -use frame_support::RuntimeDebug; +use frame_support::traits::fungibles::Transfer; +use frame_support::{transactional, RuntimeDebug}; use frame_system::{ensure_signed, pallet_prelude::OriginFor}; use orml_traits::GetByKey; use sp_core::bounded::BoundedVec; use sp_runtime::traits::AccountIdConversion; -use sp_runtime::Permill; +use sp_runtime::{DispatchError, Permill}; pub use pallet::*; @@ -390,4 +391,49 @@ impl Pallet { let r = code.into_inner().iter().map(|v| v.to_ascii_uppercase()).collect(); ReferralCode::::truncate_from(r) } + + /// Process trader fee + /// `source`: account to take the fee from + /// `trader`: account that does the trade + /// + /// Returns unused amount on success. + #[transactional] + pub fn process_trade_fee( + source: T::AccountId, + trader: T::AccountId, + asset_id: T::AssetId, + amount: Balance, + ) -> Result { + // Does trader have a linked referral account ? + let Some(ref_account) = Self::linked_referral_account(&trader) else { + return Ok(amount); + }; + // What is the referer level? + let Some((level,_)) = Self::referrer_level(&ref_account) else { + // Should not really happen, the ref entry should be always there. + return Ok(amount); + }; + + // What is asset fee for this level? if any. + let Some(tier) = Self::asset_tier(&asset_id, &level) else { + return Ok(amount); + }; + + // TODO: question: percentage from the total amount for both ? or trader takes percentage of the referrer_reward ? + let referrer_reward = tier.referrer.mul_floor(amount); + let trader_reward = tier.trader.mul_floor(amount); + + let total_taken = referrer_reward.saturating_add(trader_reward); + T::Currency::transfer(asset_id, &source, &Self::pot_account_id(), total_taken, true)?; + + // Update each accrued rewards + Accrued::::mutate(asset_id, ref_account, |v| { + *v = v.saturating_add(referrer_reward); + }); + Accrued::::mutate(asset_id, trader, |v| { + *v = v.saturating_add(trader_reward); + }); + + Ok(amount.saturating_sub(total_taken)) + } } From 57b4f1c7b23b517e09323b15256867f3e6b09269 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 24 Nov 2023 16:05:26 +0100 Subject: [PATCH 21/90] Add tests for process fees --- pallets/referrals/src/tests.rs | 38 ++++++++++++++ pallets/referrals/src/tests/mock_amm.rs | 63 ++++++++++++++++++++++++ pallets/referrals/src/tests/register.rs | 2 +- pallets/referrals/src/tests/trade_fee.rs | 60 ++++++++++++++++++++++ 4 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 pallets/referrals/src/tests/mock_amm.rs create mode 100644 pallets/referrals/src/tests/trade_fee.rs diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index b3c1d4411..dc782957a 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -19,6 +19,8 @@ mod claim; mod convert; mod link; mod register; +mod trade_fee; +mod mock_amm; use crate as pallet_referrals; use crate::*; @@ -42,6 +44,8 @@ use frame_support::{assert_noop, assert_ok}; use orml_traits::MultiCurrency; use orml_traits::{parameter_type_with_key, MultiCurrencyExtended}; use sp_runtime::{DispatchError, FixedPointNumber, FixedU128}; +use sp_runtime::traits::One; +use crate::tests::mock_amm::{Hooks, OnFeeResult, TradeResult}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -76,6 +80,7 @@ construct_runtime!( System: frame_system, Referrals: pallet_referrals, Tokens: orml_tokens, + MockAmm: mock_amm, } ); @@ -154,6 +159,12 @@ impl orml_tokens::Config for Test { type DustRemovalWhitelist = Everything; } +impl mock_amm::pallet::Config for Test { + type RuntimeEvent = RuntimeEvent; + type AssetId = AssetId; + type TradeHooks = AmmTrader; +} + pub struct ExtBuilder { endowed_accounts: Vec<(AccountId, AssetId, Balance)>, trades: Vec<(AccountId, AssetId, Balance)>, @@ -188,6 +199,7 @@ impl ExtBuilder { CONVERSION_RATE.with(|v| { let mut m = v.borrow_mut(); m.insert(pair, price); + m.insert((pair.1,pair.0), FixedU128::one().div(price)); }); self } @@ -267,3 +279,29 @@ macro_rules! assert_balance { assert_eq!(Tokens::free_balance($y, &$x), $z); }}; } + + +pub struct AmmTrader; + +const TRADE_PERCENTAGE: Permill = Permill::one(); + +impl Hooks for AmmTrader { + fn simulate_trade(who: &AccountId, asset_in: AssetId, asset_out: AssetId, amount: Balance) -> TradeResult { + let price = CONVERSION_RATE + .with(|v| v.borrow().get(&(asset_out, asset_in)).copied()) + .expect("to have a price"); + let amount_out = price.saturating_mul_int(amount); + let fee_amount = TRADE_PERCENTAGE.mul_ceil(amount_out); + TradeResult{ + amount_in: amount, + amount_out, + fee: fee_amount, + fee_asset: asset_out, + } + } + + fn on_trade_fee(source: &AccountId, trader: &AccountId, fee_asset: AssetId, fee: Balance) -> OnFeeResult { + let unused= Referrals::process_trade_fee(*source, *trader, fee_asset, fee).expect("to success"); + OnFeeResult{unused} + } +} \ No newline at end of file diff --git a/pallets/referrals/src/tests/mock_amm.rs b/pallets/referrals/src/tests/mock_amm.rs new file mode 100644 index 000000000..cbdce2a6e --- /dev/null +++ b/pallets/referrals/src/tests/mock_amm.rs @@ -0,0 +1,63 @@ + + +use frame_support::pallet_prelude::*; +pub use pallet::*; + +type Balance = u128; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_system::{ensure_signed, pallet_prelude::OriginFor}; + + #[pallet::pallet] + #[pallet::generate_store(pub (crate) trait Store)] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type AssetId: frame_support::traits::tokens::AssetId + MaybeSerializeDeserialize; + + type TradeHooks: Hooks; + } + + #[pallet::event] + #[pallet::generate_deposit(pub (crate) fn deposit_event)] + pub enum Event { + } + + #[pallet::error] + pub enum Error { + } + + #[pallet::call] + impl Pallet { + + #[pallet::call_index(0)] + #[pallet::weight(Weight::zero())] + pub fn trade(origin: OriginFor, asset_in: T::AssetId, asset_out: T::AssetId, amount: Balance ) -> DispatchResult{ + let who= ensure_signed(origin)?; + let result = T::TradeHooks::simulate_trade(&who, asset_in, asset_out, amount); + let fee_result = T::TradeHooks::on_trade_fee(&who, &who, result.fee_asset, result.fee); + Ok(()) + } + } +} + +pub struct TradeResult { + pub amount_in: Balance, + pub amount_out: Balance, + pub fee: Balance, + pub fee_asset: AssetId, +} + +pub struct OnFeeResult { + pub(crate) unused: Balance, +} + +pub trait Hooks{ + fn simulate_trade(who: &AccountId, asset_in: AssetId, asset_out: AssetId, amount: Balance) -> TradeResult; + fn on_trade_fee(source: &AccountId, trader: &AccountId, fee_asset: AssetId, fee: Balance) -> OnFeeResult; +} \ No newline at end of file diff --git a/pallets/referrals/src/tests/register.rs b/pallets/referrals/src/tests/register.rs index 1f9e9db94..43f3d202a 100644 --- a/pallets/referrals/src/tests/register.rs +++ b/pallets/referrals/src/tests/register.rs @@ -154,7 +154,7 @@ fn register_code_should_emit_event_when_successful() { } #[test] -fn singer_should_pay_the_registration_fee() { +fn signer_should_pay_the_registration_fee() { ExtBuilder::default().build().execute_with(|| { // Arrange let code = b"BALLS69".to_vec(); diff --git a/pallets/referrals/src/tests/trade_fee.rs b/pallets/referrals/src/tests/trade_fee.rs new file mode 100644 index 000000000..e8c24d889 --- /dev/null +++ b/pallets/referrals/src/tests/trade_fee.rs @@ -0,0 +1,60 @@ +use crate::tests::*; +use pretty_assertions::assert_eq; + +#[test] +fn process_trade_should_increased_accrued() { + ExtBuilder::default() + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_rewards(vec![(BOB, 1_000_000_000_000)]) + .build() + .execute_with(|| { + // ARRANGE + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + // Act + assert_ok!(MockAmm::trade( + RuntimeOrigin::signed(ALICE), + HDX, + DAI, + 1_000_000_000_000, + )); + // Assert + let rewards = Accrued::::get(&HDX, &ALICE); + assert_eq!(rewards, 5_000_000_000_000_000); + }); +} + +#[test] +fn process_trade_should_not_increase_accrued_when_trader_does_not_have_linked_account() { + ExtBuilder::default() + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_rewards(vec![(BOB, 1_000_000_000_000)]) + .build() + .execute_with(|| { + // ARRANGE + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE + )); + // Assert + assert_ok!(MockAmm::trade( + RuntimeOrigin::signed(ALICE), + HDX, + DAI, + 1_000_000_000_000, + )); + let rewards = Accrued::::get(&HDX, &ALICE); + assert_eq!(rewards, 0); + }); +} From 016f2f32811a588c1edbea84f4a86c18ad27d53e Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 28 Nov 2023 12:10:45 +0100 Subject: [PATCH 22/90] rework rewards accumulation using shares --- pallets/referrals/src/lib.rs | 118 +++++++--- pallets/referrals/src/tests.rs | 91 +++++--- pallets/referrals/src/tests/claim.rs | 24 +- pallets/referrals/src/tests/convert.rs | 284 +---------------------- pallets/referrals/src/tests/mock_amm.rs | 40 ++-- pallets/referrals/src/tests/trade_fee.rs | 138 +++++++++-- pallets/referrals/src/weights.rs | 9 + 7 files changed, 321 insertions(+), 383 deletions(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index eeb4ce3db..404736071 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -26,9 +26,11 @@ pub mod traits; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::pallet_prelude::{DispatchResult, Get}; +use frame_support::sp_runtime::FixedPointNumber; use frame_support::traits::fungibles::Transfer; -use frame_support::{transactional, RuntimeDebug}; +use frame_support::{ensure, transactional, RuntimeDebug}; use frame_system::{ensure_signed, pallet_prelude::OriginFor}; +use hydradx_traits::pools::SpotPriceProvider; use orml_traits::GetByKey; use sp_core::bounded::BoundedVec; use sp_runtime::traits::AccountIdConversion; @@ -45,7 +47,7 @@ const MIN_CODE_LENGTH: usize = 3; use scale_info::TypeInfo; -#[derive(Hash, Clone, Default, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +#[derive(Hash, Clone, Copy, Default, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] pub enum Level { #[default] Novice, @@ -77,6 +79,7 @@ pub mod pallet { use crate::traits::Convert; use frame_support::pallet_prelude::*; use frame_support::sp_runtime::ArithmeticError; + use frame_support::sp_runtime::FixedU128; use frame_support::traits::fungibles::{Inspect, Transfer}; use frame_support::PalletId; use sp_runtime::traits::Zero; @@ -90,6 +93,9 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Origin that can set asset tier reward percentages. + type AuthorityOrigin: EnsureOrigin; + /// Asset type type AssetId: frame_support::traits::tokens::AssetId + MaybeSerializeDeserialize; @@ -99,6 +105,9 @@ pub mod pallet { /// Support for asset conversion. type Convert: Convert; + /// Price provider to use for shares calculation. + type SpotPriceProvider: SpotPriceProvider; + /// ID of an asset that is used to distribute rewards in. #[pallet::constant] type RewardAsset: Get; @@ -136,18 +145,15 @@ pub mod pallet { #[pallet::getter(fn linked_referral_account)] pub(super) type LinkedAccounts = StorageMap<_, Blake2_128Concat, T::AccountId, T::AccountId>; - /// Accrued amounts of an asset from trading activity. - /// Maps an amount to asset and account. This amount needs to be converted to native currency prior to claiming as a reward. + /// Shares per account. #[pallet::storage] - #[pallet::getter(fn accrued)] - pub(super) type Accrued = - StorageDoubleMap<_, Blake2_128Concat, T::AssetId, Blake2_128Concat, T::AccountId, Balance, ValueQuery>; + #[pallet::getter(fn account_shares)] + pub(super) type Shares = StorageMap<_, Blake2_128Concat, T::AccountId, Balance, ValueQuery>; - /// Accumulated rewards - /// Reward amount of native asset per account. + /// Total share issuance. #[pallet::storage] - #[pallet::getter(fn account_rewards)] - pub(super) type Rewards = StorageMap<_, Blake2_128Concat, T::AccountId, Balance, ValueQuery>; + #[pallet::getter(fn total_shares)] + pub(super) type TotalShares = StorageValue<_, Balance, ValueQuery>; /// Referer level and total accumulated rewards over time. /// Maps referrer account to (Level, Balance). Level indicates current reward tier and Balance is used to unlock next tier level. @@ -185,6 +191,12 @@ pub mod pallet { who: T::AccountId, amount: Balance, }, + TierRewardSet { + asset_id: T::AssetId, + level: Level, + referrer: Permill, + trader: Permill, + }, } #[pallet::error] @@ -199,8 +211,8 @@ pub mod pallet { ZeroAmount, /// Linking an account to the same referral account is not allowed. LinkNotAllowed, - /// More rewards have been distributed than allowed after conversion. This is a bug. - IncorrectRewardDistribution, + /// Calculated rewards are more than the fee amount. This can happen if percentage are incorrectly set. + IncorrectRewardCalculation, } #[pallet::call] @@ -297,10 +309,11 @@ pub mod pallet { let asset_balance = T::Currency::balance(asset_id, &Self::pot_account_id()); ensure!(asset_balance > 0, Error::::ZeroAmount); - let total_reward_amount = + let total_reward_asset = T::Convert::convert(Self::pot_account_id(), asset_id, T::RewardAsset::get(), asset_balance)?; // Keep track of amount rewarded, in case of a a leftover (due to rounding) + /* let mut rewarded: Balance = 0; for (account, amount) in Accrued::::drain_prefix(asset_id) { // We need to figure out how much of the reward should be assigned to the individual recipients. @@ -341,23 +354,13 @@ pub mod pallet { // Should not really happy, but let's be safe and ensure that we have not distributed more than allowed. ensure!(rewarded <= total_reward_amount, Error::::IncorrectRewardDistribution); - // Due to rounding, there can be something left, let's just transfer it to treasury. - let remainder = total_reward_amount.saturating_sub(rewarded); - if remainder > 0 { - T::Currency::transfer( - T::RewardAsset::get(), - &Self::pot_account_id(), - &T::RegistrationFee::get().2, - remainder, - true, - )?; - } + */ Self::deposit_event(Event::Converted { from: asset_id, to: T::RewardAsset::get(), amount: asset_balance, - received: total_reward_amount, + received: total_reward_asset, }); Ok(()) @@ -373,9 +376,43 @@ pub mod pallet { #[pallet::weight(::WeightInfo::claim_rewards())] pub fn claim_rewards(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - let amount = Rewards::::take(&who); - T::Currency::transfer(T::RewardAsset::get(), &Self::pot_account_id(), &who, amount, true)?; - Self::deposit_event(Event::Claimed { who, amount }); + //let amount = Rewards::::take(&who); + //T::Currency::transfer(T::RewardAsset::get(), &Self::pot_account_id(), &who, amount, true)?; + //Self::deposit_event(Event::Claimed { who, amount }); + Ok(()) + } + + /// Set asset tier reward percentages + /// + /// /// Parameters: + /// - `origin`: + /// - `level`: + /// - `referrer`: + /// - `trader`: + /// + /// Emits `Claimed` event when successful. + #[pallet::call_index(4)] + #[pallet::weight(::WeightInfo::set_reward_percentage())] + pub fn set_reward_percentage( + origin: OriginFor, + asset_id: T::AssetId, + level: Level, + referrer: Permill, + trader: Permill, + ) -> DispatchResult { + T::AuthorityOrigin::ensure_origin(origin)?; + + //TODO: ensure that total percentage is not greater than 100% + + AssetTier::::mutate(asset_id, level, |v| { + *v = Some(Tier { referrer, trader }); + }); + Self::deposit_event(Event::TierRewardSet { + asset_id, + level, + referrer, + trader, + }); Ok(()) } } @@ -419,21 +456,28 @@ impl Pallet { return Ok(amount); }; - // TODO: question: percentage from the total amount for both ? or trader takes percentage of the referrer_reward ? + let Some(price) = T::SpotPriceProvider::spot_price(T::RewardAsset::get(), asset_id) else { + // no price, no fun. + return Ok(amount); + }; + let referrer_reward = tier.referrer.mul_floor(amount); let trader_reward = tier.trader.mul_floor(amount); - let total_taken = referrer_reward.saturating_add(trader_reward); + ensure!(total_taken <= amount, Error::::IncorrectRewardCalculation); T::Currency::transfer(asset_id, &source, &Self::pot_account_id(), total_taken, true)?; - // Update each accrued rewards - Accrued::::mutate(asset_id, ref_account, |v| { - *v = v.saturating_add(referrer_reward); + let referrer_shares = price.saturating_mul_int(referrer_reward); + let trader_shares = price.saturating_mul_int(trader_reward); + TotalShares::::mutate(|v| { + *v = v.saturating_add(referrer_shares.saturating_add(trader_shares)); }); - Accrued::::mutate(asset_id, trader, |v| { - *v = v.saturating_add(trader_reward); + Shares::::mutate(ref_account, |v| { + *v = v.saturating_add(referrer_shares); + }); + Shares::::mutate(trader, |v| { + *v = v.saturating_add(trader_shares); }); - Ok(amount.saturating_sub(total_taken)) } } diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index dc782957a..f352732ac 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -18,9 +18,9 @@ mod claim; mod convert; mod link; +mod mock_amm; mod register; mod trade_fee; -mod mock_amm; use crate as pallet_referrals; use crate::*; @@ -39,13 +39,14 @@ use frame_support::{ }; use sp_core::H256; +use crate::tests::mock_amm::{Hooks, OnFeeResult, TradeResult}; use crate::traits::Convert; use frame_support::{assert_noop, assert_ok}; +use frame_system::EnsureRoot; use orml_traits::MultiCurrency; use orml_traits::{parameter_type_with_key, MultiCurrencyExtended}; -use sp_runtime::{DispatchError, FixedPointNumber, FixedU128}; use sp_runtime::traits::One; -use crate::tests::mock_amm::{Hooks, OnFeeResult, TradeResult}; +use sp_runtime::{DispatchError, FixedPointNumber, FixedU128}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -101,9 +102,11 @@ impl GetByKey> for Volume { impl Config for Test { type RuntimeEvent = RuntimeEvent; + type AuthorityOrigin = EnsureRoot; type AssetId = AssetId; type Currency = Tokens; type Convert = AssetConvert; + type SpotPriceProvider = SpotPrice; type RewardAsset = RewardAsset; type PalletId = RefarralPalletId; type RegistrationFee = RegistrationFee; @@ -167,8 +170,8 @@ impl mock_amm::pallet::Config for Test { pub struct ExtBuilder { endowed_accounts: Vec<(AccountId, AssetId, Balance)>, - trades: Vec<(AccountId, AssetId, Balance)>, - rewards: Vec<(AccountId, Balance)>, + shares: Vec<(AccountId, Balance)>, + tiers: Vec<(AssetId, Level, Tier)>, } impl Default for ExtBuilder { @@ -178,20 +181,25 @@ impl Default for ExtBuilder { }); Self { endowed_accounts: vec![(ALICE, HDX, INITIAL_ALICE_BALANCE)], - trades: vec![], - rewards: vec![], + shares: vec![], + tiers: vec![], } } } impl ExtBuilder { - pub fn with_trade_activity(mut self, trades: Vec<(AccountId, AssetId, Balance)>) -> Self { - self.trades.extend(trades); + pub fn with_endowed_accounts(mut self, accounts: Vec<(AccountId, AssetId, Balance)>) -> Self { + self.endowed_accounts.extend(accounts); self } - pub fn with_rewards(mut self, rewards: Vec<(AccountId, Balance)>) -> Self { - self.rewards.extend(rewards); + pub fn with_shares(mut self, shares: Vec<(AccountId, Balance)>) -> Self { + self.shares.extend(shares); + self + } + + pub fn with_tiers(mut self, shares: Vec<(AssetId, Level, Tier)>) -> Self { + self.tiers.extend(shares); self } @@ -199,7 +207,7 @@ impl ExtBuilder { CONVERSION_RATE.with(|v| { let mut m = v.borrow_mut(); m.insert(pair, price); - m.insert((pair.1,pair.0), FixedU128::one().div(price)); + m.insert((pair.1, pair.0), FixedU128::one().div(price)); }); self } @@ -227,16 +235,18 @@ impl ExtBuilder { let mut r: sp_io::TestExternalities = t.into(); r.execute_with(|| { - for (acc, asset, amount) in self.trades.iter() { - Accrued::::insert(asset, acc, amount); - Tokens::update_balance(*asset, &Pallet::::pot_account_id(), *amount as i128).unwrap(); + for (acc, amount) in self.shares.iter() { + Shares::::insert(acc, amount); + TotalShares::::mutate(|v| { + *v = v.saturating_add(*amount); + }); + Tokens::update_balance(HDX, &Pallet::::pot_account_id(), *amount as i128).unwrap(); } }); r.execute_with(|| { - for (acc, amount) in self.rewards.iter() { - Rewards::::insert(acc, amount); - Tokens::update_balance(HDX, &Pallet::::pot_account_id(), *amount as i128).unwrap(); + for (asset, level, tier) in self.tiers.iter() { + AssetTier::::insert(asset, level, tier); } }); @@ -280,28 +290,53 @@ macro_rules! assert_balance { }}; } - pub struct AmmTrader; -const TRADE_PERCENTAGE: Permill = Permill::one(); +const TRADE_PERCENTAGE: Permill = Permill::from_percent(1); impl Hooks for AmmTrader { - fn simulate_trade(who: &AccountId, asset_in: AssetId, asset_out: AssetId, amount: Balance) -> TradeResult { + fn simulate_trade( + who: &AccountId, + asset_in: AssetId, + asset_out: AssetId, + amount: Balance, + ) -> Result, DispatchError> { let price = CONVERSION_RATE .with(|v| v.borrow().get(&(asset_out, asset_in)).copied()) .expect("to have a price"); let amount_out = price.saturating_mul_int(amount); - let fee_amount = TRADE_PERCENTAGE.mul_ceil(amount_out); - TradeResult{ + dbg!(amount_out); + let fee_amount = TRADE_PERCENTAGE.mul_floor(amount_out); + dbg!(fee_amount); + Ok(TradeResult { amount_in: amount, amount_out, fee: fee_amount, fee_asset: asset_out, - } + }) } - fn on_trade_fee(source: &AccountId, trader: &AccountId, fee_asset: AssetId, fee: Balance) -> OnFeeResult { - let unused= Referrals::process_trade_fee(*source, *trader, fee_asset, fee).expect("to success"); - OnFeeResult{unused} + fn on_trade_fee( + source: &AccountId, + trader: &AccountId, + fee_asset: AssetId, + fee: Balance, + ) -> Result { + let unused = Referrals::process_trade_fee(*source, *trader, fee_asset, fee)?; + Ok(OnFeeResult { unused }) } -} \ No newline at end of file +} + +pub struct SpotPrice; + +impl SpotPriceProvider for SpotPrice { + type Price = FixedU128; + + fn pair_exists(asset_a: AssetId, asset_b: AssetId) -> bool { + unimplemented!() + } + + fn spot_price(asset_a: AssetId, asset_b: AssetId) -> Option { + CONVERSION_RATE.with(|v| v.borrow().get(&(asset_a, asset_b)).copied()) + } +} diff --git a/pallets/referrals/src/tests/claim.rs b/pallets/referrals/src/tests/claim.rs index 33b2ed0b0..c303bbbae 100644 --- a/pallets/referrals/src/tests/claim.rs +++ b/pallets/referrals/src/tests/claim.rs @@ -11,7 +11,7 @@ fn claim_rewards_should_work_when_amount_is_zero() { #[test] fn claim_rewards_should_transfer_rewards() { ExtBuilder::default() - .with_rewards(vec![(BOB, 1_000_000_000_000)]) + .with_shares(vec![(BOB, 1_000_000_000_000)]) .build() .execute_with(|| { assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB))); @@ -25,23 +25,37 @@ fn claim_rewards_should_transfer_rewards() { } #[test] -fn claim_rewards_should_reset_rewards_amount() { +fn claim_rewards_should_reset_shares_amount() { ExtBuilder::default() - .with_rewards(vec![(BOB, 1_000_000_000_000)]) + .with_shares(vec![(BOB, 1_000_000_000_000)]) .build() .execute_with(|| { // Act assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB))); // Assert - let rewards = Rewards::::get(&BOB); + let rewards = Shares::::get(&BOB); assert_eq!(rewards, 0); }); } +#[test] +fn claim_rewards_should_decreased_share_issuance() { + ExtBuilder::default() + .with_shares(vec![(BOB, 1_000_000_000_000)]) + .build() + .execute_with(|| { + // Act + assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB))); + // Assert + let total_shares = TotalShares::::get(); + assert_eq!(total_shares, 0); + }); +} + #[test] fn claim_rewards_should_emit_event_when_successful() { ExtBuilder::default() - .with_rewards(vec![(BOB, 1_000_000_000_000)]) + .with_shares(vec![(BOB, 1_000_000_000_000)]) .build() .execute_with(|| { // Act diff --git a/pallets/referrals/src/tests/convert.rs b/pallets/referrals/src/tests/convert.rs index e7c5271fc..27317a30e 100644 --- a/pallets/referrals/src/tests/convert.rs +++ b/pallets/referrals/src/tests/convert.rs @@ -12,38 +12,18 @@ fn convert_should_fail_when_amount_is_zero() { }); } -#[test] -fn convert_should_convert_all_asset_entries() { - ExtBuilder::default() - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) - .with_trade_activity(vec![(BOB, DAI, 1_000_000_000_000_000_000), (BOB, DOT, 10_000_000_000)]) - .build() - .execute_with(|| { - // Arrange - assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI,)); - // Assert - let length = Accrued::::iter_prefix(DAI).count(); - assert_eq!(length, 0); - let length = Accrued::::iter_prefix(DOT).count(); - assert_eq!(length, 1); - }); -} - #[test] fn convert_should_convert_all_asset_amount_when_successful() { ExtBuilder::default() + .with_endowed_accounts(vec![(Pallet::::pot_account_id(), DAI, 1_000_000_000_000_000_000)]) .with_conversion_price( (HDX, DAI), FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), ) - .with_trade_activity(vec![(BOB, DAI, 1_000_000_000_000_000_000)]) .build() .execute_with(|| { // Arrange - assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI,)); + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); // Assert let balance = Tokens::free_balance(DAI, &Pallet::::pot_account_id()); assert_eq!(balance, 0); @@ -52,35 +32,18 @@ fn convert_should_convert_all_asset_amount_when_successful() { }); } -#[test] -fn convert_should_update_account_rewards() { - ExtBuilder::default() - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) - .with_trade_activity(vec![(BOB, DAI, 1_000_000_000_000_000_000)]) - .build() - .execute_with(|| { - // Arrange - assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI,)); - // Assert - let rewards = Rewards::::get(&BOB); - assert_eq!(rewards, 1_000_000_000_000); - }); -} #[test] fn convert_should_emit_event_when_successful() { ExtBuilder::default() + .with_endowed_accounts(vec![(Pallet::::pot_account_id(), DAI, 1_000_000_000_000_000_000)]) .with_conversion_price( (HDX, DAI), FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), ) - .with_trade_activity(vec![(BOB, DAI, 1_000_000_000_000_000_000)]) .build() .execute_with(|| { // Arrange - assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI,)); + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); // Assert expect_events(vec![Event::Converted { from: DAI, @@ -91,242 +54,3 @@ fn convert_should_emit_event_when_successful() { .into()]); }); } - -#[test] -fn convert_should_distribute_native_amount_correct_when_there_is_multiple_entries() { - ExtBuilder::default() - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) - .with_trade_activity(vec![ - (BOB, DAI, 1_000_000_000_000_000_000), - (CHARLIE, DAI, 2_000_000_000_000_000_000), - ]) - .build() - .execute_with(|| { - // Arrange - assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI,)); - // Assert - let rewards = Rewards::::get(&BOB); - assert_eq!(rewards, 1_000_000_000_000); - - let rewards = Rewards::::get(&CHARLIE); - assert_eq!(rewards, 2_000_000_000_000); - }); -} - -#[test] -fn convert_should_transfer_leftovers_to_registration_fee_beneficiary() { - ExtBuilder::default() - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_333_333_333_333, 1_333_333_333_333_333_333), - ) - .with_trade_activity(vec![ - (BOB, DAI, 1_333_333_333_333_333_333), - (CHARLIE, DAI, 2_333_333_333_333_333_333), - ]) - .build() - .execute_with(|| { - // Arrange - assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI,)); - // Assert - let rewards = Rewards::::get(&BOB); - assert_eq!(rewards, 1_333_333_333_333); - - let rewards = Rewards::::get(&CHARLIE); - assert_eq!(rewards, 2_333_333_333_332); - - let treasury = Tokens::free_balance(HDX, &TREASURY); - assert_eq!(treasury, 1); - }); -} - -#[test] -fn convert_should_have_correct_reward_balance() { - ExtBuilder::default() - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_333_333_333_333, 1_333_333_333_333_333_333), - ) - .with_trade_activity(vec![ - (BOB, DAI, 1_333_333_333_333_333_333), - (CHARLIE, DAI, 2_333_333_333_333_333_333), - ]) - .build() - .execute_with(|| { - // Arrange - assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); - // Assert - let distributed = Rewards::::iter_values().sum::(); - let reserve = Tokens::free_balance(HDX, &Pallet::::pot_account_id()); - assert_eq!(reserve, distributed); - }); -} - -#[test] -fn convert_should_update_total_accumulated_rewards_for_referrer() { - ExtBuilder::default() - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_333_333_333_333, 1_333_333_333_333_333_333), - ) - .with_trade_activity(vec![ - (BOB, DAI, 1_333_333_333_333_333_333), - (CHARLIE, DAI, 2_333_333_333_333_333_333), - ]) - .build() - .execute_with(|| { - // Arrange - let code = b"BALLS69".to_vec(); - // Act - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - code.clone(), - BOB - )); - // Act - assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); - // Assert - let (_, total_rewards) = Referrer::::get(&BOB).unwrap(); - assert_eq!(total_rewards, 1_333_333_333_333); - }); -} - -#[test] -fn convert_should_update_referrer_level_when_next_tier_is_reached() { - let mut volumes: HashMap> = HashMap::new(); - volumes.insert(Level::Novice, Some(1_000_000_000_000)); - volumes.insert(Level::Advanced, Some(2_000_000_000_000)); - volumes.insert(Level::Expert, None); - - ExtBuilder::default() - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_333_333_333_333, 1_333_333_333_333_333_333), - ) - .with_trade_activity(vec![ - (BOB, DAI, 1_333_333_333_333_333_333), - (CHARLIE, DAI, 2_333_333_333_333_333_333), - ]) - .with_tier_volumes(volumes) - .build() - .execute_with(|| { - // Arrange - let code = b"BALLS69".to_vec(); - // Act - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - code.clone(), - BOB - )); - // Act - assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); - // Assert - let (level, total_rewards) = Referrer::::get(&BOB).unwrap(); - assert_eq!(total_rewards, 1_333_333_333_333); - assert_eq!(level, Level::Advanced); - }); -} - -#[test] -fn convert_should_update_referrer_level_to_top_one_when_next_tier_is_reached_and_when_one_level_has_to_be_skipper() { - let mut volumes: HashMap> = HashMap::new(); - volumes.insert(Level::Novice, Some(1_000_000_000_000)); - volumes.insert(Level::Advanced, Some(2_000_000_000_000)); - volumes.insert(Level::Expert, None); - - ExtBuilder::default() - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) - .with_trade_activity(vec![(BOB, DAI, 2_333_333_333_333_333_333)]) - .with_tier_volumes(volumes) - .build() - .execute_with(|| { - // Arrange - let code = b"BALLS69".to_vec(); - // Act - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - code.clone(), - BOB - )); - // Act - assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); - // Assert - let (level, total_rewards) = Referrer::::get(&BOB).unwrap(); - assert_eq!(total_rewards, 2_333_333_333_333); - assert_eq!(level, Level::Expert); - }); -} - -#[test] -fn convert_should_only_update_total_amount_when_top_tier_is_reached() { - let mut volumes: HashMap> = HashMap::new(); - volumes.insert(Level::Novice, Some(1_000_000_000_000)); - volumes.insert(Level::Advanced, Some(2_000_000_000_000)); - volumes.insert(Level::Expert, None); - - ExtBuilder::default() - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) - .with_conversion_price( - (HDX, DOT), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) - .with_conversion_price( - (HDX, 1234), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) - .with_trade_activity(vec![ - (BOB, DAI, 1_333_333_333_333_333_333), - (BOB, DOT, 2_333_333_333_333_333_333), - (BOB, 1234, 1_000_000_000_000_000_000), - ]) - .with_tier_volumes(volumes) - .build() - .execute_with(|| { - // Arrange - let code = b"BALLS69".to_vec(); - // Act - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - code.clone(), - BOB - )); - // Act - assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); - assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DOT)); - assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), 1234)); - // Assert - let (level, total_rewards) = Referrer::::get(&BOB).unwrap(); - assert_eq!(total_rewards, 4_666_666_666_666); - assert_eq!(level, Level::Expert); - }); -} - -#[test] -fn convert_should_not_update_total_rewards_when_account_is_not_referral_account() { - ExtBuilder::default() - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_333_333_333_333, 1_333_333_333_333_333_333), - ) - .with_trade_activity(vec![ - (BOB, DAI, 1_333_333_333_333_333_333), - (CHARLIE, DAI, 2_333_333_333_333_333_333), - ]) - .build() - .execute_with(|| { - // Act - assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); - // Assert - let entry = Referrer::::get(&BOB); - assert_eq!(entry, None); - }); -} diff --git a/pallets/referrals/src/tests/mock_amm.rs b/pallets/referrals/src/tests/mock_amm.rs index cbdce2a6e..3a5f56412 100644 --- a/pallets/referrals/src/tests/mock_amm.rs +++ b/pallets/referrals/src/tests/mock_amm.rs @@ -1,5 +1,3 @@ - - use frame_support::pallet_prelude::*; pub use pallet::*; @@ -25,22 +23,24 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub (crate) fn deposit_event)] - pub enum Event { - } + pub enum Event {} #[pallet::error] - pub enum Error { - } + pub enum Error {} #[pallet::call] impl Pallet { - #[pallet::call_index(0)] #[pallet::weight(Weight::zero())] - pub fn trade(origin: OriginFor, asset_in: T::AssetId, asset_out: T::AssetId, amount: Balance ) -> DispatchResult{ - let who= ensure_signed(origin)?; - let result = T::TradeHooks::simulate_trade(&who, asset_in, asset_out, amount); - let fee_result = T::TradeHooks::on_trade_fee(&who, &who, result.fee_asset, result.fee); + pub fn trade( + origin: OriginFor, + asset_in: T::AssetId, + asset_out: T::AssetId, + amount: Balance, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let result = T::TradeHooks::simulate_trade(&who, asset_in, asset_out, amount)?; + let fee_result = T::TradeHooks::on_trade_fee(&who, &who, result.fee_asset, result.fee)?; Ok(()) } } @@ -57,7 +57,17 @@ pub struct OnFeeResult { pub(crate) unused: Balance, } -pub trait Hooks{ - fn simulate_trade(who: &AccountId, asset_in: AssetId, asset_out: AssetId, amount: Balance) -> TradeResult; - fn on_trade_fee(source: &AccountId, trader: &AccountId, fee_asset: AssetId, fee: Balance) -> OnFeeResult; -} \ No newline at end of file +pub trait Hooks { + fn simulate_trade( + who: &AccountId, + asset_in: AssetId, + asset_out: AssetId, + amount: Balance, + ) -> Result, DispatchError>; + fn on_trade_fee( + source: &AccountId, + trader: &AccountId, + fee_asset: AssetId, + fee: Balance, + ) -> Result; +} diff --git a/pallets/referrals/src/tests/trade_fee.rs b/pallets/referrals/src/tests/trade_fee.rs index e8c24d889..d49c4180c 100644 --- a/pallets/referrals/src/tests/trade_fee.rs +++ b/pallets/referrals/src/tests/trade_fee.rs @@ -2,50 +2,152 @@ use crate::tests::*; use pretty_assertions::assert_eq; #[test] -fn process_trade_should_increased_accrued() { +fn process_trade_fee_should_increased_referrer_shares() { ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, DAI, 2_000_000_000_000_000_000)]) .with_conversion_price( (HDX, DAI), FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), ) - .with_rewards(vec![(BOB, 1_000_000_000_000)]) + .with_tiers(vec![( + DAI, + Level::Novice, + Tier { + referrer: Permill::from_percent(50), + trader: Permill::zero(), + }, + )]) .build() .execute_with(|| { // ARRANGE assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - ALICE + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE )); assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); // Act - assert_ok!(MockAmm::trade( + assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000,)); + // Assert + let shares = Shares::::get(&ALICE); + assert_eq!(shares, 5_000_000_000); + }); +} + +#[test] +fn process_trade_fee_should_increased_trader_shares() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, DAI, 2_000_000_000_000_000_000)]) + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_tiers(vec![( + DAI, + Level::Novice, + Tier { + referrer: Permill::from_percent(50), + trader: Permill::from_percent(20), + }, + )]) + .build() + .execute_with(|| { + // ARRANGE + assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), - HDX, - DAI, - 1_000_000_000_000, + b"BALLS69".to_vec(), + ALICE )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + // Act + assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000,)); // Assert - let rewards = Accrued::::get(&HDX, &ALICE); - assert_eq!(rewards, 5_000_000_000_000_000); + let shares = Shares::::get(&BOB); + assert_eq!(shares, 2_000_000_000); }); } #[test] -fn process_trade_should_not_increase_accrued_when_trader_does_not_have_linked_account() { +fn process_trade_fee_should_increased_total_share_issuance() { ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, DAI, 2_000_000_000_000_000_000)]) .with_conversion_price( (HDX, DAI), FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), ) - .with_rewards(vec![(BOB, 1_000_000_000_000)]) + .with_tiers(vec![( + DAI, + Level::Novice, + Tier { + referrer: Permill::from_percent(50), + trader: Permill::from_percent(20), + }, + )]) .build() .execute_with(|| { // ARRANGE assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - ALICE + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + // Act + assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000,)); + // Assert + let shares = TotalShares::::get(); + assert_eq!(shares, 2_000_000_000 + 5_000_000_000); + }); +} + +#[test] +fn process_trade_fee_should_fail_when_taken_amount_is_greated_than_fee_amount() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, DAI, 2_000_000_000_000_000_000)]) + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_tiers(vec![( + DAI, + Level::Novice, + Tier { + referrer: Permill::from_percent(50), + trader: Permill::from_percent(70), + }, + )]) + .build() + .execute_with(|| { + // ARRANGE + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + // Act + assert_noop!( + MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000,), + Error::::IncorrectRewardCalculation + ); + }); +} + +#[test] +fn process_trade_should_not_increase_shares_when_trader_does_not_have_linked_account() { + ExtBuilder::default() + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_shares(vec![(BOB, 1_000_000_000_000)]) + .build() + .execute_with(|| { + // ARRANGE + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE )); // Assert assert_ok!(MockAmm::trade( @@ -54,7 +156,7 @@ fn process_trade_should_not_increase_accrued_when_trader_does_not_have_linked_ac DAI, 1_000_000_000_000, )); - let rewards = Accrued::::get(&HDX, &ALICE); - assert_eq!(rewards, 0); + let shares = Shares::::get(&ALICE); + assert_eq!(shares, 0); }); } diff --git a/pallets/referrals/src/weights.rs b/pallets/referrals/src/weights.rs index c174db4b2..e2b98244e 100644 --- a/pallets/referrals/src/weights.rs +++ b/pallets/referrals/src/weights.rs @@ -52,6 +52,7 @@ pub trait WeightInfo { fn link_code() -> Weight; fn convert() -> Weight; fn claim_rewards() -> Weight; + fn set_reward_percentage() -> Weight; } /// Weights for pallet_bonds using the hydraDX node and recommended hardware. @@ -73,6 +74,10 @@ impl WeightInfo for HydraWeight { fn claim_rewards() -> Weight { Weight::zero() } + + fn set_reward_percentage() -> Weight { + Weight::zero() + } } // For backwards compatibility and tests @@ -92,4 +97,8 @@ impl WeightInfo for () { fn claim_rewards() -> Weight { Weight::zero() } + + fn set_reward_percentage() -> Weight { + Weight::zero() + } } From c4629bec36277c191ba250a1953882c63097f0e6 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 28 Nov 2023 13:19:01 +0100 Subject: [PATCH 23/90] claim tests reworked --- pallets/referrals/src/lib.rs | 12 ++ pallets/referrals/src/tests.rs | 6 +- pallets/referrals/src/tests/claim.rs | 145 ++++++++++++++++++++--- pallets/referrals/src/tests/flow.rs | 143 ++++++++++++++++++++++ pallets/referrals/src/tests/trade_fee.rs | 33 ++++++ 5 files changed, 321 insertions(+), 18 deletions(-) create mode 100644 pallets/referrals/src/tests/flow.rs diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 404736071..f8d03eae6 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -169,6 +169,12 @@ pub mod pallet { pub(super) type AssetTier = StorageDoubleMap<_, Blake2_128Concat, T::AssetId, Blake2_128Concat, Level, Tier, OptionQuery>; + /// Information about assets that are currently in the rewards pot. + /// Used to easily determine list of assets that need to be converted. + #[pallet::storage] + #[pallet::getter(fn assets)] + pub(super) type Assets = StorageMap<_, Blake2_128Concat, T::AssetId, ()>; + #[pallet::event] #[pallet::generate_deposit(pub(crate) fn deposit_event)] pub enum Event { @@ -376,6 +382,11 @@ pub mod pallet { #[pallet::weight(::WeightInfo::claim_rewards())] pub fn claim_rewards(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; + // 1. convert all current resreves in the pot + // 2. calculate who portion using shares. + // 3. reset who shares + // 4. decreause total shares + // 5. update level if referral account //let amount = Rewards::::take(&who); //T::Currency::transfer(T::RewardAsset::get(), &Self::pot_account_id(), &who, amount, true)?; //Self::deposit_event(Event::Claimed { who, amount }); @@ -478,6 +489,7 @@ impl Pallet { Shares::::mutate(trader, |v| { *v = v.saturating_add(trader_shares); }); + Assets::::insert(asset_id, ()); Ok(amount.saturating_sub(total_taken)) } } diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index f352732ac..480a8ef36 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -17,6 +17,7 @@ mod claim; mod convert; +mod flow; mod link; mod mock_amm; mod register; @@ -332,11 +333,14 @@ pub struct SpotPrice; impl SpotPriceProvider for SpotPrice { type Price = FixedU128; - fn pair_exists(asset_a: AssetId, asset_b: AssetId) -> bool { + fn pair_exists(_asset_a: AssetId, _asset_b: AssetId) -> bool { unimplemented!() } fn spot_price(asset_a: AssetId, asset_b: AssetId) -> Option { + if asset_a == asset_b { + return Some(FixedU128::one()); + } CONVERSION_RATE.with(|v| v.borrow().get(&(asset_a, asset_b)).copied()) } } diff --git a/pallets/referrals/src/tests/claim.rs b/pallets/referrals/src/tests/claim.rs index c303bbbae..c4634fb4b 100644 --- a/pallets/referrals/src/tests/claim.rs +++ b/pallets/referrals/src/tests/claim.rs @@ -9,53 +9,83 @@ fn claim_rewards_should_work_when_amount_is_zero() { } #[test] -fn claim_rewards_should_transfer_rewards() { +fn claim_rewards_should_convert_all_assets() { ExtBuilder::default() - .with_shares(vec![(BOB, 1_000_000_000_000)]) + .with_endowed_accounts(vec![ + (Pallet::::pot_account_id(), DAI, 3_000_000_000_000_000_000), + (Pallet::::pot_account_id(), DOT, 4_000_000_000_000), + ]) + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_conversion_price( + (HDX, DOT), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000), + ) .build() .execute_with(|| { assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB))); // Assert - let reserve = Tokens::free_balance(HDX, &BOB); - assert_eq!(reserve, 1_000_000_000_000); + let acc = Pallet::::pot_account_id(); + let reserve = Tokens::free_balance(HDX, &acc); + assert_eq!(reserve, 7_000_000_000_000); + let reserve = Tokens::free_balance(DOT, &acc); + assert_eq!(reserve, 0); + let reserve = Tokens::free_balance(DAI, &acc); + assert_eq!(reserve, 0); + }); +} +#[test] +fn claim_rewards_should_calculate_correct_portion_when_claimed() { + ExtBuilder::default() + .with_endowed_accounts(vec![(Pallet::::pot_account_id(), HDX, 20_000_000_000_000)]) + .with_shares(vec![(BOB, 5_000_000_000_000), (ALICE, 15_000_000_000_000)]) + .build() + .execute_with(|| { + assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB))); + // Assert + let reserve = Tokens::free_balance(HDX, &BOB); + assert_eq!(reserve, 5_000_000_000_000); let reserve = Tokens::free_balance(HDX, &Pallet::::pot_account_id()); - assert_eq!(reserve, 0); + assert_eq!(reserve, 15_000_000_000_000); }); } #[test] -fn claim_rewards_should_reset_shares_amount() { +fn claim_rewards_should_decrease_total_shares_issuance_when_claimed() { ExtBuilder::default() - .with_shares(vec![(BOB, 1_000_000_000_000)]) + .with_endowed_accounts(vec![(Pallet::::pot_account_id(), HDX, 20_000_000_000_000)]) + .with_shares(vec![(BOB, 5_000_000_000_000), (ALICE, 15_000_000_000_000)]) .build() .execute_with(|| { - // Act assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB))); // Assert - let rewards = Shares::::get(&BOB); - assert_eq!(rewards, 0); + let reserve = TotalShares::::get(); + assert_eq!(reserve, 15_000_000_000_000); }); } #[test] -fn claim_rewards_should_decreased_share_issuance() { +fn claim_rewards_should_reset_account_shares_to_zero() { ExtBuilder::default() - .with_shares(vec![(BOB, 1_000_000_000_000)]) + .with_endowed_accounts(vec![(Pallet::::pot_account_id(), HDX, 20_000_000_000_000)]) + .with_shares(vec![(BOB, 5_000_000_000_000), (ALICE, 15_000_000_000_000)]) .build() .execute_with(|| { - // Act assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB))); // Assert - let total_shares = TotalShares::::get(); - assert_eq!(total_shares, 0); + let shares = Shares::::get(&BOB); + assert_eq!(shares, 0); }); } #[test] fn claim_rewards_should_emit_event_when_successful() { ExtBuilder::default() - .with_shares(vec![(BOB, 1_000_000_000_000)]) + .with_endowed_accounts(vec![(Pallet::::pot_account_id(), HDX, 20_000_000_000_000)]) + .with_shares(vec![(BOB, 5_000_000_000_000), (ALICE, 15_000_000_000_000)]) .build() .execute_with(|| { // Act @@ -63,8 +93,89 @@ fn claim_rewards_should_emit_event_when_successful() { // Assert expect_events(vec![Event::Claimed { who: BOB, - amount: 1_000_000_000_000, + amount: 5_000_000_000_000, } .into()]); }); } + +#[test] +fn claim_rewards_update_total_accumulated_for_referrer_account() { + ExtBuilder::default() + .with_endowed_accounts(vec![(Pallet::::pot_account_id(), HDX, 20_000_000_000_000)]) + .with_shares(vec![(BOB, 5_000_000_000_000), (ALICE, 15_000_000_000_000)]) + .build() + .execute_with(|| { + // ARRANGE + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + // Act + assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE))); + // Assert + let (level, total) = Referrer::::get(&ALICE).unwrap(); + assert_eq!(level, Level::Novice); + assert_eq!(total, 15_000_000_000_000); + }); +} + +#[test] +fn claim_rewards_should_increase_referrer_level_when_limit_is_reached() { + let mut volumes = HashMap::new(); + volumes.insert(Level::Novice, Some(10_000_000_000_000)); + volumes.insert(Level::Advanced, Some(20_000_000_000_000)); + volumes.insert(Level::Expert, None); + + ExtBuilder::default() + .with_endowed_accounts(vec![(Pallet::::pot_account_id(), HDX, 20_000_000_000_000)]) + .with_shares(vec![(BOB, 5_000_000_000_000), (ALICE, 15_000_000_000_000)]) + .with_tier_volumes(volumes) + .build() + .execute_with(|| { + // ARRANGE + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + // Act + assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE))); + // Assert + let (level, total) = Referrer::::get(&ALICE).unwrap(); + assert_eq!(level, Level::Advanced); + assert_eq!(total, 15_000_000_000_000); + }); +} + +#[test] +fn claim_rewards_should_increase_referrer_level_directly_to_top_tier_when_limit_is_reached() { + let mut volumes = HashMap::new(); + volumes.insert(Level::Novice, Some(10_000_000_000_000)); + volumes.insert(Level::Advanced, Some(13_000_000_000_000)); + volumes.insert(Level::Expert, None); + + ExtBuilder::default() + .with_endowed_accounts(vec![(Pallet::::pot_account_id(), HDX, 20_000_000_000_000)]) + .with_shares(vec![(BOB, 5_000_000_000_000), (ALICE, 15_000_000_000_000)]) + .with_tier_volumes(volumes) + .build() + .execute_with(|| { + // ARRANGE + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + // Act + assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE))); + // Assert + let (level, total) = Referrer::::get(&ALICE).unwrap(); + assert_eq!(level, Level::Expert); + assert_eq!(total, 15_000_000_000_000); + }); +} diff --git a/pallets/referrals/src/tests/flow.rs b/pallets/referrals/src/tests/flow.rs new file mode 100644 index 000000000..3179984ea --- /dev/null +++ b/pallets/referrals/src/tests/flow.rs @@ -0,0 +1,143 @@ +use crate::tests::*; +use pretty_assertions::assert_eq; + +#[test] +fn complete_referral_flow_should_work_as_expected() { + let mut volumes = HashMap::new(); + volumes.insert(Level::Novice, Some(10_000_000_000_000)); + volumes.insert(Level::Advanced, Some(13_000_000_000_000)); + volumes.insert(Level::Expert, None); + + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, DAI, 2_000_000_000_000_000_000), + (BOB, HDX, 10_000_000_000_000), + (CHARLIE, DOT, 2_000_000_000_000), + ]) + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_conversion_price((HDX, DOT), FixedU128::from_rational(1_000_000_000_000, 500_000_000_000)) + .with_tiers(vec![ + ( + DAI, + Level::Novice, + Tier { + referrer: Permill::from_float(0.005), + trader: Permill::from_float(0.002), + }, + ), + ( + DOT, + Level::Novice, + Tier { + referrer: Permill::from_float(0.005), + trader: Permill::from_float(0.002), + }, + ), + ( + DAI, + Level::Advanced, + Tier { + referrer: Permill::from_float(0.03), + trader: Permill::from_float(0.01), + }, + ), + ( + DOT, + Level::Advanced, + Tier { + referrer: Permill::from_float(0.03), + trader: Permill::from_float(0.01), + }, + ), + ( + HDX, + Level::Novice, + Tier { + referrer: Permill::from_float(0.002), + trader: Permill::from_float(0.001), + }, + ), + ( + HDX, + Level::Advanced, + Tier { + referrer: Permill::from_float(0.03), + trader: Permill::from_float(0.01), + }, + ), + ]) + .with_tier_volumes(volumes) + .build() + .execute_with(|| { + dbg!(Permill::from_float(0.005)); + // ARRANGE + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + assert_ok!(Referrals::link_code( + RuntimeOrigin::signed(CHARLIE), + b"BALLS69".to_vec() + )); + // TRADES + assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000)); + assert_ok!(MockAmm::trade( + RuntimeOrigin::signed(BOB), + DAI, + HDX, + 1_000_000_000_000_000_000 + )); + assert_ok!(MockAmm::trade( + RuntimeOrigin::signed(CHARLIE), + HDX, + DOT, + 1_000_000_000_000 + )); + + // Assert shares + let alice_shares = Shares::::get(&ALICE); + assert_eq!(alice_shares, 120_000_000); + let bob_shares = Shares::::get(&BOB); + assert_eq!(bob_shares, 30_000_000); + let charlie_shares = Shares::::get(&CHARLIE); + assert_eq!(charlie_shares, 20_000_000); + let total_shares = TotalShares::::get(); + assert_eq!(total_shares, alice_shares + bob_shares + charlie_shares); + + // CLAIMS + assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(CHARLIE),)); + // Assert charlie rewards + let shares = Shares::::get(&CHARLIE); + assert_eq!(shares , 0); + let total_shares = TotalShares::::get(); + assert_eq!(total_shares, alice_shares + bob_shares); + let charlie_balance = Tokens::free_balance(HDX, &CHARLIE); + assert_eq!(charlie_balance, 1); + + assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB),)); + // Assert BOB rewards + let shares = Shares::::get(&BOB); + assert_eq!(shares, 0); + let total_shares = TotalShares::::get(); + assert_eq!(total_shares, alice_shares); + let bob_balance = Tokens::free_balance(HDX, &BOB); + assert_eq!(bob_balance, 1); + + assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE),)); + // Assert ALICE rewards + let shares = Shares::::get(&ALICE); + assert_eq!(shares, 0); + let total_shares = TotalShares::::get(); + assert_eq!(total_shares, 0); + let alice_balance = Tokens::free_balance(HDX, &ALICE); + assert_eq!(alice_balance, 1); + let (level, total) = Referrer::::get(&ALICE).unwrap(); + assert_eq!(level, Level::Advanced); + assert_eq!(total, 123); + }); +} diff --git a/pallets/referrals/src/tests/trade_fee.rs b/pallets/referrals/src/tests/trade_fee.rs index d49c4180c..25280c426 100644 --- a/pallets/referrals/src/tests/trade_fee.rs +++ b/pallets/referrals/src/tests/trade_fee.rs @@ -160,3 +160,36 @@ fn process_trade_should_not_increase_shares_when_trader_does_not_have_linked_acc assert_eq!(shares, 0); }); } + +#[test] +fn process_trade_fee_should_add_asset_to_asset_list() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, DAI, 2_000_000_000_000_000_000)]) + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_tiers(vec![( + DAI, + Level::Novice, + Tier { + referrer: Permill::from_percent(50), + trader: Permill::from_percent(20), + }, + )]) + .build() + .execute_with(|| { + // ARRANGE + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + // Act + assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000,)); + // Assert + let asset = Assets::::get(DAI); + assert_eq!(asset, Some(())); + }); +} From e45e9aaa73b236c68e9c9e167767608ee462f8d2 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 28 Nov 2023 13:56:59 +0100 Subject: [PATCH 24/90] implement claim --- pallets/referrals/src/lib.rs | 97 +++++++++++------------- pallets/referrals/src/tests.rs | 26 ++++--- pallets/referrals/src/tests/claim.rs | 27 ++++++- pallets/referrals/src/tests/flow.rs | 19 ++--- pallets/referrals/src/tests/mock_amm.rs | 9 +-- pallets/referrals/src/tests/trade_fee.rs | 33 ++++++++ 6 files changed, 130 insertions(+), 81 deletions(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index f8d03eae6..ca402c0d5 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -195,7 +195,7 @@ pub mod pallet { }, Claimed { who: T::AccountId, - amount: Balance, + rewards: Balance, }, TierRewardSet { asset_id: T::AssetId, @@ -318,50 +318,6 @@ pub mod pallet { let total_reward_asset = T::Convert::convert(Self::pot_account_id(), asset_id, T::RewardAsset::get(), asset_balance)?; - // Keep track of amount rewarded, in case of a a leftover (due to rounding) - /* - let mut rewarded: Balance = 0; - for (account, amount) in Accrued::::drain_prefix(asset_id) { - // We need to figure out how much of the reward should be assigned to the individual recipients. - // Price = reward_asset_amount / asset_balance - // rewarded = price * account balance - let reward_amount = (total_reward_amount * amount) / asset_balance; // TODO: U256 and safe math please! - - Rewards::::try_mutate(account.clone(), |v| -> DispatchResult { - *v = v.checked_add(reward_amount).ok_or(ArithmeticError::Overflow)?; - - Referrer::::mutate(account, |d| { - // You might ask why do we need to have an OptionQuery here? it would be simpler to just have value query and update the account - // However, in Rewards, not all accounts are necessarily Referrer accounts. - // We heep there trader account which earn back some percentage of the fee. And for those, no levels! - if let Some((level, total)) = d { - *total = total.saturating_add(reward_amount); - - let next_tier = T::TierVolume::get(level); - if let Some(amount_needed) = next_tier { - if *total >= amount_needed { - *level = level.next_level(); - // let's check if we can skip two levels - let next_tier = T::TierVolume::get(level); - if let Some(amount_needed) = next_tier { - if *total >= amount_needed { - *level = level.next_level(); - } - } - } - } - } - }); - Ok(()) - })?; - rewarded = rewarded.saturating_add(reward_amount); - } - - // Should not really happy, but let's be safe and ensure that we have not distributed more than allowed. - ensure!(rewarded <= total_reward_amount, Error::::IncorrectRewardDistribution); - - */ - Self::deposit_event(Event::Converted { from: asset_id, to: T::RewardAsset::get(), @@ -382,14 +338,45 @@ pub mod pallet { #[pallet::weight(::WeightInfo::claim_rewards())] pub fn claim_rewards(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - // 1. convert all current resreves in the pot - // 2. calculate who portion using shares. - // 3. reset who shares - // 4. decreause total shares - // 5. update level if referral account - //let amount = Rewards::::take(&who); - //T::Currency::transfer(T::RewardAsset::get(), &Self::pot_account_id(), &who, amount, true)?; - //Self::deposit_event(Event::Claimed { who, amount }); + for (asset_id, _) in Assets::::drain() { + let asset_balance = T::Currency::balance(asset_id, &Self::pot_account_id()); + T::Convert::convert(Self::pot_account_id(), asset_id, T::RewardAsset::get(), asset_balance)?; + } + let shares = Shares::::take(&who); + if shares == Balance::zero() { + return Ok(()); + } + + let reward_reserve = T::Currency::balance(T::RewardAsset::get(), &Self::pot_account_id()); + let share_issuance = TotalShares::::get(); + let rewards = shares * reward_reserve / share_issuance; // TODO: use u256 and safe math, moritz! + T::Currency::transfer(T::RewardAsset::get(), &Self::pot_account_id(), &who, rewards, true)?; + + TotalShares::::mutate(|v| { + *v = v.saturating_sub(shares); + }); + + Referrer::::mutate(who.clone(), |v| { + if let Some((level, total)) = v { + *total = total.saturating_add(rewards); + + let next_tier = T::TierVolume::get(level); + if let Some(amount_needed) = next_tier { + if *total >= amount_needed { + *level = level.next_level(); + // let's check if we can skip two levels + let next_tier = T::TierVolume::get(level); + if let Some(amount_needed) = next_tier { + if *total >= amount_needed { + *level = level.next_level(); + } + } + } + } + } + }); + + Self::deposit_event(Event::Claimed { who, rewards }); Ok(()) } @@ -489,7 +476,9 @@ impl Pallet { Shares::::mutate(trader, |v| { *v = v.saturating_add(trader_shares); }); - Assets::::insert(asset_id, ()); + if asset_id != T::RewardAsset::get() { + Assets::::insert(asset_id, ()); + } Ok(amount.saturating_sub(total_taken)) } } diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 480a8ef36..811177d6c 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -40,7 +40,7 @@ use frame_support::{ }; use sp_core::H256; -use crate::tests::mock_amm::{Hooks, OnFeeResult, TradeResult}; +use crate::tests::mock_amm::{Hooks, TradeResult}; use crate::traits::Convert; use frame_support::{assert_noop, assert_ok}; use frame_system::EnsureRoot; @@ -173,6 +173,7 @@ pub struct ExtBuilder { endowed_accounts: Vec<(AccountId, AssetId, Balance)>, shares: Vec<(AccountId, Balance)>, tiers: Vec<(AssetId, Level, Tier)>, + assets: Vec, } impl Default for ExtBuilder { @@ -184,6 +185,7 @@ impl Default for ExtBuilder { endowed_accounts: vec![(ALICE, HDX, INITIAL_ALICE_BALANCE)], shares: vec![], tiers: vec![], + assets: vec![], } } } @@ -199,11 +201,14 @@ impl ExtBuilder { self } + pub fn with_assets(mut self, shares: Vec) -> Self { + self.assets.extend(shares); + self + } pub fn with_tiers(mut self, shares: Vec<(AssetId, Level, Tier)>) -> Self { self.tiers.extend(shares); self } - pub fn with_conversion_price(self, pair: (AssetId, AssetId), price: FixedU128) -> Self { CONVERSION_RATE.with(|v| { let mut m = v.borrow_mut(); @@ -241,7 +246,6 @@ impl ExtBuilder { TotalShares::::mutate(|v| { *v = v.saturating_add(*amount); }); - Tokens::update_balance(HDX, &Pallet::::pot_account_id(), *amount as i128).unwrap(); } }); @@ -250,7 +254,11 @@ impl ExtBuilder { AssetTier::::insert(asset, level, tier); } }); - + r.execute_with(|| { + for asset in self.assets.iter() { + Assets::::insert(asset, ()); + } + }); r.execute_with(|| { System::set_block_number(1); }); @@ -297,7 +305,7 @@ const TRADE_PERCENTAGE: Permill = Permill::from_percent(1); impl Hooks for AmmTrader { fn simulate_trade( - who: &AccountId, + _who: &AccountId, asset_in: AssetId, asset_out: AssetId, amount: Balance, @@ -306,9 +314,7 @@ impl Hooks for AmmTrader { .with(|v| v.borrow().get(&(asset_out, asset_in)).copied()) .expect("to have a price"); let amount_out = price.saturating_mul_int(amount); - dbg!(amount_out); let fee_amount = TRADE_PERCENTAGE.mul_floor(amount_out); - dbg!(fee_amount); Ok(TradeResult { amount_in: amount, amount_out, @@ -322,9 +328,9 @@ impl Hooks for AmmTrader { trader: &AccountId, fee_asset: AssetId, fee: Balance, - ) -> Result { - let unused = Referrals::process_trade_fee(*source, *trader, fee_asset, fee)?; - Ok(OnFeeResult { unused }) + ) -> Result<(), DispatchError> { + Referrals::process_trade_fee(*source, *trader, fee_asset, fee)?; + Ok(()) } } diff --git a/pallets/referrals/src/tests/claim.rs b/pallets/referrals/src/tests/claim.rs index c4634fb4b..044494ce5 100644 --- a/pallets/referrals/src/tests/claim.rs +++ b/pallets/referrals/src/tests/claim.rs @@ -15,6 +15,7 @@ fn claim_rewards_should_convert_all_assets() { (Pallet::::pot_account_id(), DAI, 3_000_000_000_000_000_000), (Pallet::::pot_account_id(), DOT, 4_000_000_000_000), ]) + .with_assets(vec![DAI, DOT]) .with_conversion_price( (HDX, DAI), FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), @@ -37,6 +38,30 @@ fn claim_rewards_should_convert_all_assets() { }); } +#[test] +fn claim_rewards_should_remove_assets_from_the_list() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Pallet::::pot_account_id(), DAI, 3_000_000_000_000_000_000), + (Pallet::::pot_account_id(), DOT, 4_000_000_000_000), + ]) + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_conversion_price( + (HDX, DOT), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000), + ) + .build() + .execute_with(|| { + assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB))); + // Assert + let count = Assets::::iter().count(); + assert_eq!(count, 0); + }); +} + #[test] fn claim_rewards_should_calculate_correct_portion_when_claimed() { ExtBuilder::default() @@ -93,7 +118,7 @@ fn claim_rewards_should_emit_event_when_successful() { // Assert expect_events(vec![Event::Claimed { who: BOB, - amount: 5_000_000_000_000, + rewards: 5_000_000_000_000, } .into()]); }); diff --git a/pallets/referrals/src/tests/flow.rs b/pallets/referrals/src/tests/flow.rs index 3179984ea..5150a8122 100644 --- a/pallets/referrals/src/tests/flow.rs +++ b/pallets/referrals/src/tests/flow.rs @@ -4,14 +4,16 @@ use pretty_assertions::assert_eq; #[test] fn complete_referral_flow_should_work_as_expected() { let mut volumes = HashMap::new(); - volumes.insert(Level::Novice, Some(10_000_000_000_000)); - volumes.insert(Level::Advanced, Some(13_000_000_000_000)); + volumes.insert(Level::Novice, Some(100_000_000)); + volumes.insert(Level::Advanced, Some(200_000_000)); volumes.insert(Level::Expert, None); + let bob_initial_hdx = 10_000_000_000_000; + ExtBuilder::default() .with_endowed_accounts(vec![ (BOB, DAI, 2_000_000_000_000_000_000), - (BOB, HDX, 10_000_000_000_000), + (BOB, HDX, bob_initial_hdx), (CHARLIE, DOT, 2_000_000_000_000), ]) .with_conversion_price( @@ -72,7 +74,6 @@ fn complete_referral_flow_should_work_as_expected() { .with_tier_volumes(volumes) .build() .execute_with(|| { - dbg!(Permill::from_float(0.005)); // ARRANGE assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), @@ -113,11 +114,11 @@ fn complete_referral_flow_should_work_as_expected() { assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(CHARLIE),)); // Assert charlie rewards let shares = Shares::::get(&CHARLIE); - assert_eq!(shares , 0); + assert_eq!(shares, 0); let total_shares = TotalShares::::get(); assert_eq!(total_shares, alice_shares + bob_shares); let charlie_balance = Tokens::free_balance(HDX, &CHARLIE); - assert_eq!(charlie_balance, 1); + assert_eq!(charlie_balance, 20000000); assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB),)); // Assert BOB rewards @@ -126,7 +127,7 @@ fn complete_referral_flow_should_work_as_expected() { let total_shares = TotalShares::::get(); assert_eq!(total_shares, alice_shares); let bob_balance = Tokens::free_balance(HDX, &BOB); - assert_eq!(bob_balance, 1); + assert_eq!(bob_balance, 10_000_000_000_000); assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE),)); // Assert ALICE rewards @@ -135,9 +136,9 @@ fn complete_referral_flow_should_work_as_expected() { let total_shares = TotalShares::::get(); assert_eq!(total_shares, 0); let alice_balance = Tokens::free_balance(HDX, &ALICE); - assert_eq!(alice_balance, 1); + assert_eq!(alice_balance, 778_000_120_000_000); let (level, total) = Referrer::::get(&ALICE).unwrap(); assert_eq!(level, Level::Advanced); - assert_eq!(total, 123); + assert_eq!(total, 120_000_000); }); } diff --git a/pallets/referrals/src/tests/mock_amm.rs b/pallets/referrals/src/tests/mock_amm.rs index 3a5f56412..82148ff26 100644 --- a/pallets/referrals/src/tests/mock_amm.rs +++ b/pallets/referrals/src/tests/mock_amm.rs @@ -22,7 +22,6 @@ pub mod pallet { } #[pallet::event] - #[pallet::generate_deposit(pub (crate) fn deposit_event)] pub enum Event {} #[pallet::error] @@ -40,7 +39,7 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; let result = T::TradeHooks::simulate_trade(&who, asset_in, asset_out, amount)?; - let fee_result = T::TradeHooks::on_trade_fee(&who, &who, result.fee_asset, result.fee)?; + T::TradeHooks::on_trade_fee(&who, &who, result.fee_asset, result.fee)?; Ok(()) } } @@ -53,10 +52,6 @@ pub struct TradeResult { pub fee_asset: AssetId, } -pub struct OnFeeResult { - pub(crate) unused: Balance, -} - pub trait Hooks { fn simulate_trade( who: &AccountId, @@ -69,5 +64,5 @@ pub trait Hooks { trader: &AccountId, fee_asset: AssetId, fee: Balance, - ) -> Result; + ) -> Result<(), DispatchError>; } diff --git a/pallets/referrals/src/tests/trade_fee.rs b/pallets/referrals/src/tests/trade_fee.rs index 25280c426..9956d6383 100644 --- a/pallets/referrals/src/tests/trade_fee.rs +++ b/pallets/referrals/src/tests/trade_fee.rs @@ -193,3 +193,36 @@ fn process_trade_fee_should_add_asset_to_asset_list() { assert_eq!(asset, Some(())); }); } + +#[test] +fn process_trade_fee_should_not_add_reward_asset_to_asset_list() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, HDX, 2_000_000_000_000)]) + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_tiers(vec![( + DAI, + Level::Novice, + Tier { + referrer: Permill::from_percent(50), + trader: Permill::from_percent(20), + }, + )]) + .build() + .execute_with(|| { + // ARRANGE + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + ALICE + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + // Act + assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), DAI, HDX, 1_000_000_000_000,)); + // Assert + let asset = Assets::::get(HDX); + assert_eq!(asset, None); + }); +} From 63106a710f24802cf50ff91479448ab6c775c702 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 28 Nov 2023 14:03:17 +0100 Subject: [PATCH 25/90] use safe math --- pallets/referrals/src/lib.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index ca402c0d5..30c4490e0 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -33,6 +33,7 @@ use frame_system::{ensure_signed, pallet_prelude::OriginFor}; use hydradx_traits::pools::SpotPriceProvider; use orml_traits::GetByKey; use sp_core::bounded::BoundedVec; +use sp_core::U256; use sp_runtime::traits::AccountIdConversion; use sp_runtime::{DispatchError, Permill}; @@ -349,13 +350,22 @@ pub mod pallet { let reward_reserve = T::Currency::balance(T::RewardAsset::get(), &Self::pot_account_id()); let share_issuance = TotalShares::::get(); - let rewards = shares * reward_reserve / share_issuance; // TODO: use u256 and safe math, moritz! - T::Currency::transfer(T::RewardAsset::get(), &Self::pot_account_id(), &who, rewards, true)?; + let rewards = || -> Option { + let shares_hp = U256::from(shares); + let reward_reserve_hp = U256::from(reward_reserve); + let share_issuance_hp = U256::from(share_issuance); + let r = shares_hp + .checked_mul(reward_reserve_hp)? + .checked_div(share_issuance_hp)?; + Balance::try_from(r).ok() + }() + .ok_or(ArithmeticError::Overflow)?; + + T::Currency::transfer(T::RewardAsset::get(), &Self::pot_account_id(), &who, rewards, true)?; TotalShares::::mutate(|v| { *v = v.saturating_sub(shares); }); - Referrer::::mutate(who.clone(), |v| { if let Some((level, total)) = v { *total = total.saturating_add(rewards); From 94dd7de98f47a772ee41a10085159a9db129eb37 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Tue, 28 Nov 2023 20:58:26 +0100 Subject: [PATCH 26/90] add tiers tests --- pallets/referrals/src/lib.rs | 10 ++++- pallets/referrals/src/tests.rs | 1 + pallets/referrals/src/tests/tiers.rs | 56 ++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 pallets/referrals/src/tests/tiers.rs diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 30c4490e0..7f890e4d0 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -35,7 +35,7 @@ use orml_traits::GetByKey; use sp_core::bounded::BoundedVec; use sp_core::U256; use sp_runtime::traits::AccountIdConversion; -use sp_runtime::{DispatchError, Permill}; +use sp_runtime::{traits::CheckedAdd, DispatchError, Permill}; pub use pallet::*; @@ -220,6 +220,8 @@ pub mod pallet { LinkNotAllowed, /// Calculated rewards are more than the fee amount. This can happen if percentage are incorrectly set. IncorrectRewardCalculation, + /// Given referrer and trader percentages exceeds 100% percent. + IncorrectRewardPercentage, } #[pallet::call] @@ -410,7 +412,11 @@ pub mod pallet { ) -> DispatchResult { T::AuthorityOrigin::ensure_origin(origin)?; - //TODO: ensure that total percentage is not greater than 100% + //ensure that total percentage does not exceed 100% + ensure!( + referrer.checked_add(&trader).is_some(), + Error::::IncorrectRewardPercentage + ); AssetTier::::mutate(asset_id, level, |v| { *v = Some(Tier { referrer, trader }); diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 811177d6c..63494beac 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -21,6 +21,7 @@ mod flow; mod link; mod mock_amm; mod register; +mod tiers; mod trade_fee; use crate as pallet_referrals; diff --git a/pallets/referrals/src/tests/tiers.rs b/pallets/referrals/src/tests/tiers.rs new file mode 100644 index 000000000..055698221 --- /dev/null +++ b/pallets/referrals/src/tests/tiers.rs @@ -0,0 +1,56 @@ +use crate::tests::*; +use pretty_assertions::assert_eq; +use sp_runtime::DispatchError::BadOrigin; + +#[test] +fn setting_asset_tier_should_fail_when_not_correct_origin() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Referrals::set_reward_percentage( + RuntimeOrigin::signed(BOB), + DAI, + Level::Novice, + Permill::from_percent(1), + Permill::from_percent(2), + ), + BadOrigin + ); + }); +} + +#[test] +fn setting_asset_tier_should_correctly_update_storage() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(Referrals::set_reward_percentage( + RuntimeOrigin::root(), + DAI, + Level::Novice, + Permill::from_percent(1), + Permill::from_percent(2), + )); + let d = AssetTier::::get(&DAI, Level::Novice); + assert_eq!( + d, + Some(Tier { + referrer: Permill::from_percent(1), + trader: Permill::from_percent(2) + }) + ) + }); +} + +#[test] +fn setting_asset_tier_should_fail_when_total_percentage_exceeds_hundred_percent() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Referrals::set_reward_percentage( + RuntimeOrigin::root(), + DAI, + Level::Novice, + Permill::from_percent(70), + Permill::from_percent(40), + ), + Error::::IncorrectRewardPercentage + ); + }); +} From c63f7cb5d4247bf98d2ae59cfb5b18e532a83194 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Tue, 28 Nov 2023 21:25:25 +0100 Subject: [PATCH 27/90] benchmarking --- Cargo.lock | 1 + pallets/referrals/Cargo.toml | 12 +++--- pallets/referrals/src/benchmarking.rs | 55 +++++++++++++++++++++++++++ pallets/referrals/src/lib.rs | 2 + 4 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 pallets/referrals/src/benchmarking.rs diff --git a/Cargo.lock b/Cargo.lock index 862ec6f5a..8063ab885 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7804,6 +7804,7 @@ dependencies = [ name = "pallet-referrals" version = "1.0.0" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "hydradx-traits", diff --git a/pallets/referrals/Cargo.toml b/pallets/referrals/Cargo.toml index 3fd752adb..37bd1bace 100644 --- a/pallets/referrals/Cargo.toml +++ b/pallets/referrals/Cargo.toml @@ -30,7 +30,7 @@ frame-support = { workspace = true } frame-system = { workspace = true } # Optional imports for benchmarking -#frame-benchmarking = { workspace = true, optional = true } +frame-benchmarking = { workspace = true, optional = true } sp-io = { workspace = true, optional = true } pallet-timestamp = { workspace = true, optional = true } orml-traits = { workspace = true } @@ -38,7 +38,6 @@ orml-traits = { workspace = true } [dev-dependencies] sp-io = { workspace = true } sp-tracing = { workspace = true } -#frame-benchmarking = { workspace = true } pretty_assertions = "1.2.1" orml-tokens = { workspace = true } @@ -54,10 +53,11 @@ std = [ "sp-core/std", "sp-io/std", "orml-tokens/std", + "frame-benchmarking/std" ] -#runtime-benchmarks = [ -# "frame-benchmarking/runtime-benchmarks", -# "sp-io", -#] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "sp-io", +] try-runtime = [ "frame-support/try-runtime" ] diff --git a/pallets/referrals/src/benchmarking.rs b/pallets/referrals/src/benchmarking.rs new file mode 100644 index 000000000..301623989 --- /dev/null +++ b/pallets/referrals/src/benchmarking.rs @@ -0,0 +1,55 @@ +// Copyright (C) 2020-2023 Intergalactic, Limited (GIB). +// 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. + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; + +use frame_benchmarking::account; +use frame_benchmarking::benchmarks; +use frame_support::traits::tokens::fungibles::Mutate; +use frame_system::RawOrigin; +use sp_std::vec; + +benchmarks! { + where_clause { where + T::Currency: Mutate, + } + + register_code{ + let caller: T::AccountId = account("caller", 0, 1); + let code = vec![b'x'; T::CodeLength::get() as usize]; + let (asset, fee, _) = T::RegistrationFee::get(); + T::Currency::mint_into(asset, &caller, fee)?; + + }: _(RawOrigin::Signed(caller.clone()), code.clone(), caller.clone()) + verify { + let entry = Pallet::::referrer_level(caller.clone()); + assert_eq!(entry, Some((Level::Novice, 0))); + let c = Pallet::::normalize_code(ReferralCode::::truncate_from(code)); + let entry = Pallet::::referral_account(c); + assert_eq!(entry, Some(caller)); + } + +} + +#[cfg(test)] +mod tests { + use super::Pallet; + use crate::tests::*; + use frame_benchmarking::impl_benchmark_test_suite; + + impl_benchmark_test_suite!(Pallet, super::ExtBuilder::default().build(), super::Test); +} diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 7f890e4d0..3288aa080 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -20,6 +20,8 @@ mod weights; +#[cfg(any(feature = "runtime-benchmarks", test))] +mod benchmarking; #[cfg(test)] mod tests; pub mod traits; From 23a04e54ac1ea3c3fc22d537247fb37bb58254d1 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Tue, 28 Nov 2023 21:53:14 +0100 Subject: [PATCH 28/90] add benchamrks for some extrinsics --- pallets/referrals/src/benchmarking.rs | 22 ++++++++++++++++++++++ pallets/referrals/src/lib.rs | 6 ++++++ pallets/referrals/src/tests.rs | 23 +++++++++++++++++++++++ pallets/referrals/src/traits.rs | 5 +++++ 4 files changed, 56 insertions(+) diff --git a/pallets/referrals/src/benchmarking.rs b/pallets/referrals/src/benchmarking.rs index 301623989..3f26d4282 100644 --- a/pallets/referrals/src/benchmarking.rs +++ b/pallets/referrals/src/benchmarking.rs @@ -26,6 +26,7 @@ use sp_std::vec; benchmarks! { where_clause { where T::Currency: Mutate, + T::AssetId: From, } register_code{ @@ -43,6 +44,27 @@ benchmarks! { assert_eq!(entry, Some(caller)); } + link_code{ + let caller: T::AccountId = account("caller", 0, 1); + let user: T::AccountId = account("user", 0, 1); + let code = vec![b'x'; T::CodeLength::get() as usize]; + let (asset, fee, _) = T::RegistrationFee::get(); + T::Currency::mint_into(asset, &caller, fee)?; + Pallet::::register_code(RawOrigin::Signed(caller.clone()).into(), code.clone(), caller.clone())?; + }: _(RawOrigin::Signed(user.clone()), code.clone()) + verify { + let entry = Pallet::::linked_referral_account(user); + assert_eq!(entry, Some(caller)); + } + + convert{ + let caller: T::AccountId = account("caller", 0, 1); + let (asset_id, amount) = T::BenchmarkHelper::prepare_convertible_asset_and_amount(); + T::Currency::mint_into(asset_id.into(), &Pallet::::pot_account_id(), amount)?; + }: _(RawOrigin::Signed(caller), asset_id.into()) + verify { + } + } #[cfg(test)] diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 3288aa080..64024c92a 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -39,6 +39,9 @@ use sp_core::U256; use sp_runtime::traits::AccountIdConversion; use sp_runtime::{traits::CheckedAdd, DispatchError, Permill}; +#[cfg(feature = "runtime-benchmarks")] +pub use crate::traits::BenchmarkHelper; + pub use pallet::*; use weights::WeightInfo; @@ -133,6 +136,9 @@ pub mod pallet { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; } /// Referral codes diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 63494beac..815e8467a 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -115,6 +115,9 @@ impl Config for Test { type CodeLength = CodeLength; type TierVolume = Volume; type WeightInfo = (); + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = Benchmarking; } impl frame_system::Config for Test { @@ -351,3 +354,23 @@ impl SpotPriceProvider for SpotPrice { CONVERSION_RATE.with(|v| v.borrow().get(&(asset_a, asset_b)).copied()) } } + +#[cfg(feature = "runtime-benchmarks")] +use crate::traits::BenchmarkHelper; + +#[cfg(feature = "runtime-benchmarks")] +pub struct Benchmarking; + +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for Benchmarking { + fn prepare_convertible_asset_and_amount() -> (AssetId, Balance) { + let price = FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000); + CONVERSION_RATE.with(|v| { + let mut m = v.borrow_mut(); + m.insert((1234, HDX), price); + m.insert((HDX, 1234), FixedU128::one().div(price)); + }); + + (1234, 1_000_000_000_000) + } +} diff --git a/pallets/referrals/src/traits.rs b/pallets/referrals/src/traits.rs index c3ddbdd01..f696c8734 100644 --- a/pallets/referrals/src/traits.rs +++ b/pallets/referrals/src/traits.rs @@ -4,3 +4,8 @@ pub trait Convert { fn convert(who: AccountId, asset_from: AssetId, asset_to: AssetId, amount: Balance) -> Result; } + +#[cfg(feature = "runtime-benchmarks")] +pub trait BenchmarkHelper { + fn prepare_convertible_asset_and_amount() -> (AssetId, Balance); +} From ada274f7c2a30876c1e6b2375c61d6c14e07ba44 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Tue, 28 Nov 2023 22:05:45 +0100 Subject: [PATCH 29/90] happy clippy happy life --- pallets/referrals/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 64024c92a..938194676 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -474,7 +474,7 @@ impl Pallet { }; // What is asset fee for this level? if any. - let Some(tier) = Self::asset_tier(&asset_id, &level) else { + let Some(tier) = Self::asset_tier(asset_id, level) else { return Ok(amount); }; From fdcc72141e1b5e203280d29a556009f008079e91 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Tue, 28 Nov 2023 22:06:41 +0100 Subject: [PATCH 30/90] update dependencies --- Cargo.lock | 1 - pallets/referrals/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8063ab885..6adaac136 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7810,7 +7810,6 @@ dependencies = [ "hydradx-traits", "orml-tokens", "orml-traits", - "pallet-timestamp", "parity-scale-codec", "pretty_assertions", "scale-info", diff --git a/pallets/referrals/Cargo.toml b/pallets/referrals/Cargo.toml index 37bd1bace..175c896d6 100644 --- a/pallets/referrals/Cargo.toml +++ b/pallets/referrals/Cargo.toml @@ -32,7 +32,6 @@ frame-system = { workspace = true } # Optional imports for benchmarking frame-benchmarking = { workspace = true, optional = true } sp-io = { workspace = true, optional = true } -pallet-timestamp = { workspace = true, optional = true } orml-traits = { workspace = true } [dev-dependencies] From 3ff68de0d561271ad23315370612aa7f63829157 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Tue, 28 Nov 2023 22:21:57 +0100 Subject: [PATCH 31/90] cargo description --- pallets/referrals/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/referrals/Cargo.toml b/pallets/referrals/Cargo.toml index 175c896d6..b014011ba 100644 --- a/pallets/referrals/Cargo.toml +++ b/pallets/referrals/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" license = "Apache-2.0" homepage = '/~https://github.com/galacticcouncil/hydradx-node' repository = '/~https://github.com/galacticcouncil/hydradx-node' -description = "HydraDX Bonds pallet" +description = "HydraDX Referrals pallet" readme = "README.md" [package.metadata.docs.rs] From b70614456a70ab68fda6ca1c83c9b82e69428642 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 29 Nov 2023 14:44:36 +0100 Subject: [PATCH 32/90] finish benchmarks --- pallets/referrals/src/benchmarking.rs | 48 +++++++++++++++++++++++++- pallets/referrals/src/lib.rs | 3 ++ pallets/referrals/src/tests.rs | 12 ++++++- pallets/referrals/src/tests/claim.rs | 3 +- pallets/referrals/src/tests/convert.rs | 21 +++++++++++ 5 files changed, 83 insertions(+), 4 deletions(-) diff --git a/pallets/referrals/src/benchmarking.rs b/pallets/referrals/src/benchmarking.rs index 3f26d4282..6da0a7931 100644 --- a/pallets/referrals/src/benchmarking.rs +++ b/pallets/referrals/src/benchmarking.rs @@ -19,7 +19,7 @@ use super::*; use frame_benchmarking::account; use frame_benchmarking::benchmarks; -use frame_support::traits::tokens::fungibles::Mutate; +use frame_support::traits::tokens::fungibles::{Inspect, Mutate}; use frame_system::RawOrigin; use sp_std::vec; @@ -61,8 +61,54 @@ benchmarks! { let caller: T::AccountId = account("caller", 0, 1); let (asset_id, amount) = T::BenchmarkHelper::prepare_convertible_asset_and_amount(); T::Currency::mint_into(asset_id.into(), &Pallet::::pot_account_id(), amount)?; + Assets::::insert(asset_id,()); }: _(RawOrigin::Signed(caller), asset_id.into()) verify { + let count = Assets::::iter().count(); + assert_eq!(count , 0); + let balance = T::Currency::balance(asset_id, &Pallet::::pot_account_id()); + assert_eq!(balance, 0); + } + + claim_rewards{ + let caller: T::AccountId = account("caller", 0, 1); + let code = vec![b'x'; T::CodeLength::get() as usize]; + let (asset, fee, _) = T::RegistrationFee::get(); + T::Currency::mint_into(asset, &caller, fee)?; + Pallet::::register_code(RawOrigin::Signed(caller.clone()).into(), code.clone(), caller.clone())?; + + // The worst case is when referrer account is updated to the top tier in one call + // So we need to have enough RewardAsset in the pot. And give all the shares to the caller. + let top_tier_volume = T::TierVolume::get(&Level::Advanced).expect("to have all level configured"); + T::Currency::mint_into(T::RewardAsset::get(), &Pallet::::pot_account_id(), top_tier_volume)?; + Shares::::insert(caller.clone(), 1_000_000_000_000); + TotalShares::::put(1_000_000_000_000); + + // Worst case is to convert an asset. let's prepare one. + let (asset_id, amount) = T::BenchmarkHelper::prepare_convertible_asset_and_amount(); + Assets::::insert(asset_id,()); + T::Currency::mint_into(asset_id.into(), &Pallet::::pot_account_id(), amount)?; + }: _(RawOrigin::Signed(caller.clone())) + verify { + let count = Assets::::iter().count(); + assert_eq!(count , 0); + let balance = T::Currency::balance(T::RewardAsset::get(), &caller); + assert_eq!(balance, 1001000000000); + let (level, total) = Referrer::::get(&caller).expect("correct entry"); + assert_eq!(level, Level::Expert); + assert_eq!(total, 1_001_000_000_000); + } + + set_reward_percentage{ + let referrer_percentage = Permill::from_percent(70); + let trader_percentage = Permill::from_percent(30); + }: _(RawOrigin::Root, T::RewardAsset::get(), Level::Expert, referrer_percentage, trader_percentage) + verify { + let entry = Pallet::::asset_tier(T::RewardAsset::get(), Level::Expert); + assert_eq!(entry, Some(Tier{ + referrer: referrer_percentage, + trader: trader_percentage, + })); } } diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 938194676..f116d8fdb 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -323,12 +323,15 @@ pub mod pallet { #[pallet::weight(::WeightInfo::convert())] pub fn convert(origin: OriginFor, asset_id: T::AssetId) -> DispatchResult { ensure_signed(origin)?; + let asset_balance = T::Currency::balance(asset_id, &Self::pot_account_id()); ensure!(asset_balance > 0, Error::::ZeroAmount); let total_reward_asset = T::Convert::convert(Self::pot_account_id(), asset_id, T::RewardAsset::get(), asset_balance)?; + Assets::::remove(asset_id); + Self::deposit_event(Event::Converted { from: asset_id, to: T::RewardAsset::get(), diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 815e8467a..e0fdec46f 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -98,7 +98,17 @@ pub struct Volume; impl GetByKey> for Volume { fn get(level: &Level) -> Option { - TIER_VOLUME.with(|v| v.borrow().get(level).copied()).unwrap_or_default() + let c = TIER_VOLUME.with(|v| v.borrow().get(level).copied()); + + if let Some(l) = c { + return l; + } else { + match level { + Level::Novice => Some(1), + Level::Advanced => Some(1_000_000_000), + Level::Expert => None, + } + } } } diff --git a/pallets/referrals/src/tests/claim.rs b/pallets/referrals/src/tests/claim.rs index 044494ce5..dd17296cc 100644 --- a/pallets/referrals/src/tests/claim.rs +++ b/pallets/referrals/src/tests/claim.rs @@ -141,8 +141,7 @@ fn claim_rewards_update_total_accumulated_for_referrer_account() { // Act assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE))); // Assert - let (level, total) = Referrer::::get(&ALICE).unwrap(); - assert_eq!(level, Level::Novice); + let (_, total) = Referrer::::get(&ALICE).unwrap(); assert_eq!(total, 15_000_000_000_000); }); } diff --git a/pallets/referrals/src/tests/convert.rs b/pallets/referrals/src/tests/convert.rs index 27317a30e..ae1857686 100644 --- a/pallets/referrals/src/tests/convert.rs +++ b/pallets/referrals/src/tests/convert.rs @@ -20,6 +20,7 @@ fn convert_should_convert_all_asset_amount_when_successful() { (HDX, DAI), FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), ) + .with_assets(vec![DAI]) .build() .execute_with(|| { // Arrange @@ -32,6 +33,25 @@ fn convert_should_convert_all_asset_amount_when_successful() { }); } +#[test] +fn convert_should_remove_asset_from_the_asset_list() { + ExtBuilder::default() + .with_endowed_accounts(vec![(Pallet::::pot_account_id(), DAI, 1_000_000_000_000_000_000)]) + .with_conversion_price( + (HDX, DAI), + FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), + ) + .with_assets(vec![DAI]) + .build() + .execute_with(|| { + // Arrange + assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); + // Assert + let entry = Assets::::get(DAI); + assert_eq!(entry, None) + }); +} + #[test] fn convert_should_emit_event_when_successful() { ExtBuilder::default() @@ -40,6 +60,7 @@ fn convert_should_emit_event_when_successful() { (HDX, DAI), FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), ) + .with_assets(vec![DAI]) .build() .execute_with(|| { // Arrange From ada57a806386bddb53909626db65d65083ce76b5 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 29 Nov 2023 14:52:49 +0100 Subject: [PATCH 33/90] use correct claim rewards weight --- pallets/referrals/src/lib.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index f116d8fdb..ca53f84b9 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -349,7 +349,12 @@ pub mod pallet { /// /// Emits `Claimed` event when successful. #[pallet::call_index(3)] - #[pallet::weight(::WeightInfo::claim_rewards())] + #[pallet::weight( { + let c = (Assets::::iter().count() as u64).min(1); + let w = ::WeightInfo::claim_rewards(); + let one_read = T::DbWeight::get().reads(1 as u64); + w.saturating_mul(c).saturating_add(one_read) + })] pub fn claim_rewards(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; for (asset_id, _) in Assets::::drain() { From fcb5628021c94ebe80c0afa62b1ab0a8c5c6c2f4 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 29 Nov 2023 14:58:07 +0100 Subject: [PATCH 34/90] use correct claim rewards weight --- pallets/referrals/src/benchmarking.rs | 9 ++------- pallets/referrals/src/lib.rs | 5 +++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/pallets/referrals/src/benchmarking.rs b/pallets/referrals/src/benchmarking.rs index 6da0a7931..840348727 100644 --- a/pallets/referrals/src/benchmarking.rs +++ b/pallets/referrals/src/benchmarking.rs @@ -83,20 +83,15 @@ benchmarks! { T::Currency::mint_into(T::RewardAsset::get(), &Pallet::::pot_account_id(), top_tier_volume)?; Shares::::insert(caller.clone(), 1_000_000_000_000); TotalShares::::put(1_000_000_000_000); - - // Worst case is to convert an asset. let's prepare one. - let (asset_id, amount) = T::BenchmarkHelper::prepare_convertible_asset_and_amount(); - Assets::::insert(asset_id,()); - T::Currency::mint_into(asset_id.into(), &Pallet::::pot_account_id(), amount)?; }: _(RawOrigin::Signed(caller.clone())) verify { let count = Assets::::iter().count(); assert_eq!(count , 0); let balance = T::Currency::balance(T::RewardAsset::get(), &caller); - assert_eq!(balance, 1001000000000); + assert_eq!(balance, 1000000000); let (level, total) = Referrer::::get(&caller).expect("correct entry"); assert_eq!(level, Level::Expert); - assert_eq!(total, 1_001_000_000_000); + assert_eq!(total, 1000000000); } set_reward_percentage{ diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index ca53f84b9..b89b010a6 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -350,10 +350,11 @@ pub mod pallet { /// Emits `Claimed` event when successful. #[pallet::call_index(3)] #[pallet::weight( { - let c = (Assets::::iter().count() as u64).min(1); + let c = Assets::::iter().count() as u64; + let convert_weight = (::WeightInfo::convert()).saturating_mul(c); let w = ::WeightInfo::claim_rewards(); let one_read = T::DbWeight::get().reads(1 as u64); - w.saturating_mul(c).saturating_add(one_read) + w.saturating_add(convert_weight).saturating_add(one_read) })] pub fn claim_rewards(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; From bf427b8ddf8521bd3e6fbe113367be5e7dd88fb3 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 29 Nov 2023 15:00:37 +0100 Subject: [PATCH 35/90] happy clippy happy life --- pallets/referrals/src/benchmarking.rs | 8 ++++---- pallets/referrals/src/lib.rs | 2 +- pallets/referrals/src/tests.rs | 2 +- pallets/referrals/src/tests/claim.rs | 8 ++++---- pallets/referrals/src/tests/flow.rs | 14 +++++++------- pallets/referrals/src/tests/register.rs | 4 ++-- pallets/referrals/src/tests/tiers.rs | 2 +- pallets/referrals/src/tests/trade_fee.rs | 6 +++--- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/pallets/referrals/src/benchmarking.rs b/pallets/referrals/src/benchmarking.rs index 840348727..9d3da166c 100644 --- a/pallets/referrals/src/benchmarking.rs +++ b/pallets/referrals/src/benchmarking.rs @@ -51,7 +51,7 @@ benchmarks! { let (asset, fee, _) = T::RegistrationFee::get(); T::Currency::mint_into(asset, &caller, fee)?; Pallet::::register_code(RawOrigin::Signed(caller.clone()).into(), code.clone(), caller.clone())?; - }: _(RawOrigin::Signed(user.clone()), code.clone()) + }: _(RawOrigin::Signed(user.clone()), code) verify { let entry = Pallet::::linked_referral_account(user); assert_eq!(entry, Some(caller)); @@ -60,9 +60,9 @@ benchmarks! { convert{ let caller: T::AccountId = account("caller", 0, 1); let (asset_id, amount) = T::BenchmarkHelper::prepare_convertible_asset_and_amount(); - T::Currency::mint_into(asset_id.into(), &Pallet::::pot_account_id(), amount)?; + T::Currency::mint_into(asset_id, &Pallet::::pot_account_id(), amount)?; Assets::::insert(asset_id,()); - }: _(RawOrigin::Signed(caller), asset_id.into()) + }: _(RawOrigin::Signed(caller), asset_id) verify { let count = Assets::::iter().count(); assert_eq!(count , 0); @@ -75,7 +75,7 @@ benchmarks! { let code = vec![b'x'; T::CodeLength::get() as usize]; let (asset, fee, _) = T::RegistrationFee::get(); T::Currency::mint_into(asset, &caller, fee)?; - Pallet::::register_code(RawOrigin::Signed(caller.clone()).into(), code.clone(), caller.clone())?; + Pallet::::register_code(RawOrigin::Signed(caller.clone()).into(), code, caller.clone())?; // The worst case is when referrer account is updated to the top tier in one call // So we need to have enough RewardAsset in the pot. And give all the shares to the caller. diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index b89b010a6..8511606ac 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -353,7 +353,7 @@ pub mod pallet { let c = Assets::::iter().count() as u64; let convert_weight = (::WeightInfo::convert()).saturating_mul(c); let w = ::WeightInfo::claim_rewards(); - let one_read = T::DbWeight::get().reads(1 as u64); + let one_read = T::DbWeight::get().reads(1_u64); w.saturating_add(convert_weight).saturating_add(one_read) })] pub fn claim_rewards(origin: OriginFor) -> DispatchResult { diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index e0fdec46f..ee204ef5e 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -101,7 +101,7 @@ impl GetByKey> for Volume { let c = TIER_VOLUME.with(|v| v.borrow().get(level).copied()); if let Some(l) = c { - return l; + l } else { match level { Level::Novice => Some(1), diff --git a/pallets/referrals/src/tests/claim.rs b/pallets/referrals/src/tests/claim.rs index dd17296cc..c1c613396 100644 --- a/pallets/referrals/src/tests/claim.rs +++ b/pallets/referrals/src/tests/claim.rs @@ -101,7 +101,7 @@ fn claim_rewards_should_reset_account_shares_to_zero() { .execute_with(|| { assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB))); // Assert - let shares = Shares::::get(&BOB); + let shares = Shares::::get(BOB); assert_eq!(shares, 0); }); } @@ -141,7 +141,7 @@ fn claim_rewards_update_total_accumulated_for_referrer_account() { // Act assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE))); // Assert - let (_, total) = Referrer::::get(&ALICE).unwrap(); + let (_, total) = Referrer::::get(ALICE).unwrap(); assert_eq!(total, 15_000_000_000_000); }); } @@ -169,7 +169,7 @@ fn claim_rewards_should_increase_referrer_level_when_limit_is_reached() { // Act assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE))); // Assert - let (level, total) = Referrer::::get(&ALICE).unwrap(); + let (level, total) = Referrer::::get(ALICE).unwrap(); assert_eq!(level, Level::Advanced); assert_eq!(total, 15_000_000_000_000); }); @@ -198,7 +198,7 @@ fn claim_rewards_should_increase_referrer_level_directly_to_top_tier_when_limit_ // Act assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE))); // Assert - let (level, total) = Referrer::::get(&ALICE).unwrap(); + let (level, total) = Referrer::::get(ALICE).unwrap(); assert_eq!(level, Level::Expert); assert_eq!(total, 15_000_000_000_000); }); diff --git a/pallets/referrals/src/tests/flow.rs b/pallets/referrals/src/tests/flow.rs index 5150a8122..589d28f50 100644 --- a/pallets/referrals/src/tests/flow.rs +++ b/pallets/referrals/src/tests/flow.rs @@ -101,11 +101,11 @@ fn complete_referral_flow_should_work_as_expected() { )); // Assert shares - let alice_shares = Shares::::get(&ALICE); + let alice_shares = Shares::::get(ALICE); assert_eq!(alice_shares, 120_000_000); - let bob_shares = Shares::::get(&BOB); + let bob_shares = Shares::::get(BOB); assert_eq!(bob_shares, 30_000_000); - let charlie_shares = Shares::::get(&CHARLIE); + let charlie_shares = Shares::::get(CHARLIE); assert_eq!(charlie_shares, 20_000_000); let total_shares = TotalShares::::get(); assert_eq!(total_shares, alice_shares + bob_shares + charlie_shares); @@ -113,7 +113,7 @@ fn complete_referral_flow_should_work_as_expected() { // CLAIMS assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(CHARLIE),)); // Assert charlie rewards - let shares = Shares::::get(&CHARLIE); + let shares = Shares::::get(CHARLIE); assert_eq!(shares, 0); let total_shares = TotalShares::::get(); assert_eq!(total_shares, alice_shares + bob_shares); @@ -122,7 +122,7 @@ fn complete_referral_flow_should_work_as_expected() { assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB),)); // Assert BOB rewards - let shares = Shares::::get(&BOB); + let shares = Shares::::get(BOB); assert_eq!(shares, 0); let total_shares = TotalShares::::get(); assert_eq!(total_shares, alice_shares); @@ -131,13 +131,13 @@ fn complete_referral_flow_should_work_as_expected() { assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE),)); // Assert ALICE rewards - let shares = Shares::::get(&ALICE); + let shares = Shares::::get(ALICE); assert_eq!(shares, 0); let total_shares = TotalShares::::get(); assert_eq!(total_shares, 0); let alice_balance = Tokens::free_balance(HDX, &ALICE); assert_eq!(alice_balance, 778_000_120_000_000); - let (level, total) = Referrer::::get(&ALICE).unwrap(); + let (level, total) = Referrer::::get(ALICE).unwrap(); assert_eq!(level, Level::Advanced); assert_eq!(total, 120_000_000); }); diff --git a/pallets/referrals/src/tests/register.rs b/pallets/referrals/src/tests/register.rs index 43f3d202a..552570e8d 100644 --- a/pallets/referrals/src/tests/register.rs +++ b/pallets/referrals/src/tests/register.rs @@ -161,7 +161,7 @@ fn signer_should_pay_the_registration_fee() { // Act assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), - code.clone(), + code, BOB )); // Assert @@ -179,7 +179,7 @@ fn singer_should_set_default_level_for_referrer() { // Act assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), - code.clone(), + code, BOB )); // Assert diff --git a/pallets/referrals/src/tests/tiers.rs b/pallets/referrals/src/tests/tiers.rs index 055698221..564fe8aa4 100644 --- a/pallets/referrals/src/tests/tiers.rs +++ b/pallets/referrals/src/tests/tiers.rs @@ -28,7 +28,7 @@ fn setting_asset_tier_should_correctly_update_storage() { Permill::from_percent(1), Permill::from_percent(2), )); - let d = AssetTier::::get(&DAI, Level::Novice); + let d = AssetTier::::get(DAI, Level::Novice); assert_eq!( d, Some(Tier { diff --git a/pallets/referrals/src/tests/trade_fee.rs b/pallets/referrals/src/tests/trade_fee.rs index 9956d6383..0f4219bd5 100644 --- a/pallets/referrals/src/tests/trade_fee.rs +++ b/pallets/referrals/src/tests/trade_fee.rs @@ -29,7 +29,7 @@ fn process_trade_fee_should_increased_referrer_shares() { // Act assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000,)); // Assert - let shares = Shares::::get(&ALICE); + let shares = Shares::::get(ALICE); assert_eq!(shares, 5_000_000_000); }); } @@ -62,7 +62,7 @@ fn process_trade_fee_should_increased_trader_shares() { // Act assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000,)); // Assert - let shares = Shares::::get(&BOB); + let shares = Shares::::get(BOB); assert_eq!(shares, 2_000_000_000); }); } @@ -156,7 +156,7 @@ fn process_trade_should_not_increase_shares_when_trader_does_not_have_linked_acc DAI, 1_000_000_000_000, )); - let shares = Shares::::get(&ALICE); + let shares = Shares::::get(ALICE); assert_eq!(shares, 0); }); } From cd9189b4001ff248c6d4cc86f9650f281d81fe46 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 29 Nov 2023 15:07:51 +0100 Subject: [PATCH 36/90] reformat --- pallets/referrals/src/tests/register.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/pallets/referrals/src/tests/register.rs b/pallets/referrals/src/tests/register.rs index 552570e8d..1233a8a62 100644 --- a/pallets/referrals/src/tests/register.rs +++ b/pallets/referrals/src/tests/register.rs @@ -159,11 +159,7 @@ fn signer_should_pay_the_registration_fee() { // Arrange let code = b"BALLS69".to_vec(); // Act - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - code, - BOB - )); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code, BOB)); // Assert let (fee_asset, amount, beneficiary) = RegistrationFee::get(); assert_balance!(ALICE, fee_asset, INITIAL_ALICE_BALANCE - amount); @@ -177,11 +173,7 @@ fn singer_should_set_default_level_for_referrer() { // Arrange let code = b"BALLS69".to_vec(); // Act - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - code, - BOB - )); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code, BOB)); // Assert let entry = Pallet::::referrer_level(BOB); assert_eq!(entry, Some((Level::default(), Balance::zero()))); From 00ebe5d5b4e08368bbe0deab11f41e0aa6b7f4dd Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 30 Nov 2023 14:53:59 +0100 Subject: [PATCH 37/90] docd and readme --- README.md | 189 ----------------------------------- pallets/referrals/README.md | 28 +++++- pallets/referrals/src/lib.rs | 75 ++++++++++---- 3 files changed, 83 insertions(+), 209 deletions(-) diff --git a/README.md b/README.md index 4a1a0a06f..e69de29bb 100644 --- a/README.md +++ b/README.md @@ -1,189 +0,0 @@ -# HydraDX node - -CROSS-CHAIN LIQUIDITY PROTOCOL BUILT ON SUBSTRATE - -## Contributions & Code of Conduct - -Please follow the contributions guidelines as outlined in [`docs/CONTRIBUTING.md`](docs/CONTRIBUTING.md). -We are welcoming and friendly community please follow our [Code of Conduct](docs/CODE_OF_CONDUCT.md). - -## Local Development - -Follow these steps to prepare a local Substrate development environment :hammer_and_wrench: - -### Simple Setup - -Install all the required dependencies with a single command (be patient, this can take up to 30 -minutes). - -```bash -curl https://getsubstrate.io -sSf | bash -s -- --fast -``` - -### Manual Setup - -Find manual setup instructions at the -[Substrate Developer Hub](https://substrate.dev/docs/en/knowledgebase/getting-started/#manual-installation). - -### Build - -Once the development environment is set up, build the node. This command will build the -[Wasm](https://substrate.dev/docs/en/knowledgebase/advanced/executor#wasm-execution) and -[native](https://substrate.dev/docs/en/knowledgebase/advanced/executor#native-execution) code: - -```bash -cargo build --release -``` - -## Run - -### Local Testnet - -Relay chain repository (polkadot) has to be built in `../polkadot` -Install `polkadot-launch` utility used to start network. - -``` -npm install -g polkadot-launch -``` - -Start local testnet with 4 relay chain validators and HydraDX as a parachain with 2 collators. - -``` -cd ./rococo-local -polkadot-launch config.json -``` - -Observe HydraDX logs - -``` -multitail 99*.log -``` - -### Local Testnet with Zombienet - -Relay chain repository (polkadot) has to be built in `../polkadot` -Grab `zombienet` utility used to start network from [releases](/~https://github.com/paritytech/zombienet/releases) - -Start local testnet with 4 relay chain validators and HydraDX as a parachain with 2 collators. - -``` -cd ./rococo-local -zombienet spawn config-zombienet.json -``` - -### Interaction with the node - -Go to the polkadot apps at https://dotapps.io - -Then open settings screen -> developer and paste - -*NOTE - FixedU128 type is not yet implemented for polkadot apps. Balance is a measure so price can be reasonably selected. If using polkadot apps to create pool:* -- 1 Mega Units equals 1:1 price -- 20 Mega Units equals 20:1 price -- 50 Kilo Units equals 0.05:1 price - -``` -{ - "types": [ - { - "AssetPair": { - "asset_in": "AssetId", - "asset_out": "AssetId" - }, - "Amount": "i128", - "AmountOf": "Amount", - "Address": "AccountId", - "OrmlAccountData": { - "free": "Balance", - "frozen": "Balance", - "reserved": "Balance" - }, - "BlockNumber": "u32", - "BalanceInfo": { - "amount": "Balance", - "assetId": "AssetId" - }, - "Chain": { - "genesisHash": "Vec", - "lastBlockHash": "Vec" - }, - "CurrencyId": "AssetId", - "CurrencyIdOf": "AssetId", - "Intention": { - "who": "AccountId", - "asset_sell": "AssetId", - "asset_buy": "AssetId", - "amount": "Balance", - "discount": "bool", - "sell_or_buy": "IntentionType" - }, - "IntentionId": "Hash", - "IntentionType": { - "_enum": [ - "SELL", - "BUY" - ] - }, - "LookupSource": "AccountId", - "OrderedSet": "Vec", - "Price": "Balance", - "Fee": { - "numerator": "u32", - "denominator": "u32" - }, - "VestingScheduleOf": { - "start": "BlockNumber", - "period": "BlockNumber", - "period_count": "u32", - "per_period": "Balance" - } - } - ], - "alias": { - "tokens": { - "AccountData": "OrmlAccountData" - } - } -} -``` - -Connect to the -- Hacknet: `wss://hack.hydradx.io:9944` -- [Stakenet](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frpc-01.snakenet.hydradx.io): `wss://rpc-01.snakenet.hydradx.io` -- or local node – if you are on chromium based browser, set chrome://flags/#allow-insecure-localhost - -### Performance check - -Prerequisites: rust/cargo, python 3.8+ - -With the following script it is possible to run a simple performance check. It might be useful -to determine whether your machine is suitable to run HydraDX node. - -From the top-level node directory: - -```bash -./scripts/check_performance.sh -``` - -This will run series of benchmarks ( which may take a while). -The output will show benchmark results of HydraDX pallets and comparison against reference values. - -The most interesting information would be the difference between the HydraDx benchmark value and the local machine's benchmark. - -If the difference is >= 0, performance is similar or better. -However, if the difference < 0 - your machine might not suitable to run HydraDX node. Contact HydraDX devs to discuss the results. - -### Testing of storage migrations and runtime upgrades - -The `try-runtime` tool can be used to test storage migrations and runtime upgrades against state from a real chain. -Run the following command to test against the state on HydraDX -```bash -cargo run --release --features=try-runtime try-runtime --no-spec-name-check on-runtime-upgrade live --uri wss://rpc.hydradx.cloud:443 -``` -or against HydraDX testnet on Rococo -```bash -cargo run --release --features=try-runtime try-runtime --no-spec-name-check on-runtime-upgrade live --uri wss://rococo-hydradx-rpc.hydration.dev:443 -``` - -### Honorable contributions -[@apopiak](/~https://github.com/apopiak) for great reviews [#87](/~https://github.com/galacticcouncil/HydraDX-node/pull/87) and support. diff --git a/pallets/referrals/README.md b/pallets/referrals/README.md index cc037d0a8..896b53ce1 100644 --- a/pallets/referrals/README.md +++ b/pallets/referrals/README.md @@ -1,3 +1,27 @@ -# Referrals pallet +# pallet-referrals -## Overview +## Referrals pallet + +Support for referrals, referral codes and rewards distribution. + +### Overview + +Referrals give an opportunity to users to earn some rewards from trading activity if the trader +used their referral code to link their account to the referrer account. + +The trader can get back part of the trade fee too if configured. + +Pallet also provides support for volume-based tiering. Referrer can reached higher Level based on the total amount generated by users of the referrer code. +The higher level, the better reward. + +Rewards are accumulated in the pallet's account and if it is not RewardAsset, it is converted to RewardAsset prior to claim. + +//! ### Terminology + +* **Referral code:** a string of certain size that identifies the referrer. Must be alphanumeric and upper case. +* **Referrer:** user that registered a code +* **Trader:** user that does a trade +* **Reward Asset:** id of an asset which rewards are paid in. Usually native asset. + + +License: Apache-2.0 diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 8511606ac..dd876443d 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -15,6 +15,27 @@ //! # Referrals pallet //! +//! Support for referrals, referral codes and rewards distribution. +//! +//! ## Overview +//! +//! Referrals give an opportunity to users to earn some rewards from trading activity if the trader +//! used their referral code to link their account to the referrer account. +//! +//! The trader can get back part of the trade fee too if configured. +//! +//! Pallet also provides support for volume-based tiering. Referrer can reached higher Level based on the total amount generated by users of the referrer code. +//! The higher level, the better reward. +//! +//! Rewards are accumulated in the pallet's account and if it is not RewardAsset, it is converted to RewardAsset prior to claim. +//! +//! //! ### Terminology +//! +//! * **Referral code:** a string of certain size that identifies the referrer. Must be alphanumeric and upper case. +//! * **Referrer:** user that registered a code +//! * **Trader:** user that does a trade +//! * **Reward Asset:** id of an asset which rewards are paid in. Usually native asset. +//! #![cfg_attr(not(feature = "std"), no_std)] @@ -53,6 +74,8 @@ const MIN_CODE_LENGTH: usize = 3; use scale_info::TypeInfo; +/// Referrer level. +/// Indicates current level of the referrer to determine which reward percentages are used. #[derive(Hash, Clone, Copy, Default, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] pub enum Level { #[default] @@ -171,8 +194,8 @@ pub mod pallet { #[pallet::getter(fn referrer_level)] pub(super) type Referrer = StorageMap<_, Blake2_128Concat, T::AccountId, (Level, Balance), OptionQuery>; - /// - /// + /// Asset tier information. + /// Maps (asset_id, level) to Tier which provides information about reward percentages. #[pallet::storage] #[pallet::getter(fn asset_tier)] pub(super) type AssetTier = @@ -187,25 +210,30 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(crate) fn deposit_event)] pub enum Event { + /// Referral code has been registered. CodeRegistered { code: ReferralCode, account: T::AccountId, }, + /// Referral code has been linked to an account. CodeLinked { account: T::AccountId, code: ReferralCode, referral_account: T::AccountId, }, + /// Asset has been converted to RewardAsset. Converted { from: T::AssetId, to: T::AssetId, amount: Balance, received: Balance, }, + /// Rewards claimed. Claimed { who: T::AccountId, rewards: Balance, }, + /// New asset tier has been set. TierRewardSet { asset_id: T::AssetId, level: Level, @@ -217,16 +245,23 @@ pub mod pallet { #[pallet::error] #[cfg_attr(test, derive(PartialEq, Eq))] pub enum Error { + /// Referral code is too long. TooLong, + /// Referral code is too short. TooShort, + /// Referral code contains invalid character. Only alphanumeric characters are allowed. InvalidCharacter, + /// Referral code already exists. AlreadyExists, + /// Provided referral code is invalid. Either does not exist or is too long. InvalidCode, + /// Account is already linked to another referral account. AlreadyLinked, + /// Nothing in the referral pot account for the asset. ZeroAmount, /// Linking an account to the same referral account is not allowed. LinkNotAllowed, - /// Calculated rewards are more than the fee amount. This can happen if percentage are incorrectly set. + /// Calculated rewards are more than the fee amount. This can happen if percentages are incorrectly set. IncorrectRewardCalculation, /// Given referrer and trader percentages exceeds 100% percent. IncorrectRewardPercentage, @@ -244,9 +279,8 @@ pub mod pallet { /// `code` must contain only alfa-numeric characters and all characters will be converted to upper case. /// /// /// Parameters: - /// - `origin`: - /// - `code`: - /// - `account`: + /// - `code`: Code to register. Must follow the restrictions. + /// - `account`: Account which the code is bound to. /// /// Emits `CodeRegistered` event when successful. #[pallet::call_index(0)] @@ -283,9 +317,12 @@ pub mod pallet { /// Link a code to an account. /// + /// `Code` must be valid registered code. Otherwise `InvalidCode` is returned. + /// + /// Signer account is linked to the referral account of the code. + /// /// /// Parameters: - /// - `origin`: - /// - `code`: + /// - `code`: Code to use to link the signer account to. /// /// Emits `CodeLinked` event when successful. #[pallet::call_index(1)] @@ -312,11 +349,10 @@ pub mod pallet { Ok(()) } - /// Convert accrued asset amount to native currency. + /// Convert accrued asset amount to reward currency. /// /// /// Parameters: - /// - `origin`: - /// - `asset_id`: + /// - `asset_id`: Id of an asset to convert to RewardAsset. /// /// Emits `Converted` event when successful. #[pallet::call_index(2)] @@ -344,8 +380,11 @@ pub mod pallet { /// Claim accumulated rewards /// - /// /// Parameters: - /// - `origin`: + /// IF there is any asset in the reward pot, all is converted to RewardCurrency first. + /// + /// Reward amount is calculated based on the shares of the signer account. + /// + /// if the signer account is referrer account, total accumulated rewards is updated as well as referrer level if reached. /// /// Emits `Claimed` event when successful. #[pallet::call_index(3)] @@ -412,12 +451,12 @@ pub mod pallet { /// Set asset tier reward percentages /// /// /// Parameters: - /// - `origin`: - /// - `level`: - /// - `referrer`: - /// - `trader`: + /// - `asset_id`: asset id + /// - `level`: level + /// - `referrer`: referrer percentage that goes to the referrer. + /// - `trader`: trader percentage that goes back to the trader. /// - /// Emits `Claimed` event when successful. + /// Emits `TierRewardSet` event when successful. #[pallet::call_index(4)] #[pallet::weight(::WeightInfo::set_reward_percentage())] pub fn set_reward_percentage( From 25abc70cbbd66b7e757cba911b37d624717d8f14 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 30 Nov 2023 15:27:30 +0100 Subject: [PATCH 38/90] runtime integration --- Cargo.lock | 6 ++-- pallets/omnipool/Cargo.toml | 2 +- pallets/omnipool/src/lib.rs | 12 +++---- pallets/omnipool/src/provider.rs | 5 ++- pallets/omnipool/src/traits.rs | 14 ++++++-- pallets/referrals/src/lib.rs | 8 ++--- runtime/adapters/Cargo.toml | 1 + runtime/adapters/src/lib.rs | 9 +++-- runtime/hydradx/Cargo.toml | 5 ++- runtime/hydradx/src/assets.rs | 58 +++++++++++++++++++++++++++++++- runtime/hydradx/src/lib.rs | 3 +- 11 files changed, 100 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6adaac136..84c4d3887 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4198,6 +4198,7 @@ dependencies = [ "pallet-liquidity-mining", "pallet-omnipool", "pallet-omnipool-liquidity-mining", + "pallet-referrals", "pallet-route-executor", "pallet-stableswap", "pallet-staking 2.1.0", @@ -4221,7 +4222,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "193.0.0" +version = "194.0.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", @@ -4287,6 +4288,7 @@ dependencies = [ "pallet-otc", "pallet-preimage", "pallet-proxy", + "pallet-referrals", "pallet-relaychain-info", "pallet-route-executor", "pallet-scheduler", @@ -7640,7 +7642,7 @@ dependencies = [ [[package]] name = "pallet-omnipool" -version = "4.0.1" +version = "4.0.2" dependencies = [ "bitflags", "frame-benchmarking", diff --git a/pallets/omnipool/Cargo.toml b/pallets/omnipool/Cargo.toml index d59a416be..838a8663e 100644 --- a/pallets/omnipool/Cargo.toml +++ b/pallets/omnipool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-omnipool" -version = "4.0.1" +version = "4.0.2" authors = ['GalacticCouncil'] edition = "2021" license = "Apache-2.0" diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index b7ac14134..6e7d09c54 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -1076,7 +1076,7 @@ pub mod pallet { Self::update_hdx_subpool_hub_asset(origin, state_changes.hdx_hub_amount)?; - Self::process_trade_fee(asset_out, state_changes.fee.asset_fee)?; + Self::process_trade_fee(&who, asset_out, state_changes.fee.asset_fee)?; Self::deposit_event(Event::SellExecuted { who, @@ -1268,7 +1268,7 @@ pub mod pallet { Self::update_hdx_subpool_hub_asset(origin, state_changes.hdx_hub_amount)?; - Self::process_trade_fee(asset_in, state_changes.fee.asset_fee)?; + Self::process_trade_fee(&who, asset_in, state_changes.fee.asset_fee)?; Self::deposit_event(Event::BuyExecuted { who, @@ -1727,7 +1727,7 @@ impl Pallet { Self::set_asset_state(asset_out, new_asset_out_state); - Self::process_trade_fee(asset_out, state_changes.fee.asset_fee)?; + Self::process_trade_fee(&who, asset_out, state_changes.fee.asset_fee)?; Self::deposit_event(Event::SellExecuted { who: who.clone(), @@ -1833,7 +1833,7 @@ impl Pallet { Self::set_asset_state(asset_out, new_asset_out_state); - Self::process_trade_fee(T::HubAssetId::get(), state_changes.fee.asset_fee)?; + Self::process_trade_fee(&who, T::HubAssetId::get(), state_changes.fee.asset_fee)?; Self::deposit_event(Event::BuyExecuted { who: who.clone(), @@ -1945,10 +1945,10 @@ impl Pallet { } /// Calls `on_trade_fee` hook and ensures that no more than the fee amount is transferred. - fn process_trade_fee(asset: T::AssetId, amount: Balance) -> DispatchResult { + fn process_trade_fee(trader: &T::AccountId, asset: T::AssetId, amount: Balance) -> DispatchResult { let account = Self::protocol_account(); let original_asset_reserve = T::Currency::free_balance(asset, &account); - let unused = T::OmnipoolHooks::on_trade_fee(account.clone(), asset, amount)?; + let unused = T::OmnipoolHooks::on_trade_fee(account.clone(), trader.clone(), asset, amount)?; let asset_reserve = T::Currency::free_balance(asset, &account); let updated_asset_reserve = asset_reserve.saturating_add(amount.saturating_sub(unused)); ensure!( diff --git a/pallets/omnipool/src/provider.rs b/pallets/omnipool/src/provider.rs index b18d88c9c..63a29d6a0 100644 --- a/pallets/omnipool/src/provider.rs +++ b/pallets/omnipool/src/provider.rs @@ -1,7 +1,7 @@ use crate::pallet::Assets; use crate::{Config, Pallet}; use hydradx_traits::pools::SpotPriceProvider; -use sp_runtime::traits::{CheckedMul, Get}; +use sp_runtime::traits::{CheckedMul, Get, One}; use sp_runtime::{FixedPointNumber, FixedU128}; impl SpotPriceProvider for Pallet { @@ -12,6 +12,9 @@ impl SpotPriceProvider for Pallet { } fn spot_price(asset_a: T::AssetId, asset_b: T::AssetId) -> Option { + if asset_a == asset_b { + return Some(FixedU128::one()); + } if asset_a == T::HubAssetId::get() { let asset_b = Self::load_asset_state(asset_b).ok()?; FixedU128::checked_from_rational(asset_b.hub_reserve, asset_b.reserve) diff --git a/pallets/omnipool/src/traits.rs b/pallets/omnipool/src/traits.rs index 7e3d5f56e..fa484f50e 100644 --- a/pallets/omnipool/src/traits.rs +++ b/pallets/omnipool/src/traits.rs @@ -58,7 +58,12 @@ where fn on_trade_weight() -> Weight; /// Returns unused amount - fn on_trade_fee(fee_account: AccountId, asset: AssetId, amount: Balance) -> Result; + fn on_trade_fee( + fee_account: AccountId, + trader: AccountId, + asset: AssetId, + amount: Balance, + ) -> Result; } impl OmnipoolHooks for () @@ -91,7 +96,12 @@ where Weight::zero() } - fn on_trade_fee(_fee_account: AccountId, _asset: AssetId, amount: Balance) -> Result { + fn on_trade_fee( + _fee_account: AccountId, + _trader: AccountId, + _asset: AssetId, + amount: Balance, + ) -> Result { Ok(amount) } } diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index dd876443d..16e7bca8c 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -27,7 +27,7 @@ //! Pallet also provides support for volume-based tiering. Referrer can reached higher Level based on the total amount generated by users of the referrer code. //! The higher level, the better reward. //! -//! Rewards are accumulated in the pallet's account and if it is not RewardAsset, it is converted to RewardAsset prior to claim. +//! Rewards are accumulated in the pallet's account and if it is not RewardAsset, it is converted to RewardAsset prior to claim. //! //! //! ### Terminology //! @@ -59,6 +59,7 @@ use sp_core::bounded::BoundedVec; use sp_core::U256; use sp_runtime::traits::AccountIdConversion; use sp_runtime::{traits::CheckedAdd, DispatchError, Permill}; +use sp_std::vec::Vec; #[cfg(feature = "runtime-benchmarks")] pub use crate::traits::BenchmarkHelper; @@ -229,10 +230,7 @@ pub mod pallet { received: Balance, }, /// Rewards claimed. - Claimed { - who: T::AccountId, - rewards: Balance, - }, + Claimed { who: T::AccountId, rewards: Balance }, /// New asset tier has been set. TierRewardSet { asset_id: T::AssetId, diff --git a/runtime/adapters/Cargo.toml b/runtime/adapters/Cargo.toml index 163b14ace..e60a4bc6d 100644 --- a/runtime/adapters/Cargo.toml +++ b/runtime/adapters/Cargo.toml @@ -28,6 +28,7 @@ pallet-staking = { workspace = true } pallet-route-executor = { workspace = true } pallet-currencies = { workspace = true } pallet-stableswap = { workspace = true } +pallet-referrals = { workspace = true } # Substrate dependencies frame-support = { workspace = true } diff --git a/runtime/adapters/src/lib.rs b/runtime/adapters/src/lib.rs index ae0d23ef4..ee0cba0a6 100644 --- a/runtime/adapters/src/lib.rs +++ b/runtime/adapters/src/lib.rs @@ -331,9 +331,11 @@ where Runtime: pallet_ema_oracle::Config + pallet_circuit_breaker::Config + frame_system::Config - + pallet_staking::Config, + + pallet_staking::Config + + pallet_referrals::Config, ::AccountId: From, ::AssetId: From, + ::AssetId: From, { type Error = DispatchError; @@ -460,8 +462,9 @@ where w1.saturating_add(w2).saturating_add(w3) } - fn on_trade_fee(fee_account: AccountId, asset: AssetId, amount: Balance) -> Result { - pallet_staking::Pallet::::process_trade_fee(fee_account.into(), asset.into(), amount) + fn on_trade_fee(fee_account: AccountId, trader: AccountId, asset: AssetId, amount: Balance) -> Result { + let unused = pallet_referrals::Pallet::::process_trade_fee(fee_account.clone().into(), trader.clone().into(), asset.into(), amount)?; + pallet_staking::Pallet::::process_trade_fee(fee_account.into(), asset.into(), unused) } } diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index 24497f6fd..9ba18e408 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "193.0.0" +version = "194.0.0" authors = ["GalacticCouncil"] edition = "2021" license = "Apache 2.0" @@ -35,6 +35,7 @@ pallet-stableswap = { workspace = true } pallet-bonds = { workspace = true } pallet-lbp = { workspace = true } pallet-xyk = { workspace = true } +pallet-referrals = { workspace = true } # pallets pallet-balances = { workspace = true } @@ -188,6 +189,7 @@ runtime-benchmarks = [ "pallet-stableswap/runtime-benchmarks", "pallet-lbp/runtime-benchmarks", "pallet-xyk/runtime-benchmarks", + "pallet-referrals/runtime-benchmarks", ] std = [ "codec/std", @@ -280,6 +282,7 @@ std = [ "pallet-evm-chain-id/std", "pallet-evm-precompile-dispatch/std", "pallet-xyk/std", + "pallet-referrals/std", ] try-runtime= [ "frame-try-runtime", diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index e6a7085f2..2afe2e6dd 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -54,13 +54,14 @@ use frame_support::{ use frame_system::{EnsureRoot, EnsureSigned, RawOrigin}; use hydradx_traits::router::{inverse_route, Trade}; use orml_traits::currency::MutationHooks; -use orml_traits::GetByKey; +use orml_traits::{GetByKey, MultiCurrency}; use pallet_dynamic_fees::types::FeeParams; use pallet_lbp::weights::WeightInfo as LbpWeights; use pallet_route_executor::{weights::WeightInfo as RouterWeights, AmmTradeWeights, MAX_NUMBER_OF_TRADES}; use pallet_staking::types::Action; use pallet_staking::SigmoidPercentage; use pallet_xyk::weights::WeightInfo as XykWeights; +use sp_runtime::DispatchError; use sp_std::num::NonZeroU16; parameter_types! { @@ -797,6 +798,8 @@ where use pallet_currencies::fungibles::FungibleCurrencies; +use pallet_referrals::traits::Convert; +use pallet_referrals::Level; #[cfg(feature = "runtime-benchmarks")] use pallet_stableswap::BenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] @@ -985,3 +988,56 @@ impl pallet_xyk::Config for Runtime { type NonDustableWhitelistHandler = Duster; type OracleSource = XYKOracleSourceIdentifier; } + +parameter_types! { + pub const ReferralsPalletId: PalletId = PalletId(*b"referral"); + pub RegistrationFee: (AssetId,Balance, AccountId)= (NativeAssetId::get(), 1, TreasuryAccount::get()); + pub const MaxCodeLength: u32 = 7; +} + +impl pallet_referrals::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AuthorityOrigin = EnsureRoot; + type AssetId = AssetId; + type Currency = FungibleCurrencies; + type Convert = ConvertViaOmnipool; + type SpotPriceProvider = Omnipool; + type RewardAsset = NativeAssetId; + type PalletId = ReferralsPalletId; + type RegistrationFee = RegistrationFee; + type CodeLength = MaxCodeLength; + type TierVolume = ReferralsLevelTiers; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + +pub struct ConvertViaOmnipool; +impl Convert for ConvertViaOmnipool { + type Error = DispatchError; + + fn convert( + who: AccountId, + asset_from: AssetId, + asset_to: AssetId, + amount: Balance, + ) -> Result { + let balance = Currencies::free_balance(asset_to, &who); + Omnipool::sell(RuntimeOrigin::signed(who.clone()), asset_from, asset_to, amount, 0)?; + let balance_after = Currencies::free_balance(asset_to, &who); + let received = balance_after.saturating_sub(balance); + Ok(received) + } +} + +pub struct ReferralsLevelTiers; + +impl GetByKey> for ReferralsLevelTiers { + fn get(k: &Level) -> Option { + match k { + Level::Novice => Some(1_000_000_000_000), + Level::Advanced => Some(10_000_000_000_000), + Level::Expert => None, + } + } +} diff --git a/runtime/hydradx/src/lib.rs b/runtime/hydradx/src/lib.rs index 875b654c4..d2f976614 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -99,7 +99,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("hydradx"), impl_name: create_runtime_str!("hydradx"), authoring_version: 1, - spec_version: 193, + spec_version: 194, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -167,6 +167,7 @@ construct_runtime!( Bonds: pallet_bonds = 71, LBP: pallet_lbp = 73, XYK: pallet_xyk = 74, + Referrals: pallet_referrals = 75, // ORML related modules Tokens: orml_tokens = 77, From 27adcd02161b944368ba957b8eef36d5b12a66b0 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 30 Nov 2023 16:21:47 +0100 Subject: [PATCH 39/90] fix trait change --- Cargo.lock | 2 +- pallets/circuit-breaker/Cargo.toml | 2 +- pallets/circuit-breaker/src/tests/mock.rs | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 84c4d3887..7eb820f70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6902,7 +6902,7 @@ dependencies = [ [[package]] name = "pallet-circuit-breaker" -version = "1.1.17" +version = "1.1.18" dependencies = [ "frame-benchmarking", "frame-support", diff --git a/pallets/circuit-breaker/Cargo.toml b/pallets/circuit-breaker/Cargo.toml index c942ae40e..dac9d9e92 100644 --- a/pallets/circuit-breaker/Cargo.toml +++ b/pallets/circuit-breaker/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-circuit-breaker" -version = "1.1.17" +version = "1.1.18" authors = ["GalacticCouncil "] edition = "2021" license = "Apache-2.0" diff --git a/pallets/circuit-breaker/src/tests/mock.rs b/pallets/circuit-breaker/src/tests/mock.rs index 81c53b3a8..46a0a69aa 100644 --- a/pallets/circuit-breaker/src/tests/mock.rs +++ b/pallets/circuit-breaker/src/tests/mock.rs @@ -304,7 +304,12 @@ where todo!() } - fn on_trade_fee(_fee_account: AccountId, _asset: AssetId, _amount: Balance) -> Result { + fn on_trade_fee( + _fee_account: AccountId, + _trader: AccountId, + _asset: AssetId, + _amount: Balance, + ) -> Result { Ok(Balance::zero()) } } From b6f8b3902c2c6b51c1dddd2a05dfcdbf2842cd01 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 30 Nov 2023 16:46:19 +0100 Subject: [PATCH 40/90] reformat --- runtime/adapters/src/lib.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/runtime/adapters/src/lib.rs b/runtime/adapters/src/lib.rs index ee0cba0a6..e4de47e07 100644 --- a/runtime/adapters/src/lib.rs +++ b/runtime/adapters/src/lib.rs @@ -462,8 +462,18 @@ where w1.saturating_add(w2).saturating_add(w3) } - fn on_trade_fee(fee_account: AccountId, trader: AccountId, asset: AssetId, amount: Balance) -> Result { - let unused = pallet_referrals::Pallet::::process_trade_fee(fee_account.clone().into(), trader.clone().into(), asset.into(), amount)?; + fn on_trade_fee( + fee_account: AccountId, + trader: AccountId, + asset: AssetId, + amount: Balance, + ) -> Result { + let unused = pallet_referrals::Pallet::::process_trade_fee( + fee_account.clone().into(), + trader.clone().into(), + asset.into(), + amount, + )?; pallet_staking::Pallet::::process_trade_fee(fee_account.into(), asset.into(), unused) } } From fe080357de5ce402816caf91d061610eeb415e72 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Fri, 8 Dec 2023 10:18:35 +0100 Subject: [PATCH 41/90] ressurect readme --- README.md | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) diff --git a/README.md b/README.md index e69de29bb..c2877997e 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,189 @@ +# HydraDX node + +CROSS-CHAIN LIQUIDITY PROTOCOL BUILT ON SUBSTRATE + +## Contributions & Code of Conduct + +Please follow the contributions guidelines as outlined in [`docs/CONTRIBUTING.md`](docs/CONTRIBUTING.md). +We are welcoming and friendly community please follow our [Code of Conduct](docs/CODE_OF_CONDUCT.md). + +## Local Development + +Follow these steps to prepare a local Substrate development environment :hammer_and_wrench: + +### Simple Setup + +Install all the required dependencies with a single command (be patient, this can take up to 30 +minutes). + +```bash +curl https://getsubstrate.io -sSf | bash -s -- --fast +``` + +### Manual Setup + +Find manual setup instructions at the +[Substrate Developer Hub](https://substrate.dev/docs/en/knowledgebase/getting-started/#manual-installation). + +### Build + +Once the development environment is set up, build the node. This command will build the +[Wasm](https://substrate.dev/docs/en/knowledgebase/advanced/executor#wasm-execution) and +[native](https://substrate.dev/docs/en/knowledgebase/advanced/executor#native-execution) code: + +```bash +cargo build --release +``` + +## Run + +### Local Testnet + +Relay chain repository (polkadot) has to be built in `../polkadot` +Install `polkadot-launch` utility used to start network. + +``` +npm install -g polkadot-launch +``` + +Start local testnet with 4 relay chain validators and HydraDX as a parachain with 2 collators. + +``` +cd ./rococo-local +polkadot-launch config.json +``` + +Observe HydraDX logs + +``` +multitail 99*.log +``` + +### Local Testnet with Zombienet + +Relay chain repository (polkadot) has to be built in `../polkadot` +Grab `zombienet` utility used to start network from [releases](/~https://github.com/paritytech/zombienet/releases) + +Start local testnet with 4 relay chain validators and HydraDX as a parachain with 2 collators. + +``` +cd ./rococo-local +zombienet spawn config-zombienet.json +``` + +### Interaction with the node + +Go to the polkadot apps at https://dotapps.io + +Then open settings screen -> developer and paste + +*NOTE - FixedU128 type is not yet implemented for polkadot apps. Balance is a measure so price can be reasonably selected. If using polkadot apps to create pool:* +- 1 Mega Units equals 1:1 price +- 20 Mega Units equals 20:1 price +- 50 Kilo Units equals 0.05:1 price + +``` +{ + "types": [ + { + "AssetPair": { + "asset_in": "AssetId", + "asset_out": "AssetId" + }, + "Amount": "i128", + "AmountOf": "Amount", + "Address": "AccountId", + "OrmlAccountData": { + "free": "Balance", + "frozen": "Balance", + "reserved": "Balance" + }, + "BlockNumber": "u32", + "BalanceInfo": { + "amount": "Balance", + "assetId": "AssetId" + }, + "Chain": { + "genesisHash": "Vec", + "lastBlockHash": "Vec" + }, + "CurrencyId": "AssetId", + "CurrencyIdOf": "AssetId", + "Intention": { + "who": "AccountId", + "asset_sell": "AssetId", + "asset_buy": "AssetId", + "amount": "Balance", + "discount": "bool", + "sell_or_buy": "IntentionType" + }, + "IntentionId": "Hash", + "IntentionType": { + "_enum": [ + "SELL", + "BUY" + ] + }, + "LookupSource": "AccountId", + "OrderedSet": "Vec", + "Price": "Balance", + "Fee": { + "numerator": "u32", + "denominator": "u32" + }, + "VestingScheduleOf": { + "start": "BlockNumber", + "period": "BlockNumber", + "period_count": "u32", + "per_period": "Balance" + } + } + ], + "alias": { + "tokens": { + "AccountData": "OrmlAccountData" + } + } +} +``` + +Connect to the +- Hacknet: `wss://hack.hydradx.io:9944` +- [Stakenet](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frpc-01.snakenet.hydradx.io): `wss://rpc-01.snakenet.hydradx.io` +- or local node – if you are on chromium based browser, set chrome://flags/#allow-insecure-localhost + +### Performance check + +Prerequisites: rust/cargo, python 3.8+ + +With the following script it is possible to run a simple performance check. It might be useful +to determine whether your machine is suitable to run HydraDX node. + +From the top-level node directory: + +```bash +./scripts/check_performance.sh +``` + +This will run series of benchmarks ( which may take a while). +The output will show benchmark results of HydraDX pallets and comparison against reference values. + +The most interesting information would be the difference between the HydraDx benchmark value and the local machine's benchmark. + +If the difference is >= 0, performance is similar or better. +However, if the difference < 0 - your machine might not suitable to run HydraDX node. Contact HydraDX devs to discuss the results. + +### Testing of storage migrations and runtime upgrades + +The `try-runtime` tool can be used to test storage migrations and runtime upgrades against state from a real chain. +Run the following command to test against the state on HydraDX +```bash +cargo run --release --features=try-runtime try-runtime --no-spec-name-check on-runtime-upgrade live --uri wss://rpc.hydradx.cloud:443 +``` +or against HydraDX testnet on Rococo +```bash +cargo run --release --features=try-runtime try-runtime --no-spec-name-check on-runtime-upgrade live --uri wss://rococo-hydradx-rpc.hydration.dev:443 +``` + +### Honorable contributions +[@apopiak](/~https://github.com/apopiak) for great reviews [#87](/~https://github.com/galacticcouncil/HydraDX-node/pull/87) and support. \ No newline at end of file From a1aa65a0c16cba27fe0a601cb3344d945adba1fc Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Fri, 8 Dec 2023 10:19:52 +0100 Subject: [PATCH 42/90] ressurect readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c2877997e..4a1a0a06f 100644 --- a/README.md +++ b/README.md @@ -186,4 +186,4 @@ cargo run --release --features=try-runtime try-runtime --no-spec-name-check on-r ``` ### Honorable contributions -[@apopiak](/~https://github.com/apopiak) for great reviews [#87](/~https://github.com/galacticcouncil/HydraDX-node/pull/87) and support. \ No newline at end of file +[@apopiak](/~https://github.com/apopiak) for great reviews [#87](/~https://github.com/galacticcouncil/HydraDX-node/pull/87) and support. From 2bee22a91e9396e338f2964af048445bb7aa687d Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Fri, 8 Dec 2023 10:45:21 +0100 Subject: [PATCH 43/90] unneccesary stuff --- pallets/referrals/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 16e7bca8c..ebd3d9199 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -29,7 +29,7 @@ //! //! Rewards are accumulated in the pallet's account and if it is not RewardAsset, it is converted to RewardAsset prior to claim. //! -//! //! ### Terminology +//! ### Terminology //! //! * **Referral code:** a string of certain size that identifies the referrer. Must be alphanumeric and upper case. //! * **Referrer:** user that registered a code @@ -276,7 +276,7 @@ pub mod pallet { /// Maximum length is limited to `T::CodeLength`. /// `code` must contain only alfa-numeric characters and all characters will be converted to upper case. /// - /// /// Parameters: + /// Parameters: /// - `code`: Code to register. Must follow the restrictions. /// - `account`: Account which the code is bound to. /// From 87ea6ee4a11c707884e32c6f188ba88fb7fe96b0 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Fri, 8 Dec 2023 12:19:19 +0100 Subject: [PATCH 44/90] keep alive for reward transfer --- pallets/referrals/src/lib.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index ebd3d9199..e88ca46c1 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -319,7 +319,7 @@ pub mod pallet { /// /// Signer account is linked to the referral account of the code. /// - /// /// Parameters: + /// Parameters: /// - `code`: Code to use to link the signer account to. /// /// Emits `CodeLinked` event when successful. @@ -349,7 +349,7 @@ pub mod pallet { /// Convert accrued asset amount to reward currency. /// - /// /// Parameters: + /// Parameters: /// - `asset_id`: Id of an asset to convert to RewardAsset. /// /// Emits `Converted` event when successful. @@ -418,7 +418,16 @@ pub mod pallet { }() .ok_or(ArithmeticError::Overflow)?; - T::Currency::transfer(T::RewardAsset::get(), &Self::pot_account_id(), &who, rewards, true)?; + // Make sure that we can transfer all the rewards if all shares withdrawn. + let keep_pot_alive = shares != share_issuance; + + T::Currency::transfer( + T::RewardAsset::get(), + &Self::pot_account_id(), + &who, + rewards, + keep_pot_alive, + )?; TotalShares::::mutate(|v| { *v = v.saturating_sub(shares); }); @@ -448,7 +457,7 @@ pub mod pallet { /// Set asset tier reward percentages /// - /// /// Parameters: + /// Parameters: /// - `asset_id`: asset id /// - `level`: level /// - `referrer`: referrer percentage that goes to the referrer. From aff0e73cb122a47ff1f5f725db8d2e93a9a73282 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Fri, 8 Dec 2023 16:51:17 +0100 Subject: [PATCH 45/90] remove account param from register call --- Cargo.lock | 1 + integration-tests/Cargo.toml | 1 + integration-tests/src/lib.rs | 1 + integration-tests/src/referrals.rs | 54 ++++++++++++++++++++++++ pallets/referrals/src/lib.rs | 9 ++-- pallets/referrals/src/tests/claim.rs | 3 -- pallets/referrals/src/tests/flow.rs | 1 - pallets/referrals/src/tests/link.rs | 11 +---- pallets/referrals/src/tests/register.rs | 53 ++++++++--------------- pallets/referrals/src/tests/trade_fee.rs | 7 --- runtime/hydradx/src/assets.rs | 2 +- 11 files changed, 80 insertions(+), 63 deletions(-) create mode 100644 integration-tests/src/referrals.rs diff --git a/Cargo.lock b/Cargo.lock index 56d9f027e..f6973127b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10841,6 +10841,7 @@ dependencies = [ "pallet-omnipool", "pallet-omnipool-liquidity-mining", "pallet-otc", + "pallet-referrals", "pallet-relaychain-info", "pallet-route-executor", "pallet-scheduler", diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 0d1881a3a..462f702af 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -20,6 +20,7 @@ pallet-circuit-breaker = { workspace = true } pallet-omnipool-liquidity-mining = { workspace = true } pallet-bonds = { workspace = true } pallet-stableswap = { workspace = true } +pallet-referrals = { workspace = true } # Warehouse dependencies pallet-asset-registry = { workspace = true } diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs index 1dca8de52..553a10b8d 100644 --- a/integration-tests/src/lib.rs +++ b/integration-tests/src/lib.rs @@ -16,6 +16,7 @@ mod omnipool_liquidity_mining; mod oracle; mod otc; mod polkadot_test_net; +mod referrals; mod router; mod staking; mod transact_call_filter; diff --git a/integration-tests/src/referrals.rs b/integration-tests/src/referrals.rs new file mode 100644 index 000000000..b913c35fc --- /dev/null +++ b/integration-tests/src/referrals.rs @@ -0,0 +1,54 @@ +#![cfg(test)] +use crate::polkadot_test_net::*; +use frame_support::assert_ok; +use hydradx_runtime::{Currencies, Referrals, Runtime, RuntimeOrigin}; +use orml_traits::MultiCurrency; +use xcm_emulator::TestExt; + +#[test] +fn registering_a_code_should_charge_registration_fee() { + Hydra::execute_with(|| { + let code = b"BALLS69".to_vec(); + let (reg_asset, reg_fee, reg_account) = ::RegistrationFee::get(); + let balance = Currencies::free_balance(reg_asset, ®_account); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE.into()), code)); + let balance_after = Currencies::free_balance(reg_asset, ®_account); + let diff = balance_after - balance; + assert_eq!(diff, reg_fee); + }); +} + +#[test] +fn trading_in_omnipool_should_transfer_portion_of_fee_to_reward_pot() { + Hydra::execute_with(|| { + let code = b"BALLS69".to_vec(); + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE.into()), + code.clone() + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB.into()), code)); + }); +} + +#[test] +fn trading_in_omnipool_should_increase_referrer_shares() { + Hydra::execute_with(|| { + let code = b"BALLS69".to_vec(); + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE.into()), + code.clone() + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB.into()), code)); + }); +} +#[test] +fn trading_in_omnipool_should_increase_trader_shares() { + Hydra::execute_with(|| { + let code = b"BALLS69".to_vec(); + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE.into()), + code.clone() + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB.into()), code)); + }); +} diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index e88ca46c1..a100e10f8 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -278,12 +278,11 @@ pub mod pallet { /// /// Parameters: /// - `code`: Code to register. Must follow the restrictions. - /// - `account`: Account which the code is bound to. /// /// Emits `CodeRegistered` event when successful. #[pallet::call_index(0)] #[pallet::weight(::WeightInfo::register_code())] - pub fn register_code(origin: OriginFor, code: Vec, account: T::AccountId) -> DispatchResult { + pub fn register_code(origin: OriginFor, code: Vec) -> DispatchResult { let who = ensure_signed(origin)?; let code: ReferralCode = code.try_into().map_err(|_| Error::::TooLong)?; @@ -306,9 +305,9 @@ pub mod pallet { let (fee_asset, fee_amount, beneficiary) = T::RegistrationFee::get(); T::Currency::transfer(fee_asset, &who, &beneficiary, fee_amount, true)?; - *v = Some(account.clone()); - Referrer::::insert(&account, (Level::default(), Balance::zero())); - Self::deposit_event(Event::CodeRegistered { code, account }); + *v = Some(who.clone()); + Referrer::::insert(&who, (Level::default(), Balance::zero())); + Self::deposit_event(Event::CodeRegistered { code, account: who }); Ok(()) }) } diff --git a/pallets/referrals/src/tests/claim.rs b/pallets/referrals/src/tests/claim.rs index c1c613396..d6388026a 100644 --- a/pallets/referrals/src/tests/claim.rs +++ b/pallets/referrals/src/tests/claim.rs @@ -135,7 +135,6 @@ fn claim_rewards_update_total_accumulated_for_referrer_account() { assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - ALICE )); assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); // Act @@ -163,7 +162,6 @@ fn claim_rewards_should_increase_referrer_level_when_limit_is_reached() { assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - ALICE )); assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); // Act @@ -192,7 +190,6 @@ fn claim_rewards_should_increase_referrer_level_directly_to_top_tier_when_limit_ assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - ALICE )); assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); // Act diff --git a/pallets/referrals/src/tests/flow.rs b/pallets/referrals/src/tests/flow.rs index 589d28f50..28417fc51 100644 --- a/pallets/referrals/src/tests/flow.rs +++ b/pallets/referrals/src/tests/flow.rs @@ -78,7 +78,6 @@ fn complete_referral_flow_should_work_as_expected() { assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - ALICE )); assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); assert_ok!(Referrals::link_code( diff --git a/pallets/referrals/src/tests/link.rs b/pallets/referrals/src/tests/link.rs index ef6850051..d0e514050 100644 --- a/pallets/referrals/src/tests/link.rs +++ b/pallets/referrals/src/tests/link.rs @@ -8,7 +8,6 @@ fn link_code_should_work_when_code_is_valid() { assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - ALICE, )); // ACT assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); @@ -42,7 +41,6 @@ fn link_code_should_link_correctly_when_code_is_valid() { assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - ALICE )); // ACT @@ -61,7 +59,6 @@ fn link_code_should_fail_when_linking_to_same_acccount() { assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - ALICE )); // ACT @@ -79,7 +76,6 @@ fn link_code_should_link_correctly_when_code_is_lowercase() { assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - ALICE )); // ACT @@ -98,7 +94,6 @@ fn link_code_should_fail_when_account_is_already_linked() { assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - ALICE )); assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); @@ -115,11 +110,7 @@ fn link_code_should_emit_event_when_successful() { ExtBuilder::default().build().execute_with(|| { //ARRANGE let code = b"BALLS69".to_vec(); - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - code.clone(), - ALICE - )); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); // ACT assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code.clone())); // ASSERT diff --git a/pallets/referrals/src/tests/register.rs b/pallets/referrals/src/tests/register.rs index 1233a8a62..dc551661e 100644 --- a/pallets/referrals/src/tests/register.rs +++ b/pallets/referrals/src/tests/register.rs @@ -8,7 +8,6 @@ fn register_code_should_work_when_code_is_max_length() { assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - BOB )); }); } @@ -16,11 +15,7 @@ fn register_code_should_work_when_code_is_max_length() { #[test] fn register_code_should_work_when_code_is_min_length() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"ABC".to_vec(), - BOB - )); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), b"ABC".to_vec(),)); }); } @@ -28,7 +23,7 @@ fn register_code_should_work_when_code_is_min_length() { fn register_code_should_fail_when_code_is_too_long() { ExtBuilder::default().build().execute_with(|| { assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"TOOMANYBALLS69".to_vec(), BOB), + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"TOOMANYBALLS69".to_vec()), Error::::TooLong ); }); @@ -38,15 +33,15 @@ fn register_code_should_fail_when_code_is_too_long() { fn register_code_should_fail_when_code_is_too_short() { ExtBuilder::default().build().execute_with(|| { assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"".to_vec(), BOB), + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"".to_vec()), Error::::TooShort ); assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"A".to_vec(), BOB), + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"A".to_vec()), Error::::TooShort ); assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"AB".to_vec(), BOB), + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"AB".to_vec()), Error::::TooShort ); }); @@ -59,11 +54,10 @@ fn register_code_should_fail_when_code_already_exists() { assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - BOB )); // Act assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), BOB), + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec()), Error::::AlreadyExists ); }); @@ -76,11 +70,10 @@ fn register_code_should_fail_when_code_is_lowercase_and_already_exists() { assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - BOB )); // Act assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"balls69".to_vec(), BOB), + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"balls69".to_vec()), Error::::AlreadyExists ); }); @@ -90,7 +83,7 @@ fn register_code_should_fail_when_code_is_lowercase_and_already_exists() { fn register_code_should_fail_when_code_contains_invalid_char() { ExtBuilder::default().build().execute_with(|| { assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"ABC?".to_vec(), BOB), + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"ABC?".to_vec()), Error::::InvalidCharacter ); }); @@ -102,14 +95,10 @@ fn register_code_should_store_account_mapping_to_code_correctly() { // Arrange let code = b"BALLS69".to_vec(); // Act - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - code.clone(), - BOB - )); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); // Assert let entry = Pallet::::referral_account::>(code.try_into().unwrap()); - assert_eq!(entry, Some(BOB)); + assert_eq!(entry, Some(ALICE)); }); } @@ -119,17 +108,13 @@ fn register_code_should_convert_to_upper_case_when_code_is_lower_case() { // Arrange let code = b"balls69".to_vec(); // Act - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - code.clone(), - BOB - )); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); // Assert let entry = Pallet::::referral_account::>(code.clone().try_into().unwrap()); assert_eq!(entry, None); let normalized = Pallet::::normalize_code(code.try_into().unwrap()); let entry = Pallet::::referral_account::>(normalized); - assert_eq!(entry, Some(BOB)); + assert_eq!(entry, Some(ALICE)); }); } @@ -139,15 +124,11 @@ fn register_code_should_emit_event_when_successful() { // Arrange let code = b"BALLS69".to_vec(); // Act - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - code.clone(), - BOB - )); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); // Assert expect_events(vec![Event::CodeRegistered { code: code.try_into().unwrap(), - account: BOB, + account: ALICE, } .into()]); }); @@ -159,7 +140,7 @@ fn signer_should_pay_the_registration_fee() { // Arrange let code = b"BALLS69".to_vec(); // Act - assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code, BOB)); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code)); // Assert let (fee_asset, amount, beneficiary) = RegistrationFee::get(); assert_balance!(ALICE, fee_asset, INITIAL_ALICE_BALANCE - amount); @@ -173,9 +154,9 @@ fn singer_should_set_default_level_for_referrer() { // Arrange let code = b"BALLS69".to_vec(); // Act - assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code, BOB)); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code)); // Assert - let entry = Pallet::::referrer_level(BOB); + let entry = Pallet::::referrer_level(ALICE); assert_eq!(entry, Some((Level::default(), Balance::zero()))); }); } diff --git a/pallets/referrals/src/tests/trade_fee.rs b/pallets/referrals/src/tests/trade_fee.rs index 0f4219bd5..0ab4d9015 100644 --- a/pallets/referrals/src/tests/trade_fee.rs +++ b/pallets/referrals/src/tests/trade_fee.rs @@ -23,7 +23,6 @@ fn process_trade_fee_should_increased_referrer_shares() { assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - ALICE )); assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); // Act @@ -56,7 +55,6 @@ fn process_trade_fee_should_increased_trader_shares() { assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - ALICE )); assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); // Act @@ -89,7 +87,6 @@ fn process_trade_fee_should_increased_total_share_issuance() { assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - ALICE )); assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); // Act @@ -122,7 +119,6 @@ fn process_trade_fee_should_fail_when_taken_amount_is_greated_than_fee_amount() assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - ALICE )); assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); // Act @@ -147,7 +143,6 @@ fn process_trade_should_not_increase_shares_when_trader_does_not_have_linked_acc assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - ALICE )); // Assert assert_ok!(MockAmm::trade( @@ -183,7 +178,6 @@ fn process_trade_fee_should_add_asset_to_asset_list() { assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - ALICE )); assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); // Act @@ -216,7 +210,6 @@ fn process_trade_fee_should_not_add_reward_asset_to_asset_list() { assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(), - ALICE )); assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); // Act diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 2afe2e6dd..1521f38a2 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -991,7 +991,7 @@ impl pallet_xyk::Config for Runtime { parameter_types! { pub const ReferralsPalletId: PalletId = PalletId(*b"referral"); - pub RegistrationFee: (AssetId,Balance, AccountId)= (NativeAssetId::get(), 1, TreasuryAccount::get()); + pub RegistrationFee: (AssetId,Balance, AccountId)= (NativeAssetId::get(), 1_000_000_000_000, TreasuryAccount::get()); pub const MaxCodeLength: u32 = 7; } From 595a4f153b1eeac94652922ac34a65d65814a867 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Fri, 8 Dec 2023 17:52:37 +0100 Subject: [PATCH 46/90] Add reward pot to list of non dust accounts --- integration-tests/src/referrals.rs | 162 ++++++++++++++++++++++++++++- runtime/hydradx/src/lib.rs | 1 + 2 files changed, 162 insertions(+), 1 deletion(-) diff --git a/integration-tests/src/referrals.rs b/integration-tests/src/referrals.rs index b913c35fc..20d4045f7 100644 --- a/integration-tests/src/referrals.rs +++ b/integration-tests/src/referrals.rs @@ -1,8 +1,11 @@ #![cfg(test)] use crate::polkadot_test_net::*; use frame_support::assert_ok; -use hydradx_runtime::{Currencies, Referrals, Runtime, RuntimeOrigin}; +use frame_system::RawOrigin; +use hydradx_runtime::{Currencies, Omnipool, Referrals, Runtime, RuntimeOrigin, Tokens}; use orml_traits::MultiCurrency; +use primitives::AccountId; +use sp_runtime::Permill; use xcm_emulator::TestExt; #[test] @@ -21,34 +24,191 @@ fn registering_a_code_should_charge_registration_fee() { #[test] fn trading_in_omnipool_should_transfer_portion_of_fee_to_reward_pot() { Hydra::execute_with(|| { + init_omnipool_with_oracle_for_block_10(); + init_referrals_program(); let code = b"BALLS69".to_vec(); assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE.into()), code.clone() )); assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB.into()), code)); + assert_ok!(Omnipool::sell( + RuntimeOrigin::signed(BOB.into()), + HDX, + DAI, + 1_000_000_000_000, + 0 + )); + let pot_balance = Currencies::free_balance(DAI, &Referrals::pot_account_id()); + assert_eq!(pot_balance, 2_060_386_836_081); }); } #[test] fn trading_in_omnipool_should_increase_referrer_shares() { Hydra::execute_with(|| { + init_omnipool_with_oracle_for_block_10(); + init_referrals_program(); let code = b"BALLS69".to_vec(); assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE.into()), code.clone() )); assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB.into()), code)); + assert_ok!(Omnipool::sell( + RuntimeOrigin::signed(BOB.into()), + HDX, + DAI, + 1_000_000_000_000, + 0 + )); + let referrer_shares = Referrals::account_shares::(ALICE.into()); + assert_eq!(referrer_shares, 51_354_392); }); } #[test] fn trading_in_omnipool_should_increase_trader_shares() { Hydra::execute_with(|| { + init_omnipool_with_oracle_for_block_10(); + init_referrals_program(); + let code = b"BALLS69".to_vec(); + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE.into()), + code.clone() + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB.into()), code)); + assert_ok!(Omnipool::sell( + RuntimeOrigin::signed(BOB.into()), + HDX, + DAI, + 1_000_000_000_000, + 0 + )); + let referrer_shares = Referrals::account_shares::(BOB.into()); + assert_eq!(referrer_shares, 25_677_196); + }); +} + +#[test] +fn trading_in_omnipool_should_increase_total_shares_correctly() { + Hydra::execute_with(|| { + init_omnipool_with_oracle_for_block_10(); + init_referrals_program(); let code = b"BALLS69".to_vec(); assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE.into()), code.clone() )); assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB.into()), code)); + assert_ok!(Omnipool::sell( + RuntimeOrigin::signed(BOB.into()), + HDX, + DAI, + 1_000_000_000_000, + 0 + )); + let referrer_shares = Referrals::total_shares(); + assert_eq!(referrer_shares, 25_677_196 + 51_354_392); }); } + +#[test] +fn trading_hdx_in_omnipool_should_increase_trader_shares() { + Hydra::execute_with(|| { + init_omnipool_with_oracle_for_block_10(); + init_referrals_program(); + let code = b"BALLS69".to_vec(); + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE.into()), + code.clone() + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB.into()), code)); + assert_ok!(Omnipool::sell( + RuntimeOrigin::signed(BOB.into()), + DAI, + HDX, + 10_000_000_000_000_000_000, + 0 + )); + let referrer_shares = Referrals::account_shares::(BOB.into()); + assert_eq!(referrer_shares, 25_677_196); + }); +} + +fn init_omnipool_with_oracle_for_block_10() { + init_omnipool(); + do_trade_to_populate_oracle(DAI, HDX, UNITS); + set_relaychain_block_number(10); + do_trade_to_populate_oracle(DAI, HDX, UNITS); +} + +fn do_trade_to_populate_oracle(asset_1: AssetId, asset_2: AssetId, amount: Balance) { + assert_ok!(Tokens::set_balance( + RawOrigin::Root.into(), + CHARLIE.into(), + LRNA, + 1000000000000 * UNITS, + 0, + )); + + assert_ok!(Omnipool::sell( + RuntimeOrigin::signed(CHARLIE.into()), + LRNA, + asset_1, + amount, + Balance::MIN + )); + + assert_ok!(Omnipool::sell( + RuntimeOrigin::signed(CHARLIE.into()), + LRNA, + asset_2, + amount, + Balance::MIN + )); +} + +fn init_referrals_program() { + assert_ok!(Referrals::set_reward_percentage( + RuntimeOrigin::root(), + HDX, + pallet_referrals::Level::Novice, + Permill::from_percent(2), + Permill::from_percent(1), + )); + assert_ok!(Referrals::set_reward_percentage( + RuntimeOrigin::root(), + HDX, + pallet_referrals::Level::Advanced, + Permill::from_percent(5), + Permill::from_percent(2), + )); + assert_ok!(Referrals::set_reward_percentage( + RuntimeOrigin::root(), + HDX, + pallet_referrals::Level::Expert, + Permill::from_percent(10), + Permill::from_percent(5), + )); + assert_ok!(Referrals::set_reward_percentage( + RuntimeOrigin::root(), + DAI, + pallet_referrals::Level::Novice, + Permill::from_percent(2), + Permill::from_percent(1), + )); + assert_ok!(Referrals::set_reward_percentage( + RuntimeOrigin::root(), + DAI, + pallet_referrals::Level::Advanced, + Permill::from_percent(5), + Permill::from_percent(2), + )); + assert_ok!(Referrals::set_reward_percentage( + RuntimeOrigin::root(), + DAI, + pallet_referrals::Level::Expert, + Permill::from_percent(10), + Permill::from_percent(5), + )); +} diff --git a/runtime/hydradx/src/lib.rs b/runtime/hydradx/src/lib.rs index d2f976614..a9fb0bee2 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -119,6 +119,7 @@ pub fn get_all_module_accounts() -> Vec { vec![ TreasuryPalletId::get().into_account_truncating(), VestingPalletId::get().into_account_truncating(), + ReferralsPalletId::get().into_account_truncating(), ] } From f31278aa71a8a1a183c39f29366d7b9e76f57fac Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Mon, 11 Dec 2023 08:51:09 +0100 Subject: [PATCH 47/90] use ratio instead fixed type for price and use oracle provider --- Cargo.lock | 1 + pallets/referrals/Cargo.toml | 1 + pallets/referrals/src/lib.rs | 25 +++++++++------- pallets/referrals/src/tests.rs | 36 +++++++++++------------- pallets/referrals/src/tests/claim.rs | 20 +++---------- pallets/referrals/src/tests/convert.rs | 15 ++-------- pallets/referrals/src/tests/flow.rs | 7 ++--- pallets/referrals/src/tests/trade_fee.rs | 35 +++++------------------ runtime/adapters/src/lib.rs | 1 + runtime/adapters/src/price.rs | 21 ++++++++++++++ runtime/hydradx/src/assets.rs | 5 +++- traits/src/lib.rs | 2 ++ traits/src/price.rs | 5 ++++ 13 files changed, 83 insertions(+), 91 deletions(-) create mode 100644 runtime/adapters/src/price.rs create mode 100644 traits/src/price.rs diff --git a/Cargo.lock b/Cargo.lock index f6973127b..0d6be1754 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7809,6 +7809,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "hydra-dx-math", "hydradx-traits", "orml-tokens", "orml-traits", diff --git a/pallets/referrals/Cargo.toml b/pallets/referrals/Cargo.toml index b014011ba..de2678fab 100644 --- a/pallets/referrals/Cargo.toml +++ b/pallets/referrals/Cargo.toml @@ -19,6 +19,7 @@ codec = { default-features = false, features = ["derive"], package = "parity-sca # HydraDX hydradx-traits = { workspace = true } +hydra-dx-math = { workspace = true } # primitives sp-runtime = { workspace = true } diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index a100e10f8..9ed7e0c88 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -49,16 +49,18 @@ pub mod traits; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::pallet_prelude::{DispatchResult, Get}; -use frame_support::sp_runtime::FixedPointNumber; use frame_support::traits::fungibles::Transfer; use frame_support::{ensure, transactional, RuntimeDebug}; use frame_system::{ensure_signed, pallet_prelude::OriginFor}; -use hydradx_traits::pools::SpotPriceProvider; +use hydradx_traits::price::PriceProvider; use orml_traits::GetByKey; +use scale_info::TypeInfo; use sp_core::bounded::BoundedVec; use sp_core::U256; +use sp_runtime::helpers_128bit::multiply_by_rational_with_rounding; use sp_runtime::traits::AccountIdConversion; -use sp_runtime::{traits::CheckedAdd, DispatchError, Permill}; +use sp_runtime::Rounding; +use sp_runtime::{traits::CheckedAdd, ArithmeticError, DispatchError, Permill}; use sp_std::vec::Vec; #[cfg(feature = "runtime-benchmarks")] @@ -73,8 +75,6 @@ pub type ReferralCode = BoundedVec; const MIN_CODE_LENGTH: usize = 3; -use scale_info::TypeInfo; - /// Referrer level. /// Indicates current level of the referrer to determine which reward percentages are used. #[derive(Hash, Clone, Copy, Default, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] @@ -109,9 +109,9 @@ pub mod pallet { use crate::traits::Convert; use frame_support::pallet_prelude::*; use frame_support::sp_runtime::ArithmeticError; - use frame_support::sp_runtime::FixedU128; use frame_support::traits::fungibles::{Inspect, Transfer}; use frame_support::PalletId; + use hydra_dx_math::ema::EmaPrice; use sp_runtime::traits::Zero; #[pallet::pallet] @@ -136,7 +136,7 @@ pub mod pallet { type Convert: Convert; /// Price provider to use for shares calculation. - type SpotPriceProvider: SpotPriceProvider; + type PriceProvider: PriceProvider; /// ID of an asset that is used to distribute rewards in. #[pallet::constant] @@ -532,7 +532,7 @@ impl Pallet { return Ok(amount); }; - let Some(price) = T::SpotPriceProvider::spot_price(T::RewardAsset::get(), asset_id) else { + let Some(price) = T::PriceProvider::get_price(T::RewardAsset::get(), asset_id) else { // no price, no fun. return Ok(amount); }; @@ -543,8 +543,13 @@ impl Pallet { ensure!(total_taken <= amount, Error::::IncorrectRewardCalculation); T::Currency::transfer(asset_id, &source, &Self::pot_account_id(), total_taken, true)?; - let referrer_shares = price.saturating_mul_int(referrer_reward); - let trader_shares = price.saturating_mul_int(trader_reward); + let referrer_shares = multiply_by_rational_with_rounding(referrer_reward, price.n, price.d, Rounding::Down) + .ok_or(ArithmeticError::Overflow)?; + let trader_shares = multiply_by_rational_with_rounding(trader_reward, price.n, price.d, Rounding::Down) + .ok_or(ArithmeticError::Overflow)?; + + //let referrer_shares = price.saturating_mul_int(referrer_reward); + //let trader_shares = price.saturating_mul_int(trader_reward); TotalShares::::mutate(|v| { *v = v.saturating_add(referrer_shares.saturating_add(trader_shares)); }); diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index ee204ef5e..de9c009e8 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -45,10 +45,12 @@ use crate::tests::mock_amm::{Hooks, TradeResult}; use crate::traits::Convert; use frame_support::{assert_noop, assert_ok}; use frame_system::EnsureRoot; +use hydra_dx_math::ema::EmaPrice; use orml_traits::MultiCurrency; use orml_traits::{parameter_type_with_key, MultiCurrencyExtended}; -use sp_runtime::traits::One; -use sp_runtime::{DispatchError, FixedPointNumber, FixedU128}; +use sp_runtime::helpers_128bit::multiply_by_rational_with_rounding; +use sp_runtime::DispatchError; +use sp_runtime::Rounding; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -70,7 +72,7 @@ pub const TREASURY: AccountId = 400; pub(crate) const INITIAL_ALICE_BALANCE: Balance = 1_000 * ONE; thread_local! { - pub static CONVERSION_RATE: RefCell> = RefCell::new(HashMap::default()); + pub static CONVERSION_RATE: RefCell> = RefCell::new(HashMap::default()); pub static TIER_VOLUME: RefCell>> = RefCell::new(HashMap::default()); } @@ -118,7 +120,7 @@ impl Config for Test { type AssetId = AssetId; type Currency = Tokens; type Convert = AssetConvert; - type SpotPriceProvider = SpotPrice; + type PriceProvider = ConversionPrice; type RewardAsset = RewardAsset; type PalletId = RefarralPalletId; type RegistrationFee = RegistrationFee; @@ -223,11 +225,11 @@ impl ExtBuilder { self.tiers.extend(shares); self } - pub fn with_conversion_price(self, pair: (AssetId, AssetId), price: FixedU128) -> Self { + pub fn with_conversion_price(self, pair: (AssetId, AssetId), price: EmaPrice) -> Self { CONVERSION_RATE.with(|v| { let mut m = v.borrow_mut(); m.insert(pair, price); - m.insert((pair.1, pair.0), FixedU128::one().div(price)); + m.insert((pair.1, pair.0), price.inverted()); }); self } @@ -299,7 +301,7 @@ impl Convert for AssetConvert { let price = CONVERSION_RATE .with(|v| v.borrow().get(&(asset_to, asset_from)).copied()) .ok_or(Error::::InvalidCode)?; - let result = price.saturating_mul_int(amount); + let result = multiply_by_rational_with_rounding(amount, price.n, price.d, Rounding::Down).unwrap(); Tokens::update_balance(asset_from, &who, -(amount as i128)).unwrap(); Tokens::update_balance(asset_to, &who, result as i128).unwrap(); Ok(result) @@ -327,7 +329,7 @@ impl Hooks for AmmTrader { let price = CONVERSION_RATE .with(|v| v.borrow().get(&(asset_out, asset_in)).copied()) .expect("to have a price"); - let amount_out = price.saturating_mul_int(amount); + let amount_out = multiply_by_rational_with_rounding(amount, price.n, price.d, Rounding::Down).unwrap(); let fee_amount = TRADE_PERCENTAGE.mul_floor(amount_out); Ok(TradeResult { amount_in: amount, @@ -348,18 +350,14 @@ impl Hooks for AmmTrader { } } -pub struct SpotPrice; +pub struct ConversionPrice; -impl SpotPriceProvider for SpotPrice { - type Price = FixedU128; +impl PriceProvider for ConversionPrice { + type Price = EmaPrice; - fn pair_exists(_asset_a: AssetId, _asset_b: AssetId) -> bool { - unimplemented!() - } - - fn spot_price(asset_a: AssetId, asset_b: AssetId) -> Option { + fn get_price(asset_a: AssetId, asset_b: AssetId) -> Option { if asset_a == asset_b { - return Some(FixedU128::one()); + return Some(EmaPrice::one()); } CONVERSION_RATE.with(|v| v.borrow().get(&(asset_a, asset_b)).copied()) } @@ -374,11 +372,11 @@ pub struct Benchmarking; #[cfg(feature = "runtime-benchmarks")] impl BenchmarkHelper for Benchmarking { fn prepare_convertible_asset_and_amount() -> (AssetId, Balance) { - let price = FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000); + let price = EmaPrice::new(1_000_000_000_000, 1_000_000_000_000); CONVERSION_RATE.with(|v| { let mut m = v.borrow_mut(); m.insert((1234, HDX), price); - m.insert((HDX, 1234), FixedU128::one().div(price)); + m.insert((HDX, 1234), price.inverted()); }); (1234, 1_000_000_000_000) diff --git a/pallets/referrals/src/tests/claim.rs b/pallets/referrals/src/tests/claim.rs index d6388026a..6d2c004d2 100644 --- a/pallets/referrals/src/tests/claim.rs +++ b/pallets/referrals/src/tests/claim.rs @@ -16,14 +16,8 @@ fn claim_rewards_should_convert_all_assets() { (Pallet::::pot_account_id(), DOT, 4_000_000_000_000), ]) .with_assets(vec![DAI, DOT]) - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) - .with_conversion_price( - (HDX, DOT), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000), - ) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) + .with_conversion_price((HDX, DOT), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000)) .build() .execute_with(|| { assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB))); @@ -45,14 +39,8 @@ fn claim_rewards_should_remove_assets_from_the_list() { (Pallet::::pot_account_id(), DAI, 3_000_000_000_000_000_000), (Pallet::::pot_account_id(), DOT, 4_000_000_000_000), ]) - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) - .with_conversion_price( - (HDX, DOT), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000), - ) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) + .with_conversion_price((HDX, DOT), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000)) .build() .execute_with(|| { assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB))); diff --git a/pallets/referrals/src/tests/convert.rs b/pallets/referrals/src/tests/convert.rs index ae1857686..21d012954 100644 --- a/pallets/referrals/src/tests/convert.rs +++ b/pallets/referrals/src/tests/convert.rs @@ -16,10 +16,7 @@ fn convert_should_fail_when_amount_is_zero() { fn convert_should_convert_all_asset_amount_when_successful() { ExtBuilder::default() .with_endowed_accounts(vec![(Pallet::::pot_account_id(), DAI, 1_000_000_000_000_000_000)]) - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_assets(vec![DAI]) .build() .execute_with(|| { @@ -37,10 +34,7 @@ fn convert_should_convert_all_asset_amount_when_successful() { fn convert_should_remove_asset_from_the_asset_list() { ExtBuilder::default() .with_endowed_accounts(vec![(Pallet::::pot_account_id(), DAI, 1_000_000_000_000_000_000)]) - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_assets(vec![DAI]) .build() .execute_with(|| { @@ -56,10 +50,7 @@ fn convert_should_remove_asset_from_the_asset_list() { fn convert_should_emit_event_when_successful() { ExtBuilder::default() .with_endowed_accounts(vec![(Pallet::::pot_account_id(), DAI, 1_000_000_000_000_000_000)]) - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_assets(vec![DAI]) .build() .execute_with(|| { diff --git a/pallets/referrals/src/tests/flow.rs b/pallets/referrals/src/tests/flow.rs index 28417fc51..b05c3713d 100644 --- a/pallets/referrals/src/tests/flow.rs +++ b/pallets/referrals/src/tests/flow.rs @@ -16,11 +16,8 @@ fn complete_referral_flow_should_work_as_expected() { (BOB, HDX, bob_initial_hdx), (CHARLIE, DOT, 2_000_000_000_000), ]) - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) - .with_conversion_price((HDX, DOT), FixedU128::from_rational(1_000_000_000_000, 500_000_000_000)) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) + .with_conversion_price((HDX, DOT), EmaPrice::new(1_000_000_000_000, 500_000_000_000)) .with_tiers(vec![ ( DAI, diff --git a/pallets/referrals/src/tests/trade_fee.rs b/pallets/referrals/src/tests/trade_fee.rs index 0ab4d9015..8201c965e 100644 --- a/pallets/referrals/src/tests/trade_fee.rs +++ b/pallets/referrals/src/tests/trade_fee.rs @@ -5,10 +5,7 @@ use pretty_assertions::assert_eq; fn process_trade_fee_should_increased_referrer_shares() { ExtBuilder::default() .with_endowed_accounts(vec![(BOB, DAI, 2_000_000_000_000_000_000)]) - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_tiers(vec![( DAI, Level::Novice, @@ -37,10 +34,7 @@ fn process_trade_fee_should_increased_referrer_shares() { fn process_trade_fee_should_increased_trader_shares() { ExtBuilder::default() .with_endowed_accounts(vec![(BOB, DAI, 2_000_000_000_000_000_000)]) - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_tiers(vec![( DAI, Level::Novice, @@ -69,10 +63,7 @@ fn process_trade_fee_should_increased_trader_shares() { fn process_trade_fee_should_increased_total_share_issuance() { ExtBuilder::default() .with_endowed_accounts(vec![(BOB, DAI, 2_000_000_000_000_000_000)]) - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_tiers(vec![( DAI, Level::Novice, @@ -101,10 +92,7 @@ fn process_trade_fee_should_increased_total_share_issuance() { fn process_trade_fee_should_fail_when_taken_amount_is_greated_than_fee_amount() { ExtBuilder::default() .with_endowed_accounts(vec![(BOB, DAI, 2_000_000_000_000_000_000)]) - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_tiers(vec![( DAI, Level::Novice, @@ -132,10 +120,7 @@ fn process_trade_fee_should_fail_when_taken_amount_is_greated_than_fee_amount() #[test] fn process_trade_should_not_increase_shares_when_trader_does_not_have_linked_account() { ExtBuilder::default() - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_shares(vec![(BOB, 1_000_000_000_000)]) .build() .execute_with(|| { @@ -160,10 +145,7 @@ fn process_trade_should_not_increase_shares_when_trader_does_not_have_linked_acc fn process_trade_fee_should_add_asset_to_asset_list() { ExtBuilder::default() .with_endowed_accounts(vec![(BOB, DAI, 2_000_000_000_000_000_000)]) - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_tiers(vec![( DAI, Level::Novice, @@ -192,10 +174,7 @@ fn process_trade_fee_should_add_asset_to_asset_list() { fn process_trade_fee_should_not_add_reward_asset_to_asset_list() { ExtBuilder::default() .with_endowed_accounts(vec![(BOB, HDX, 2_000_000_000_000)]) - .with_conversion_price( - (HDX, DAI), - FixedU128::from_rational(1_000_000_000_000, 1_000_000_000_000_000_000), - ) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_tiers(vec![( DAI, Level::Novice, diff --git a/runtime/adapters/src/lib.rs b/runtime/adapters/src/lib.rs index e4de47e07..268fcfbc2 100644 --- a/runtime/adapters/src/lib.rs +++ b/runtime/adapters/src/lib.rs @@ -61,6 +61,7 @@ use xcm_executor::{ }; pub mod inspect; +pub mod price; pub mod xcm_exchange; pub mod xcm_execute_filter; diff --git a/runtime/adapters/src/price.rs b/runtime/adapters/src/price.rs new file mode 100644 index 000000000..33c9191d0 --- /dev/null +++ b/runtime/adapters/src/price.rs @@ -0,0 +1,21 @@ +use hydradx_traits::price::PriceProvider; +use hydradx_traits::router::{AssetPair, RouteProvider}; +use hydradx_traits::{OraclePeriod, PriceOracle}; +use sp_core::Get; +use sp_std::marker::PhantomData; + +pub struct OraclePriceProviderUsingRoute(PhantomData<(RP, OP, P)>); + +impl PriceProvider for OraclePriceProviderUsingRoute +where + RP: RouteProvider, + OP: PriceOracle, + P: Get, +{ + type Price = OP::Price; + + fn get_price(asset_a: AssetId, asset_b: AssetId) -> Option { + let route = RP::get_route(AssetPair::new(asset_a, asset_b)); + OP::price(&route, P::get()) + } +} diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 1521f38a2..41b573926 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -798,6 +798,7 @@ where use pallet_currencies::fungibles::FungibleCurrencies; +use hydradx_adapters::price::OraclePriceProviderUsingRoute; use pallet_referrals::traits::Convert; use pallet_referrals::Level; #[cfg(feature = "runtime-benchmarks")] @@ -993,6 +994,7 @@ parameter_types! { pub const ReferralsPalletId: PalletId = PalletId(*b"referral"); pub RegistrationFee: (AssetId,Balance, AccountId)= (NativeAssetId::get(), 1_000_000_000_000, TreasuryAccount::get()); pub const MaxCodeLength: u32 = 7; + pub const ReferralsOraclePeriod: OraclePeriod = OraclePeriod::Short; } impl pallet_referrals::Config for Runtime { @@ -1001,7 +1003,8 @@ impl pallet_referrals::Config for Runtime { type AssetId = AssetId; type Currency = FungibleCurrencies; type Convert = ConvertViaOmnipool; - type SpotPriceProvider = Omnipool; + type PriceProvider = + OraclePriceProviderUsingRoute, ReferralsOraclePeriod>; type RewardAsset = NativeAssetId; type PalletId = ReferralsPalletId; type RegistrationFee = RegistrationFee; diff --git a/traits/src/lib.rs b/traits/src/lib.rs index 3386fba63..fa91200de 100644 --- a/traits/src/lib.rs +++ b/traits/src/lib.rs @@ -26,6 +26,8 @@ pub mod router; pub use registry::*; pub mod oracle; +pub mod price; + pub use oracle::*; use codec::{Decode, Encode}; diff --git a/traits/src/price.rs b/traits/src/price.rs new file mode 100644 index 000000000..1dbbb6807 --- /dev/null +++ b/traits/src/price.rs @@ -0,0 +1,5 @@ +pub trait PriceProvider { + type Price; + + fn get_price(asset_a: AssetId, asset_b: AssetId) -> Option; +} From 20bba1952e265f077b5e1110c8480f564f9dfdb4 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Mon, 11 Dec 2023 09:06:08 +0100 Subject: [PATCH 48/90] fix integration tests --- integration-tests/src/referrals.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/integration-tests/src/referrals.rs b/integration-tests/src/referrals.rs index 20d4045f7..ef6d5cb62 100644 --- a/integration-tests/src/referrals.rs +++ b/integration-tests/src/referrals.rs @@ -63,7 +63,7 @@ fn trading_in_omnipool_should_increase_referrer_shares() { 0 )); let referrer_shares = Referrals::account_shares::(ALICE.into()); - assert_eq!(referrer_shares, 51_354_392); + assert_eq!(referrer_shares, 51_399_742); }); } #[test] @@ -85,7 +85,7 @@ fn trading_in_omnipool_should_increase_trader_shares() { 0 )); let referrer_shares = Referrals::account_shares::(BOB.into()); - assert_eq!(referrer_shares, 25_677_196); + assert_eq!(referrer_shares, 25_699_871); }); } @@ -108,12 +108,12 @@ fn trading_in_omnipool_should_increase_total_shares_correctly() { 0 )); let referrer_shares = Referrals::total_shares(); - assert_eq!(referrer_shares, 25_677_196 + 51_354_392); + assert_eq!(referrer_shares, 25_699_871 + 51_399_742); }); } #[test] -fn trading_hdx_in_omnipool_should_increase_trader_shares() { +fn trading_hdx_in_omnipool_should_work_when_fee_is_below_existential_deposit() { Hydra::execute_with(|| { init_omnipool_with_oracle_for_block_10(); init_referrals_program(); From 78708ce50381cb3a359c888cde87690a30ca11d8 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Mon, 11 Dec 2023 10:53:02 +0100 Subject: [PATCH 49/90] add seed amount param and exclude it from total rewards --- integration-tests/src/referrals.rs | 40 +++++++++++++++++++++++++++++- pallets/referrals/src/lib.rs | 5 ++++ runtime/hydradx/src/assets.rs | 2 ++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/integration-tests/src/referrals.rs b/integration-tests/src/referrals.rs index ef6d5cb62..f08f2967c 100644 --- a/integration-tests/src/referrals.rs +++ b/integration-tests/src/referrals.rs @@ -112,6 +112,33 @@ fn trading_in_omnipool_should_increase_total_shares_correctly() { }); } +#[test] +fn claiming_rewards_should_convert_all_assets_to_reward_asset() { + Hydra::execute_with(|| { + init_omnipool_with_oracle_for_block_10(); + init_referrals_program(); + let code = b"BALLS69".to_vec(); + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE.into()), + code.clone() + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB.into()), code)); + assert_ok!(Omnipool::sell( + RuntimeOrigin::signed(BOB.into()), + HDX, + DAI, + 1_000_000_000_000, + 0 + )); + let pot_balance = Currencies::free_balance(DAI, &Referrals::pot_account_id()); + assert!(pot_balance > 0); + + assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE.into()))); + let pot_balance = Currencies::free_balance(DAI, &Referrals::pot_account_id()); + assert_eq!(pot_balance, 0); + }); +} + #[test] fn trading_hdx_in_omnipool_should_work_when_fee_is_below_existential_deposit() { Hydra::execute_with(|| { @@ -131,7 +158,7 @@ fn trading_hdx_in_omnipool_should_work_when_fee_is_below_existential_deposit() { 0 )); let referrer_shares = Referrals::account_shares::(BOB.into()); - assert_eq!(referrer_shares, 25_677_196); + assert_eq!(referrer_shares, 9_870_477_594); }); } @@ -168,6 +195,15 @@ fn do_trade_to_populate_oracle(asset_1: AssetId, asset_2: AssetId, amount: Balan )); } +fn seed_pot_account() { + assert_ok!(Currencies::update_balance( + RawOrigin::Root.into(), + Referrals::pot_account_id(), + HDX, + (100 * UNITS) as i128, + )); +} + fn init_referrals_program() { assert_ok!(Referrals::set_reward_percentage( RuntimeOrigin::root(), @@ -211,4 +247,6 @@ fn init_referrals_program() { Permill::from_percent(10), Permill::from_percent(5), )); + + seed_pot_account(); } diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 9ed7e0c88..48dacf855 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -158,6 +158,10 @@ pub mod pallet { /// Volume needed to next tier. If None returned, it is the last tier. type TierVolume: GetByKey>; + /// Seed amount that was sent to the reward pot. + #[pallet::constant] + type SeedNativeAmount: Get; + /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; @@ -404,6 +408,7 @@ pub mod pallet { } let reward_reserve = T::Currency::balance(T::RewardAsset::get(), &Self::pot_account_id()); + let reward_reserve = reward_reserve.saturating_sub(T::SeedNativeAmount::get()); let share_issuance = TotalShares::::get(); let rewards = || -> Option { diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 41b573926..8859b975a 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -995,6 +995,7 @@ parameter_types! { pub RegistrationFee: (AssetId,Balance, AccountId)= (NativeAssetId::get(), 1_000_000_000_000, TreasuryAccount::get()); pub const MaxCodeLength: u32 = 7; pub const ReferralsOraclePeriod: OraclePeriod = OraclePeriod::Short; + pub const ReferralsSeedAmount: Balance = 100_000_000_000_000; } impl pallet_referrals::Config for Runtime { @@ -1010,6 +1011,7 @@ impl pallet_referrals::Config for Runtime { type RegistrationFee = RegistrationFee; type CodeLength = MaxCodeLength; type TierVolume = ReferralsLevelTiers; + type SeedNativeAmount = ReferralsSeedAmount; type WeightInfo = (); #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = (); From e08027e89162293ba1b99f7c74ebb6cefeace1c3 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Mon, 11 Dec 2023 11:01:37 +0100 Subject: [PATCH 50/90] tests for seed amount --- pallets/referrals/src/tests.rs | 28 ++++++++++++++++++++++++++++ pallets/referrals/src/tests/claim.rs | 22 ++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index de9c009e8..94b06409a 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -32,6 +32,7 @@ use std::collections::HashMap; use frame_support::{ construct_runtime, parameter_types, + sp_runtime::traits::Zero, sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, @@ -74,6 +75,7 @@ pub(crate) const INITIAL_ALICE_BALANCE: Balance = 1_000 * ONE; thread_local! { pub static CONVERSION_RATE: RefCell> = RefCell::new(HashMap::default()); pub static TIER_VOLUME: RefCell>> = RefCell::new(HashMap::default()); + pub static SEED_AMOUNT: RefCell = RefCell::new(Balance::zero()); } construct_runtime!( @@ -114,6 +116,14 @@ impl GetByKey> for Volume { } } +pub struct SeedAmount; + +impl Get for SeedAmount { + fn get() -> Balance { + SEED_AMOUNT.with(|v| v.borrow().clone()) + } +} + impl Config for Test { type RuntimeEvent = RuntimeEvent; type AuthorityOrigin = EnsureRoot; @@ -126,6 +136,7 @@ impl Config for Test { type RegistrationFee = RegistrationFee; type CodeLength = CodeLength; type TierVolume = Volume; + type SeedNativeAmount = SeedAmount; type WeightInfo = (); #[cfg(feature = "runtime-benchmarks")] @@ -197,6 +208,11 @@ impl Default for ExtBuilder { CONVERSION_RATE.with(|v| { v.borrow_mut().clear(); }); + + SEED_AMOUNT.with(|v| { + let mut c = v.borrow_mut(); + *c = 0u128; + }); Self { endowed_accounts: vec![(ALICE, HDX, INITIAL_ALICE_BALANCE)], shares: vec![], @@ -233,6 +249,13 @@ impl ExtBuilder { }); self } + pub fn with_seed_amount(self, amount: Balance) -> Self { + SEED_AMOUNT.with(|v| { + let mut m = v.borrow_mut(); + *m = amount; + }); + self + } pub fn with_tier_volumes(self, volumes: HashMap>) -> Self { TIER_VOLUME.with(|v| { @@ -275,6 +298,11 @@ impl ExtBuilder { Assets::::insert(asset, ()); } }); + r.execute_with(|| { + let seed_amount = SEED_AMOUNT.with(|v| v.borrow().clone()); + Tokens::update_balance(HDX, &Referrals::pot_account_id(), seed_amount as i128); + }); + r.execute_with(|| { System::set_block_number(1); }); diff --git a/pallets/referrals/src/tests/claim.rs b/pallets/referrals/src/tests/claim.rs index 6d2c004d2..e8bb8213f 100644 --- a/pallets/referrals/src/tests/claim.rs +++ b/pallets/referrals/src/tests/claim.rs @@ -133,6 +133,28 @@ fn claim_rewards_update_total_accumulated_for_referrer_account() { }); } +#[test] +fn claim_rewards_should_exclude_seed_amount() { + ExtBuilder::default() + .with_endowed_accounts(vec![(Pallet::::pot_account_id(), HDX, 20_000_000_000_000)]) + .with_shares(vec![(BOB, 5_000_000_000_000), (ALICE, 15_000_000_000_000)]) + .with_seed_amount(100_000_000_000_000) + .build() + .execute_with(|| { + // ARRANGE + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"BALLS69".to_vec(), + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + // Act + assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE))); + // Assert + let (_, total) = Referrer::::get(ALICE).unwrap(); + assert_eq!(total, 15_000_000_000_000); + }); +} + #[test] fn claim_rewards_should_increase_referrer_level_when_limit_is_reached() { let mut volumes = HashMap::new(); From 5ab6809c0522f924eb681b26028dbbbac8195497 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Mon, 11 Dec 2023 14:46:57 +0100 Subject: [PATCH 51/90] add pallet to benchmarks --- pallets/referrals/src/benchmarking.rs | 12 +++---- pallets/referrals/src/lib.rs | 2 -- pallets/referrals/src/tests.rs | 2 +- pallets/referrals/src/traits.rs | 2 ++ runtime/hydradx/src/assets.rs | 51 ++++++++++++++++++++++++++- runtime/hydradx/src/lib.rs | 2 ++ scripts/benchmark.all.sh | 1 + 7 files changed, 62 insertions(+), 10 deletions(-) diff --git a/pallets/referrals/src/benchmarking.rs b/pallets/referrals/src/benchmarking.rs index 9d3da166c..31507be38 100644 --- a/pallets/referrals/src/benchmarking.rs +++ b/pallets/referrals/src/benchmarking.rs @@ -33,9 +33,9 @@ benchmarks! { let caller: T::AccountId = account("caller", 0, 1); let code = vec![b'x'; T::CodeLength::get() as usize]; let (asset, fee, _) = T::RegistrationFee::get(); - T::Currency::mint_into(asset, &caller, fee)?; + T::Currency::mint_into(asset, &caller, 2 * fee)?; - }: _(RawOrigin::Signed(caller.clone()), code.clone(), caller.clone()) + }: _(RawOrigin::Signed(caller.clone()), code.clone()) verify { let entry = Pallet::::referrer_level(caller.clone()); assert_eq!(entry, Some((Level::Novice, 0))); @@ -49,8 +49,8 @@ benchmarks! { let user: T::AccountId = account("user", 0, 1); let code = vec![b'x'; T::CodeLength::get() as usize]; let (asset, fee, _) = T::RegistrationFee::get(); - T::Currency::mint_into(asset, &caller, fee)?; - Pallet::::register_code(RawOrigin::Signed(caller.clone()).into(), code.clone(), caller.clone())?; + T::Currency::mint_into(asset, &caller, 2 * fee)?; + Pallet::::register_code(RawOrigin::Signed(caller.clone()).into(), code.clone())?; }: _(RawOrigin::Signed(user.clone()), code) verify { let entry = Pallet::::linked_referral_account(user); @@ -74,8 +74,8 @@ benchmarks! { let caller: T::AccountId = account("caller", 0, 1); let code = vec![b'x'; T::CodeLength::get() as usize]; let (asset, fee, _) = T::RegistrationFee::get(); - T::Currency::mint_into(asset, &caller, fee)?; - Pallet::::register_code(RawOrigin::Signed(caller.clone()).into(), code, caller.clone())?; + T::Currency::mint_into(asset, &caller, 2 * fee)?; + Pallet::::register_code(RawOrigin::Signed(caller.clone()).into(), code)?; // The worst case is when referrer account is updated to the top tier in one call // So we need to have enough RewardAsset in the pot. And give all the shares to the caller. diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 48dacf855..03a0745b6 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -292,7 +292,6 @@ pub mod pallet { ensure!(code.len() >= MIN_CODE_LENGTH, Error::::TooShort); - //TODO: can we do without cloning ? or perhaps merge with normalization ensure!( code.clone() .into_inner() @@ -500,7 +499,6 @@ pub mod pallet { } impl Pallet { - //TODO: when added to runtime, make sure the account is added to the whitelist of account that cannot be dusted pub fn pot_account_id() -> T::AccountId { T::PalletId::get().into_account_truncating() } diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 94b06409a..4168e0256 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -300,7 +300,7 @@ impl ExtBuilder { }); r.execute_with(|| { let seed_amount = SEED_AMOUNT.with(|v| v.borrow().clone()); - Tokens::update_balance(HDX, &Referrals::pot_account_id(), seed_amount as i128); + Tokens::update_balance(HDX, &Referrals::pot_account_id(), seed_amount as i128).unwrap(); }); r.execute_with(|| { diff --git a/pallets/referrals/src/traits.rs b/pallets/referrals/src/traits.rs index f696c8734..300cc6403 100644 --- a/pallets/referrals/src/traits.rs +++ b/pallets/referrals/src/traits.rs @@ -7,5 +7,7 @@ pub trait Convert { #[cfg(feature = "runtime-benchmarks")] pub trait BenchmarkHelper { + // Should prepare everything that provides price for selected asset + // Amount returned is minted into pot account in benchmarks. fn prepare_convertible_asset_and_amount() -> (AssetId, Balance); } diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 8859b975a..d955469ad 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -798,7 +798,10 @@ where use pallet_currencies::fungibles::FungibleCurrencies; +#[cfg(not(feature = "runtime-benchmarks"))] use hydradx_adapters::price::OraclePriceProviderUsingRoute; + +use hydradx_traits::price::PriceProvider; use pallet_referrals::traits::Convert; use pallet_referrals::Level; #[cfg(feature = "runtime-benchmarks")] @@ -1004,8 +1007,12 @@ impl pallet_referrals::Config for Runtime { type AssetId = AssetId; type Currency = FungibleCurrencies; type Convert = ConvertViaOmnipool; + #[cfg(not(feature = "runtime-benchmarks"))] type PriceProvider = OraclePriceProviderUsingRoute, ReferralsOraclePeriod>; + + #[cfg(feature = "runtime-benchmarks")] + type PriceProvider = ReferralsDummyPriceProvider; type RewardAsset = NativeAssetId; type PalletId = ReferralsPalletId; type RegistrationFee = RegistrationFee; @@ -1014,7 +1021,7 @@ impl pallet_referrals::Config for Runtime { type SeedNativeAmount = ReferralsSeedAmount; type WeightInfo = (); #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = (); + type BenchmarkHelper = ReferralsBenchmarkHelper; } pub struct ConvertViaOmnipool; @@ -1046,3 +1053,45 @@ impl GetByKey> for ReferralsLevelTiers { } } } + +#[cfg(feature = "runtime-benchmarks")] +use pallet_referrals::BenchmarkHelper as RefBenchmarkHelper; + +#[cfg(feature = "runtime-benchmarks")] +pub struct ReferralsBenchmarkHelper; + +#[cfg(feature = "runtime-benchmarks")] +impl RefBenchmarkHelper for ReferralsBenchmarkHelper { + fn prepare_convertible_asset_and_amount() -> (AssetId, Balance) { + let asset_id: u32 = 1234u32; + let asset_name = asset_id.to_le_bytes().to_vec(); + let name: BoundedVec = asset_name.clone().try_into().unwrap(); + + AssetRegistry::register_asset( + name, + pallet_asset_registry::AssetType::::Token, + 1_000_000, + Some(asset_id), + None, + ) + .unwrap(); + (1234, 1_000_000_000_000_000_000) + //AssetRegistry::set_metadata(RuntimeOrigin::root(), asset_id, asset_name, 18).unwrap(); + //(asset_id, 1_000_000_000_000_000_000) + } +} + +#[cfg(feature = "runtime-benchmarks")] +pub struct ReferralsDummyPriceProvider; + +#[cfg(feature = "runtime-benchmarks")] +impl PriceProvider for ReferralsDummyPriceProvider { + type Price = EmaPrice; + + fn get_price(asset_a: AssetId, asset_b: AssetId) -> Option { + if asset_a == asset_b { + return Some(EmaPrice::one()); + } + Some(EmaPrice::new(1_000_000_000_000, 2_000_000_000_000_000_0000)) + } +} diff --git a/runtime/hydradx/src/lib.rs b/runtime/hydradx/src/lib.rs index a9fb0bee2..4909fbb76 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -550,6 +550,7 @@ impl_runtime_apis! { list_benchmark!(list, extra, pallet_staking, Staking); list_benchmark!(list, extra, pallet_lbp, LBP); list_benchmark!(list, extra, pallet_xyk, XYK); + list_benchmark!(list, extra, pallet_referrals, Referrals); list_benchmark!(list, extra, cumulus_pallet_xcmp_queue, XcmpQueue); list_benchmark!(list, extra, pallet_transaction_pause, TransactionPause); @@ -620,6 +621,7 @@ impl_runtime_apis! { add_benchmark!(params, batches, pallet_lbp, LBP); add_benchmark!(params, batches, pallet_xyk, XYK); add_benchmark!(params, batches, pallet_stableswap, Stableswap); + add_benchmark!(params, batches, pallet_referrals, Referrals); add_benchmark!(params, batches, cumulus_pallet_xcmp_queue, XcmpQueue); add_benchmark!(params, batches, pallet_transaction_pause, TransactionPause); diff --git a/scripts/benchmark.all.sh b/scripts/benchmark.all.sh index dc446f8e1..060268a5f 100644 --- a/scripts/benchmark.all.sh +++ b/scripts/benchmark.all.sh @@ -32,6 +32,7 @@ pallets=("frame-system:system" "pallet-route-executor:route_executor" "pallet-stableswap:stableswap" "pallet-staking:staking" +"pallet-referrals:referrals" ) command="cargo run --bin hydradx --release --features=runtime-benchmarks -- benchmark pallet --pallet=[pallet] --execution=wasm --wasm-execution=compiled --heap-pages=4096 --chain=dev --extrinsic='*' --steps=5 --repeat=20 --output [output].rs --template .maintain/pallet-weight-template-no-back.hbs" From c8468e4f4ec5a709229e356b66f3b7d51685f8e4 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Tue, 12 Dec 2023 10:44:10 +0100 Subject: [PATCH 52/90] referalls benchmarks adjusted and added weights --- pallets/referrals/src/benchmarking.rs | 8 +- pallets/referrals/src/weights.rs | 195 ++++++++++++++++++++++---- runtime/hydradx/src/assets.rs | 40 +++++- 3 files changed, 209 insertions(+), 34 deletions(-) diff --git a/pallets/referrals/src/benchmarking.rs b/pallets/referrals/src/benchmarking.rs index 31507be38..da6ae5f1a 100644 --- a/pallets/referrals/src/benchmarking.rs +++ b/pallets/referrals/src/benchmarking.rs @@ -76,11 +76,12 @@ benchmarks! { let (asset, fee, _) = T::RegistrationFee::get(); T::Currency::mint_into(asset, &caller, 2 * fee)?; Pallet::::register_code(RawOrigin::Signed(caller.clone()).into(), code)?; + let caller_balance = T::Currency::balance(T::RewardAsset::get(), &caller); // The worst case is when referrer account is updated to the top tier in one call // So we need to have enough RewardAsset in the pot. And give all the shares to the caller. let top_tier_volume = T::TierVolume::get(&Level::Advanced).expect("to have all level configured"); - T::Currency::mint_into(T::RewardAsset::get(), &Pallet::::pot_account_id(), top_tier_volume)?; + T::Currency::mint_into(T::RewardAsset::get(), &Pallet::::pot_account_id(), top_tier_volume + T::SeedNativeAmount::get())?; Shares::::insert(caller.clone(), 1_000_000_000_000); TotalShares::::put(1_000_000_000_000); }: _(RawOrigin::Signed(caller.clone())) @@ -88,10 +89,10 @@ benchmarks! { let count = Assets::::iter().count(); assert_eq!(count , 0); let balance = T::Currency::balance(T::RewardAsset::get(), &caller); - assert_eq!(balance, 1000000000); + assert!(balance > caller_balance); let (level, total) = Referrer::::get(&caller).expect("correct entry"); assert_eq!(level, Level::Expert); - assert_eq!(total, 1000000000); + assert_eq!(total, top_tier_volume); } set_reward_percentage{ @@ -105,7 +106,6 @@ benchmarks! { trader: trader_percentage, })); } - } #[cfg(test)] diff --git a/pallets/referrals/src/weights.rs b/pallets/referrals/src/weights.rs index e2b98244e..3cf7f4557 100644 --- a/pallets/referrals/src/weights.rs +++ b/pallets/referrals/src/weights.rs @@ -15,26 +15,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_bonds +//! Autogenerated weights for pallet_referrals //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-08-11, STEPS: 10, REPEAT: 30, LOW RANGE: [], HIGH RANGE: [] +//! DATE: 2023-12-12, STEPS: 5, REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // target/release/hydradx // benchmark // pallet -// --chain=dev -// --steps=10 -// --repeat=30 +// --pallet=pallet_referrals // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --template=.maintain/pallet-weight-template.hbs -// --pallet=pallet-bonds -// --output=bonds.rs +// --chain=dev // --extrinsic=* +// --steps=5 +// --repeat=20 +// --output +// referrals.rs +// --template +// .maintain/pallet-weight-template-no-back.hbs #![allow(unused_parens)] #![allow(unused_imports)] @@ -55,50 +57,187 @@ pub trait WeightInfo { fn set_reward_percentage() -> Weight; } -/// Weights for pallet_bonds using the hydraDX node and recommended hardware. +/// Weights for pallet_referrals using the hydraDX node and recommended hardware. pub struct HydraWeight(PhantomData); impl WeightInfo for HydraWeight { + // Storage: Referrals ReferralCodes (r:1 w:1) + // Proof: Referrals ReferralCodes (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + // Storage: System Account (r:2 w:2) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Referrals Referrer (r:0 w:1) + // Proof: Referrals Referrer (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) fn register_code() -> Weight { - Weight::zero() + // Minimum execution time: 61_000 nanoseconds. + Weight::from_ref_time(62_000_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) + .saturating_add(T::DbWeight::get().writes(4 as u64)) } - + // Storage: Referrals ReferralCodes (r:1 w:0) + // Proof: Referrals ReferralCodes (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + // Storage: Referrals LinkedAccounts (r:1 w:1) + // Proof: Referrals LinkedAccounts (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) fn link_code() -> Weight { - Weight::zero() + // Minimum execution time: 26_000 nanoseconds. + Weight::from_ref_time(27_000_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) } - + // Storage: Tokens Accounts (r:2 w:2) + // Proof: Tokens Accounts (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + // Storage: System Account (r:2 w:2) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Omnipool Assets (r:2 w:2) + // Proof: Omnipool Assets (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) + // Storage: Omnipool HubAssetImbalance (r:1 w:1) + // Proof: Omnipool HubAssetImbalance (max_values: Some(1), max_size: Some(17), added: 512, mode: MaxEncodedLen) + // Storage: DynamicFees AssetFee (r:1 w:0) + // Proof: DynamicFees AssetFee (max_values: None, max_size: Some(24), added: 2499, mode: MaxEncodedLen) + // Storage: EmaOracle Oracles (r:1 w:0) + // Proof: EmaOracle Oracles (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + // Storage: AssetRegistry Assets (r:1 w:0) + // Proof: AssetRegistry Assets (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) + // Storage: EmaOracle Accumulator (r:1 w:1) + // Proof: EmaOracle Accumulator (max_values: Some(1), max_size: Some(5921), added: 6416, mode: MaxEncodedLen) + // Storage: CircuitBreaker AllowedTradeVolumeLimitPerAsset (r:2 w:2) + // Proof: CircuitBreaker AllowedTradeVolumeLimitPerAsset (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + // Storage: CircuitBreaker TradeVolumeLimitPerAsset (r:2 w:0) + // Proof: CircuitBreaker TradeVolumeLimitPerAsset (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + // Storage: CircuitBreaker LiquidityAddLimitPerAsset (r:1 w:0) + // Proof: CircuitBreaker LiquidityAddLimitPerAsset (max_values: None, max_size: Some(29), added: 2504, mode: MaxEncodedLen) + // Storage: CircuitBreaker AllowedAddLiquidityAmountPerAsset (r:1 w:1) + // Proof: CircuitBreaker AllowedAddLiquidityAmountPerAsset (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + // Storage: CircuitBreaker LiquidityRemoveLimitPerAsset (r:1 w:0) + // Proof: CircuitBreaker LiquidityRemoveLimitPerAsset (max_values: None, max_size: Some(29), added: 2504, mode: MaxEncodedLen) + // Storage: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (r:1 w:1) + // Proof: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + // Storage: Referrals LinkedAccounts (r:1 w:0) + // Proof: Referrals LinkedAccounts (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + // Storage: Staking Staking (r:1 w:0) + // Proof: Staking Staking (max_values: Some(1), max_size: Some(48), added: 543, mode: MaxEncodedLen) + // Storage: MultiTransactionPayment AccountCurrencyMap (r:0 w:1) + // Proof: MultiTransactionPayment AccountCurrencyMap (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + // Storage: Referrals Assets (r:0 w:1) + // Proof: Referrals Assets (max_values: None, max_size: Some(20), added: 2495, mode: MaxEncodedLen) fn convert() -> Weight { - Weight::zero() + // Minimum execution time: 304_000 nanoseconds. + Weight::from_ref_time(307_000_000 as u64) + .saturating_add(T::DbWeight::get().reads(21 as u64)) + .saturating_add(T::DbWeight::get().writes(14 as u64)) } - + // Storage: Referrals Assets (r:1 w:0) + // Proof: Referrals Assets (max_values: None, max_size: Some(20), added: 2495, mode: MaxEncodedLen) + // Storage: Referrals Shares (r:1 w:1) + // Proof: Referrals Shares (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + // Storage: System Account (r:2 w:2) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Referrals TotalShares (r:1 w:1) + // Proof: Referrals TotalShares (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + // Storage: Referrals Referrer (r:1 w:1) + // Proof: Referrals Referrer (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) fn claim_rewards() -> Weight { - Weight::zero() + // Minimum execution time: 73_000 nanoseconds. + Weight::from_ref_time(74_000_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(5 as u64)) } - + // Storage: Referrals AssetTier (r:1 w:1) + // Proof: Referrals AssetTier (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) fn set_reward_percentage() -> Weight { - Weight::zero() + // Minimum execution time: 19_000 nanoseconds. + Weight::from_ref_time(19_000_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) } } -// For backwards compatibility and tests impl WeightInfo for () { + // Storage: Referrals ReferralCodes (r:1 w:1) + // Proof: Referrals ReferralCodes (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + // Storage: System Account (r:2 w:2) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Referrals Referrer (r:0 w:1) + // Proof: Referrals Referrer (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) fn register_code() -> Weight { - Weight::zero() + // Minimum execution time: 61_000 nanoseconds. + Weight::from_ref_time(62_000_000 as u64) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) + .saturating_add(RocksDbWeight::get().writes(4 as u64)) } - + // Storage: Referrals ReferralCodes (r:1 w:0) + // Proof: Referrals ReferralCodes (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + // Storage: Referrals LinkedAccounts (r:1 w:1) + // Proof: Referrals LinkedAccounts (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) fn link_code() -> Weight { - Weight::zero() + // Minimum execution time: 26_000 nanoseconds. + Weight::from_ref_time(27_000_000 as u64) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) } - + // Storage: Tokens Accounts (r:2 w:2) + // Proof: Tokens Accounts (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + // Storage: System Account (r:2 w:2) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Omnipool Assets (r:2 w:2) + // Proof: Omnipool Assets (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) + // Storage: Omnipool HubAssetImbalance (r:1 w:1) + // Proof: Omnipool HubAssetImbalance (max_values: Some(1), max_size: Some(17), added: 512, mode: MaxEncodedLen) + // Storage: DynamicFees AssetFee (r:1 w:0) + // Proof: DynamicFees AssetFee (max_values: None, max_size: Some(24), added: 2499, mode: MaxEncodedLen) + // Storage: EmaOracle Oracles (r:1 w:0) + // Proof: EmaOracle Oracles (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + // Storage: AssetRegistry Assets (r:1 w:0) + // Proof: AssetRegistry Assets (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) + // Storage: EmaOracle Accumulator (r:1 w:1) + // Proof: EmaOracle Accumulator (max_values: Some(1), max_size: Some(5921), added: 6416, mode: MaxEncodedLen) + // Storage: CircuitBreaker AllowedTradeVolumeLimitPerAsset (r:2 w:2) + // Proof: CircuitBreaker AllowedTradeVolumeLimitPerAsset (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + // Storage: CircuitBreaker TradeVolumeLimitPerAsset (r:2 w:0) + // Proof: CircuitBreaker TradeVolumeLimitPerAsset (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + // Storage: CircuitBreaker LiquidityAddLimitPerAsset (r:1 w:0) + // Proof: CircuitBreaker LiquidityAddLimitPerAsset (max_values: None, max_size: Some(29), added: 2504, mode: MaxEncodedLen) + // Storage: CircuitBreaker AllowedAddLiquidityAmountPerAsset (r:1 w:1) + // Proof: CircuitBreaker AllowedAddLiquidityAmountPerAsset (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + // Storage: CircuitBreaker LiquidityRemoveLimitPerAsset (r:1 w:0) + // Proof: CircuitBreaker LiquidityRemoveLimitPerAsset (max_values: None, max_size: Some(29), added: 2504, mode: MaxEncodedLen) + // Storage: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (r:1 w:1) + // Proof: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + // Storage: Referrals LinkedAccounts (r:1 w:0) + // Proof: Referrals LinkedAccounts (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + // Storage: Staking Staking (r:1 w:0) + // Proof: Staking Staking (max_values: Some(1), max_size: Some(48), added: 543, mode: MaxEncodedLen) + // Storage: MultiTransactionPayment AccountCurrencyMap (r:0 w:1) + // Proof: MultiTransactionPayment AccountCurrencyMap (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + // Storage: Referrals Assets (r:0 w:1) + // Proof: Referrals Assets (max_values: None, max_size: Some(20), added: 2495, mode: MaxEncodedLen) fn convert() -> Weight { - Weight::zero() + // Minimum execution time: 304_000 nanoseconds. + Weight::from_ref_time(307_000_000 as u64) + .saturating_add(RocksDbWeight::get().reads(21 as u64)) + .saturating_add(RocksDbWeight::get().writes(14 as u64)) } - + // Storage: Referrals Assets (r:1 w:0) + // Proof: Referrals Assets (max_values: None, max_size: Some(20), added: 2495, mode: MaxEncodedLen) + // Storage: Referrals Shares (r:1 w:1) + // Proof: Referrals Shares (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + // Storage: System Account (r:2 w:2) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Referrals TotalShares (r:1 w:1) + // Proof: Referrals TotalShares (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + // Storage: Referrals Referrer (r:1 w:1) + // Proof: Referrals Referrer (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) fn claim_rewards() -> Weight { - Weight::zero() + // Minimum execution time: 73_000 nanoseconds. + Weight::from_ref_time(74_000_000 as u64) + .saturating_add(RocksDbWeight::get().reads(6 as u64)) + .saturating_add(RocksDbWeight::get().writes(5 as u64)) } - + // Storage: Referrals AssetTier (r:1 w:1) + // Proof: Referrals AssetTier (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) fn set_reward_percentage() -> Weight { - Weight::zero() + // Minimum execution time: 19_000 nanoseconds. + Weight::from_ref_time(19_000_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) } } diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index d955469ad..105435ca2 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -1075,9 +1075,45 @@ impl RefBenchmarkHelper for ReferralsBenchmarkHelper { None, ) .unwrap(); + AssetRegistry::set_metadata(RuntimeOrigin::root(), asset_id, asset_name, 18).unwrap(); + + let native_price = FixedU128::from_inner(1201500000000000); + let asset_price = FixedU128::from_inner(45_000_000_000); + + Currencies::update_balance( + RuntimeOrigin::root(), + Omnipool::protocol_account(), + NativeAssetId::get(), + 1_000_000_000_000_000_000, + ) + .unwrap(); + + Currencies::update_balance( + RuntimeOrigin::root(), + Omnipool::protocol_account(), + asset_id, + 1_000_000_000_000_000_000_000_000, + ) + .unwrap(); + + Omnipool::add_token( + RuntimeOrigin::root(), + NativeAssetId::get(), + native_price, + Permill::from_percent(10), + TreasuryAccount::get(), + ) + .unwrap(); + + Omnipool::add_token( + RuntimeOrigin::root(), + asset_id, + asset_price, + Permill::from_percent(10), + TreasuryAccount::get(), + ) + .unwrap(); (1234, 1_000_000_000_000_000_000) - //AssetRegistry::set_metadata(RuntimeOrigin::root(), asset_id, asset_name, 18).unwrap(); - //(asset_id, 1_000_000_000_000_000_000) } } From c6c5a06947ebcd8a538c42984bc893300ee6772b Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Tue, 12 Dec 2023 10:57:24 +0100 Subject: [PATCH 53/90] bump versions --- Cargo.lock | 8 ++++---- integration-tests/Cargo.toml | 2 +- runtime/adapters/Cargo.toml | 2 +- runtime/hydradx/Cargo.toml | 2 +- runtime/hydradx/src/lib.rs | 2 +- traits/Cargo.toml | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9fa054218..69839ca0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4176,7 +4176,7 @@ dependencies = [ [[package]] name = "hydradx-adapters" -version = "0.6.7" +version = "0.6.8" dependencies = [ "cumulus-pallet-parachain-system", "cumulus-primitives-core", @@ -4223,7 +4223,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "194.0.0" +version = "195.0.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", @@ -4339,7 +4339,7 @@ dependencies = [ [[package]] name = "hydradx-traits" -version = "2.8.1" +version = "2.8.2" dependencies = [ "frame-support", "impl-trait-for-tuples", @@ -10791,7 +10791,7 @@ dependencies = [ [[package]] name = "runtime-integration-tests" -version = "1.16.4" +version = "1.16.5" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 462f702af..e8a6d0565 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runtime-integration-tests" -version = "1.16.4" +version = "1.16.5" description = "Integration tests" authors = ["GalacticCouncil"] edition = "2021" diff --git a/runtime/adapters/Cargo.toml b/runtime/adapters/Cargo.toml index e60a4bc6d..8920207d1 100644 --- a/runtime/adapters/Cargo.toml +++ b/runtime/adapters/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-adapters" -version = "0.6.7" +version = "0.6.8" description = "Structs and other generic types for building runtimes." authors = ["GalacticCouncil"] edition = "2021" diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index 9ba18e408..fb7ca6cc3 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "194.0.0" +version = "195.0.0" authors = ["GalacticCouncil"] edition = "2021" license = "Apache 2.0" diff --git a/runtime/hydradx/src/lib.rs b/runtime/hydradx/src/lib.rs index 4909fbb76..24b27431a 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -99,7 +99,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("hydradx"), impl_name: create_runtime_str!("hydradx"), authoring_version: 1, - spec_version: 194, + spec_version: 195, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/traits/Cargo.toml b/traits/Cargo.toml index fded7238d..efacdd11e 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-traits" -version = "2.8.1" +version = "2.8.2" description = "Shared traits" authors = ["GalacticCouncil"] edition = "2021" From d6f1ecc2d853d54e2c338ba16cbcc4fbac88c8f8 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Tue, 12 Dec 2023 11:03:29 +0100 Subject: [PATCH 54/90] add aditional test --- pallets/referrals/src/lib.rs | 23 +++++++++++++---------- pallets/referrals/src/tests/tiers.rs | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 03a0745b6..653360170 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -50,7 +50,7 @@ pub mod traits; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::pallet_prelude::{DispatchResult, Get}; use frame_support::traits::fungibles::Transfer; -use frame_support::{ensure, transactional, RuntimeDebug}; +use frame_support::{defensive, ensure, transactional, RuntimeDebug}; use frame_system::{ensure_signed, pallet_prelude::OriginFor}; use hydradx_traits::price::PriceProvider; use orml_traits::GetByKey; @@ -438,15 +438,17 @@ pub mod pallet { if let Some((level, total)) = v { *total = total.saturating_add(rewards); - let next_tier = T::TierVolume::get(level); - if let Some(amount_needed) = next_tier { - if *total >= amount_needed { - *level = level.next_level(); - // let's check if we can skip two levels - let next_tier = T::TierVolume::get(level); - if let Some(amount_needed) = next_tier { - if *total >= amount_needed { - *level = level.next_level(); + if *level != Level::Expert { + let next_tier = T::TierVolume::get(level); + if let Some(amount_needed) = next_tier { + if *total >= amount_needed { + *level = level.next_level(); + // let's check if we can skip two levels + let next_tier = T::TierVolume::get(level); + if let Some(amount_needed) = next_tier { + if *total >= amount_needed { + *level = level.next_level(); + } } } } @@ -527,6 +529,7 @@ impl Pallet { // What is the referer level? let Some((level,_)) = Self::referrer_level(&ref_account) else { // Should not really happen, the ref entry should be always there. + defensive!("Referrer details not found"); return Ok(amount); }; diff --git a/pallets/referrals/src/tests/tiers.rs b/pallets/referrals/src/tests/tiers.rs index 564fe8aa4..dc186561c 100644 --- a/pallets/referrals/src/tests/tiers.rs +++ b/pallets/referrals/src/tests/tiers.rs @@ -54,3 +54,23 @@ fn setting_asset_tier_should_fail_when_total_percentage_exceeds_hundred_percent( ); }); } + +#[test] +fn setting_asset_tier_should_emit_event() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(Referrals::set_reward_percentage( + RuntimeOrigin::root(), + DAI, + Level::Novice, + Permill::from_percent(1), + Permill::from_percent(2), + )); + expect_events(vec![Event::TierRewardSet { + asset_id: DAI, + level: Level::Novice, + referrer: Permill::from_percent(1), + trader: Permill::from_percent(2), + } + .into()]); + }); +} From 975484771f534a5cc4edbe0975b49dd79311d407 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Tue, 12 Dec 2023 11:09:47 +0100 Subject: [PATCH 55/90] Add to try runtime --- runtime/hydradx/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index fb7ca6cc3..e0c5d1f32 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -349,4 +349,5 @@ try-runtime= [ "pallet-evm/try-runtime", "pallet-evm-chain-id/try-runtime", "pallet-xyk/try-runtime", + "pallet-referrals/try-runtime", ] From 313d3dc52fa64c51b2363b04b208e3c34875acf4 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Tue, 12 Dec 2023 11:27:16 +0100 Subject: [PATCH 56/90] happy clippy happy life --- pallets/omnipool/src/lib.rs | 4 ++-- pallets/referrals/src/tests.rs | 4 ++-- runtime/adapters/src/lib.rs | 2 +- runtime/hydradx/src/assets.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index 6e7d09c54..7951e078a 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -1727,7 +1727,7 @@ impl Pallet { Self::set_asset_state(asset_out, new_asset_out_state); - Self::process_trade_fee(&who, asset_out, state_changes.fee.asset_fee)?; + Self::process_trade_fee(who, asset_out, state_changes.fee.asset_fee)?; Self::deposit_event(Event::SellExecuted { who: who.clone(), @@ -1833,7 +1833,7 @@ impl Pallet { Self::set_asset_state(asset_out, new_asset_out_state); - Self::process_trade_fee(&who, T::HubAssetId::get(), state_changes.fee.asset_fee)?; + Self::process_trade_fee(who, T::HubAssetId::get(), state_changes.fee.asset_fee)?; Self::deposit_event(Event::BuyExecuted { who: who.clone(), diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 4168e0256..51b94af08 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -120,7 +120,7 @@ pub struct SeedAmount; impl Get for SeedAmount { fn get() -> Balance { - SEED_AMOUNT.with(|v| v.borrow().clone()) + SEED_AMOUNT.with(|v| *v.borrow()) } } @@ -299,7 +299,7 @@ impl ExtBuilder { } }); r.execute_with(|| { - let seed_amount = SEED_AMOUNT.with(|v| v.borrow().clone()); + let seed_amount = SEED_AMOUNT.with(|v| *v.borrow()); Tokens::update_balance(HDX, &Referrals::pot_account_id(), seed_amount as i128).unwrap(); }); diff --git a/runtime/adapters/src/lib.rs b/runtime/adapters/src/lib.rs index 268fcfbc2..b6721abe3 100644 --- a/runtime/adapters/src/lib.rs +++ b/runtime/adapters/src/lib.rs @@ -471,7 +471,7 @@ where ) -> Result { let unused = pallet_referrals::Pallet::::process_trade_fee( fee_account.clone().into(), - trader.clone().into(), + trader.into(), asset.into(), amount, )?; diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 105435ca2..094c93c0b 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -1128,6 +1128,6 @@ impl PriceProvider for ReferralsDummyPriceProvider { if asset_a == asset_b { return Some(EmaPrice::one()); } - Some(EmaPrice::new(1_000_000_000_000, 2_000_000_000_000_000_0000)) + Some(EmaPrice::new(1_000_000_000_000, 2_000_000_000_000_000_000)) } } From 2e943d2d421eb6ec2c27fb108ca2d22ceaace293 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Tue, 12 Dec 2023 16:57:01 +0100 Subject: [PATCH 57/90] change reg fee to 222HDX --- runtime/hydradx/src/assets.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 094c93c0b..a49fa9c65 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -995,7 +995,7 @@ impl pallet_xyk::Config for Runtime { parameter_types! { pub const ReferralsPalletId: PalletId = PalletId(*b"referral"); - pub RegistrationFee: (AssetId,Balance, AccountId)= (NativeAssetId::get(), 1_000_000_000_000, TreasuryAccount::get()); + pub RegistrationFee: (AssetId,Balance, AccountId)= (NativeAssetId::get(), 222_000_000_000_000, TreasuryAccount::get()); pub const MaxCodeLength: u32 = 7; pub const ReferralsOraclePeriod: OraclePeriod = OraclePeriod::Short; pub const ReferralsSeedAmount: Balance = 100_000_000_000_000; @@ -1010,7 +1010,6 @@ impl pallet_referrals::Config for Runtime { #[cfg(not(feature = "runtime-benchmarks"))] type PriceProvider = OraclePriceProviderUsingRoute, ReferralsOraclePeriod>; - #[cfg(feature = "runtime-benchmarks")] type PriceProvider = ReferralsDummyPriceProvider; type RewardAsset = NativeAssetId; From 30aa1772303a47834dcc763581c9bb799e2d2579 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Wed, 13 Dec 2023 13:25:31 +0100 Subject: [PATCH 58/90] set levels --- runtime/hydradx/src/assets.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index a49fa9c65..13c7cf860 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -1046,8 +1046,8 @@ pub struct ReferralsLevelTiers; impl GetByKey> for ReferralsLevelTiers { fn get(k: &Level) -> Option { match k { - Level::Novice => Some(1_000_000_000_000), - Level::Advanced => Some(10_000_000_000_000), + Level::Novice => Some(10_000_000_000_000), + Level::Advanced => Some(100_000_000_000_000), Level::Expert => None, } } From 2d9727d27dd3f4b432da766cb3941cfbec043c27 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Wed, 13 Dec 2023 13:57:19 +0100 Subject: [PATCH 59/90] adjust omnipool benchmark --- runtime/hydradx/src/benchmarking/omnipool.rs | 21 ++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/runtime/hydradx/src/benchmarking/omnipool.rs b/runtime/hydradx/src/benchmarking/omnipool.rs index af8f3a7fe..d974e7680 100644 --- a/runtime/hydradx/src/benchmarking/omnipool.rs +++ b/runtime/hydradx/src/benchmarking/omnipool.rs @@ -1,4 +1,6 @@ -use crate::{AccountId, AssetId, AssetRegistry, Balance, EmaOracle, Omnipool, Runtime, RuntimeOrigin, System}; +use crate::{ + AccountId, AssetId, AssetRegistry, Balance, EmaOracle, Omnipool, Referrals, Runtime, RuntimeOrigin, System, +}; use super::*; @@ -193,9 +195,10 @@ runtime_benchmarks! { let token_amount = 200_000_000_000_000_u128; update_balance(token_id, &acc, token_amount); + update_balance(0, &owner, 1000_000_000_000_000_u128); // Add the token to the pool - Omnipool::add_token(RawOrigin::Root.into(), token_id, token_price, Permill::from_percent(100), owner)?; + Omnipool::add_token(RawOrigin::Root.into(), token_id, token_price, Permill::from_percent(100), owner.clone())?; // Create LP provider account with correct balance aand add some liquidity let lp_provider: AccountId = account("provider", 1, 1); @@ -218,6 +221,11 @@ runtime_benchmarks! { let amount_sell = 100_000_000_000_u128; let buy_min_amount = 10_000_000_000_u128; + // Register and link referral code to account for the weight too + let code = b"MYCODE".to_vec(); + Referrals::register_code(RawOrigin::Signed(owner).into(), code.clone())?; + Referrals::link_code(RawOrigin::Signed(seller.clone()).into(), code)?; + Referrals::set_reward_percentage(RawOrigin::Root.into(), DAI, pallet_referrals::Level::Novice, Permill::from_percent(1), Permill::from_percent(1))?; }: { Omnipool::sell(RawOrigin::Signed(seller.clone()).into(), token_id, DAI, amount_sell, buy_min_amount)? } verify { assert!(::Currency::free_balance(DAI, &seller) >= buy_min_amount); @@ -236,9 +244,10 @@ runtime_benchmarks! { let token_amount = 200_000_000_000_000_u128; update_balance(token_id, &acc, token_amount); + update_balance(0, &owner, 1000_000_000_000_000_u128); // Add the token to the pool - Omnipool::add_token(RawOrigin::Root.into(), token_id, token_price, Permill::from_percent(100), owner)?; + Omnipool::add_token(RawOrigin::Root.into(), token_id, token_price, Permill::from_percent(100), owner.clone())?; // Create LP provider account with correct balance aand add some liquidity let lp_provider: AccountId = account("provider", 1, 1); @@ -260,7 +269,11 @@ runtime_benchmarks! { let amount_buy = 1_000_000_000_000_u128; let sell_max_limit = 2_000_000_000_000_u128; - + // Register and link referral code to account for the weight too + let code = b"MYCODE".to_vec(); + Referrals::register_code(RawOrigin::Signed(owner).into(), code.clone())?; + Referrals::link_code(RawOrigin::Signed(seller.clone()).into(), code)?; + Referrals::set_reward_percentage(RawOrigin::Root.into(), token_id, pallet_referrals::Level::Novice, Permill::from_percent(1), Permill::from_percent(1))?; }: { Omnipool::buy(RawOrigin::Signed(seller.clone()).into(), DAI, token_id, amount_buy, sell_max_limit)? } verify { assert!(::Currency::free_balance(DAI, &seller) >= Balance::zero()); From 7cb4424188bb65f63fa02e53747add08eca1c63d Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Wed, 13 Dec 2023 14:09:36 +0100 Subject: [PATCH 60/90] allow only one code per account --- pallets/referrals/src/lib.rs | 15 +++++++++++++++ pallets/referrals/src/tests/register.rs | 18 ++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 653360170..285c4fdb2 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -176,6 +176,12 @@ pub mod pallet { pub(super) type ReferralCodes = StorageMap<_, Blake2_128Concat, ReferralCode, T::AccountId>; + /// Referral accounts + #[pallet::storage] + #[pallet::getter(fn referral_code)] + pub(super) type ReferralAccounts = + StorageMap<_, Blake2_128Concat, T::AccountId, ReferralCode>; + /// Linked accounts. /// Maps an account to a referral account. #[pallet::storage] @@ -267,6 +273,8 @@ pub mod pallet { IncorrectRewardCalculation, /// Given referrer and trader percentages exceeds 100% percent. IncorrectRewardPercentage, + /// The account has already a code registered. + AlreadyRegistered, } #[pallet::call] @@ -288,6 +296,12 @@ pub mod pallet { #[pallet::weight(::WeightInfo::register_code())] pub fn register_code(origin: OriginFor, code: Vec) -> DispatchResult { let who = ensure_signed(origin)?; + + ensure!( + ReferralAccounts::::get(&who).is_none(), + Error::::AlreadyRegistered + ); + let code: ReferralCode = code.try_into().map_err(|_| Error::::TooLong)?; ensure!(code.len() >= MIN_CODE_LENGTH, Error::::TooShort); @@ -310,6 +324,7 @@ pub mod pallet { *v = Some(who.clone()); Referrer::::insert(&who, (Level::default(), Balance::zero())); + ReferralAccounts::::insert(&who, code.clone()); Self::deposit_event(Event::CodeRegistered { code, account: who }); Ok(()) }) diff --git a/pallets/referrals/src/tests/register.rs b/pallets/referrals/src/tests/register.rs index dc551661e..64d46b49f 100644 --- a/pallets/referrals/src/tests/register.rs +++ b/pallets/referrals/src/tests/register.rs @@ -57,7 +57,7 @@ fn register_code_should_fail_when_code_already_exists() { )); // Act assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec()), + Referrals::register_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec()), Error::::AlreadyExists ); }); @@ -73,7 +73,7 @@ fn register_code_should_fail_when_code_is_lowercase_and_already_exists() { )); // Act assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"balls69".to_vec()), + Referrals::register_code(RuntimeOrigin::signed(BOB), b"balls69".to_vec()), Error::::AlreadyExists ); }); @@ -160,3 +160,17 @@ fn singer_should_set_default_level_for_referrer() { assert_eq!(entry, Some((Level::default(), Balance::zero()))); }); } + +#[test] +fn register_code_should_fail_when_account_has_already_code_registered() { + ExtBuilder::default().build().execute_with(|| { + // Arrange + let code = b"BALLS69".to_vec(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + let code = b"SECOND".to_vec(); + assert_noop!( + Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone()), + Error::::AlreadyRegistered + ); + }); +} From e14b0e451b6bdce43b02e0f308fec6716e32b547 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Wed, 13 Dec 2023 14:11:30 +0100 Subject: [PATCH 61/90] set min length to 5 --- pallets/referrals/src/lib.rs | 2 +- pallets/referrals/src/tests/register.rs | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 285c4fdb2..01eadadf5 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -73,7 +73,7 @@ use weights::WeightInfo; pub type Balance = u128; pub type ReferralCode = BoundedVec; -const MIN_CODE_LENGTH: usize = 3; +const MIN_CODE_LENGTH: usize = 5; /// Referrer level. /// Indicates current level of the referrer to determine which reward percentages are used. diff --git a/pallets/referrals/src/tests/register.rs b/pallets/referrals/src/tests/register.rs index 64d46b49f..245c1dc03 100644 --- a/pallets/referrals/src/tests/register.rs +++ b/pallets/referrals/src/tests/register.rs @@ -15,7 +15,10 @@ fn register_code_should_work_when_code_is_max_length() { #[test] fn register_code_should_work_when_code_is_min_length() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), b"ABC".to_vec(),)); + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE), + b"ABCDE".to_vec(), + )); }); } @@ -44,6 +47,14 @@ fn register_code_should_fail_when_code_is_too_short() { Referrals::register_code(RuntimeOrigin::signed(ALICE), b"AB".to_vec()), Error::::TooShort ); + assert_noop!( + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"ABC".to_vec()), + Error::::TooShort + ); + assert_noop!( + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"ABCD".to_vec()), + Error::::TooShort + ); }); } @@ -83,7 +94,7 @@ fn register_code_should_fail_when_code_is_lowercase_and_already_exists() { fn register_code_should_fail_when_code_contains_invalid_char() { ExtBuilder::default().build().execute_with(|| { assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"ABC?".to_vec()), + Referrals::register_code(RuntimeOrigin::signed(ALICE), b"ABCD?".to_vec()), Error::::InvalidCharacter ); }); From c0648362d661cba75130fa2a4e1579d54f5b94c2 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Wed, 13 Dec 2023 14:36:54 +0100 Subject: [PATCH 62/90] new weights for omnipool and referrals --- pallets/referrals/src/lib.rs | 2 +- runtime/hydradx/src/assets.rs | 3 +- runtime/hydradx/src/weights/mod.rs | 1 + runtime/hydradx/src/weights/omnipool.rs | 120 +++++++++++-------- runtime/hydradx/src/weights/referrals.rs | 140 +++++++++++++++++++++++ 5 files changed, 218 insertions(+), 48 deletions(-) create mode 100644 runtime/hydradx/src/weights/referrals.rs diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 01eadadf5..21637c5b7 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -39,7 +39,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -mod weights; +pub mod weights; #[cfg(any(feature = "runtime-benchmarks", test))] mod benchmarking; diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 13c7cf860..31cb5da7a 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -801,6 +801,7 @@ use pallet_currencies::fungibles::FungibleCurrencies; #[cfg(not(feature = "runtime-benchmarks"))] use hydradx_adapters::price::OraclePriceProviderUsingRoute; +#[cfg(feature = "runtime-benchmarks")] use hydradx_traits::price::PriceProvider; use pallet_referrals::traits::Convert; use pallet_referrals::Level; @@ -1018,7 +1019,7 @@ impl pallet_referrals::Config for Runtime { type CodeLength = MaxCodeLength; type TierVolume = ReferralsLevelTiers; type SeedNativeAmount = ReferralsSeedAmount; - type WeightInfo = (); + type WeightInfo = weights::referrals::HydraWeight; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = ReferralsBenchmarkHelper; } diff --git a/runtime/hydradx/src/weights/mod.rs b/runtime/hydradx/src/weights/mod.rs index 1b7827d67..4aa531d56 100644 --- a/runtime/hydradx/src/weights/mod.rs +++ b/runtime/hydradx/src/weights/mod.rs @@ -33,3 +33,4 @@ pub mod vesting; pub mod xcm; pub mod xcmp_queue; pub mod xyk; +pub mod referrals; diff --git a/runtime/hydradx/src/weights/omnipool.rs b/runtime/hydradx/src/weights/omnipool.rs index c70eff905..f205a52ef 100644 --- a/runtime/hydradx/src/weights/omnipool.rs +++ b/runtime/hydradx/src/weights/omnipool.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_omnipool //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-15, STEPS: 5, REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! DATE: 2023-12-13, STEPS: 5, REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -79,8 +79,8 @@ impl WeightInfo for HydraWeight { // Storage: Omnipool Positions (r:0 w:1) // Proof: Omnipool Positions (max_values: None, max_size: Some(100), added: 2575, mode: MaxEncodedLen) fn add_token() -> Weight { - // Minimum execution time: 147_434 nanoseconds. - Weight::from_ref_time(148_837_000 as u64) + // Minimum execution time: 146_335 nanoseconds. + Weight::from_ref_time(147_377_000 as u64) .saturating_add(T::DbWeight::get().reads(12 as u64)) .saturating_add(T::DbWeight::get().writes(10 as u64)) } @@ -121,8 +121,8 @@ impl WeightInfo for HydraWeight { // Storage: Omnipool Positions (r:0 w:1) // Proof: Omnipool Positions (max_values: None, max_size: Some(100), added: 2575, mode: MaxEncodedLen) fn add_liquidity() -> Weight { - // Minimum execution time: 224_128 nanoseconds. - Weight::from_ref_time(226_321_000 as u64) + // Minimum execution time: 219_924 nanoseconds. + Weight::from_ref_time(221_299_000 as u64) .saturating_add(T::DbWeight::get().reads(20 as u64)) .saturating_add(T::DbWeight::get().writes(14 as u64)) } @@ -165,12 +165,12 @@ impl WeightInfo for HydraWeight { // Storage: Uniques ItemPriceOf (r:0 w:1) // Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(113), added: 2588, mode: MaxEncodedLen) fn remove_liquidity() -> Weight { - // Minimum execution time: 298_601 nanoseconds. - Weight::from_ref_time(300_078_000 as u64) + // Minimum execution time: 293_052 nanoseconds. + Weight::from_ref_time(295_034_000 as u64) .saturating_add(T::DbWeight::get().reads(23 as u64)) .saturating_add(T::DbWeight::get().writes(16 as u64)) } - // Storage: Tokens Accounts (r:4 w:4) + // Storage: Tokens Accounts (r:5 w:5) // Proof: Tokens Accounts (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) // Storage: Omnipool Assets (r:3 w:3) // Proof: Omnipool Assets (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) @@ -180,9 +180,9 @@ impl WeightInfo for HydraWeight { // Proof: DynamicFees AssetFee (max_values: None, max_size: Some(24), added: 2499, mode: MaxEncodedLen) // Storage: AssetRegistry Assets (r:2 w:0) // Proof: AssetRegistry Assets (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) - // Storage: System Account (r:2 w:1) + // Storage: System Account (r:3 w:2) // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - // Storage: MultiTransactionPayment AccountCurrencyMap (r:1 w:1) + // Storage: MultiTransactionPayment AccountCurrencyMap (r:2 w:2) // Proof: MultiTransactionPayment AccountCurrencyMap (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) // Storage: MultiTransactionPayment AcceptedCurrencies (r:1 w:0) // Proof: MultiTransactionPayment AcceptedCurrencies (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) @@ -198,15 +198,27 @@ impl WeightInfo for HydraWeight { // Proof: CircuitBreaker LiquidityRemoveLimitPerAsset (max_values: None, max_size: Some(29), added: 2504, mode: MaxEncodedLen) // Storage: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (r:1 w:0) // Proof: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + // Storage: Referrals LinkedAccounts (r:1 w:0) + // Proof: Referrals LinkedAccounts (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + // Storage: Referrals Referrer (r:1 w:0) + // Proof: Referrals Referrer (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + // Storage: Referrals AssetTier (r:1 w:0) + // Proof: Referrals AssetTier (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + // Storage: Referrals TotalShares (r:1 w:1) + // Proof: Referrals TotalShares (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + // Storage: Referrals Shares (r:2 w:2) + // Proof: Referrals Shares (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + // Storage: Referrals Assets (r:0 w:1) + // Proof: Referrals Assets (max_values: None, max_size: Some(20), added: 2495, mode: MaxEncodedLen) fn sell() -> Weight { - // Minimum execution time: 252_248 nanoseconds. - Weight::from_ref_time(255_333_000 as u64) - .saturating_add(T::DbWeight::get().reads(22 as u64)) - .saturating_add(T::DbWeight::get().writes(14 as u64)) + // Minimum execution time: 318_696 nanoseconds. + Weight::from_ref_time(320_028_000 as u64) + .saturating_add(T::DbWeight::get().reads(31 as u64)) + .saturating_add(T::DbWeight::get().writes(21 as u64)) } // Storage: Omnipool Assets (r:3 w:3) // Proof: Omnipool Assets (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) - // Storage: Tokens Accounts (r:4 w:4) + // Storage: Tokens Accounts (r:5 w:5) // Proof: Tokens Accounts (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) // Storage: Omnipool HubAssetImbalance (r:1 w:1) // Proof: Omnipool HubAssetImbalance (max_values: Some(1), max_size: Some(17), added: 512, mode: MaxEncodedLen) @@ -216,11 +228,11 @@ impl WeightInfo for HydraWeight { // Proof: EmaOracle Oracles (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) // Storage: AssetRegistry Assets (r:2 w:0) // Proof: AssetRegistry Assets (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) - // Storage: System Account (r:2 w:1) + // Storage: System Account (r:3 w:2) // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - // Storage: MultiTransactionPayment AccountCurrencyMap (r:1 w:1) + // Storage: MultiTransactionPayment AccountCurrencyMap (r:2 w:1) // Proof: MultiTransactionPayment AccountCurrencyMap (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) - // Storage: MultiTransactionPayment AcceptedCurrencies (r:1 w:0) + // Storage: MultiTransactionPayment AcceptedCurrencies (r:2 w:0) // Proof: MultiTransactionPayment AcceptedCurrencies (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) // Storage: EmaOracle Accumulator (r:1 w:1) // Proof: EmaOracle Accumulator (max_values: Some(1), max_size: Some(5921), added: 6416, mode: MaxEncodedLen) @@ -234,17 +246,29 @@ impl WeightInfo for HydraWeight { // Proof: CircuitBreaker LiquidityRemoveLimitPerAsset (max_values: None, max_size: Some(29), added: 2504, mode: MaxEncodedLen) // Storage: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (r:1 w:0) // Proof: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + // Storage: Referrals LinkedAccounts (r:1 w:0) + // Proof: Referrals LinkedAccounts (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + // Storage: Referrals Referrer (r:1 w:0) + // Proof: Referrals Referrer (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + // Storage: Referrals AssetTier (r:1 w:0) + // Proof: Referrals AssetTier (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + // Storage: Referrals TotalShares (r:1 w:1) + // Proof: Referrals TotalShares (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + // Storage: Referrals Shares (r:2 w:2) + // Proof: Referrals Shares (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + // Storage: Referrals Assets (r:0 w:1) + // Proof: Referrals Assets (max_values: None, max_size: Some(20), added: 2495, mode: MaxEncodedLen) fn buy() -> Weight { - // Minimum execution time: 285_129 nanoseconds. - Weight::from_ref_time(291_349_000 as u64) - .saturating_add(T::DbWeight::get().reads(24 as u64)) - .saturating_add(T::DbWeight::get().writes(15 as u64)) + // Minimum execution time: 344_823 nanoseconds. + Weight::from_ref_time(347_210_000 as u64) + .saturating_add(T::DbWeight::get().reads(34 as u64)) + .saturating_add(T::DbWeight::get().writes(21 as u64)) } // Storage: Omnipool Assets (r:1 w:1) // Proof: Omnipool Assets (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) fn set_asset_tradable_state() -> Weight { - // Minimum execution time: 36_408 nanoseconds. - Weight::from_ref_time(36_896_000 as u64) + // Minimum execution time: 35_160 nanoseconds. + Weight::from_ref_time(35_515_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } @@ -261,8 +285,8 @@ impl WeightInfo for HydraWeight { // Storage: MultiTransactionPayment AcceptedCurrencies (r:1 w:0) // Proof: MultiTransactionPayment AcceptedCurrencies (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) fn refund_refused_asset() -> Weight { - // Minimum execution time: 111_080 nanoseconds. - Weight::from_ref_time(114_389_000 as u64) + // Minimum execution time: 108_179 nanoseconds. + Weight::from_ref_time(109_484_000 as u64) .saturating_add(T::DbWeight::get().reads(8 as u64)) .saturating_add(T::DbWeight::get().writes(5 as u64)) } @@ -279,16 +303,16 @@ impl WeightInfo for HydraWeight { // Storage: Uniques ItemPriceOf (r:0 w:1) // Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(113), added: 2588, mode: MaxEncodedLen) fn sacrifice_position() -> Weight { - // Minimum execution time: 78_568 nanoseconds. - Weight::from_ref_time(79_330_000 as u64) + // Minimum execution time: 77_117 nanoseconds. + Weight::from_ref_time(78_173_000 as u64) .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().writes(6 as u64)) } // Storage: Omnipool Assets (r:1 w:1) // Proof: Omnipool Assets (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) fn set_asset_weight_cap() -> Weight { - // Minimum execution time: 35_559 nanoseconds. - Weight::from_ref_time(36_075_000 as u64) + // Minimum execution time: 34_863 nanoseconds. + Weight::from_ref_time(35_340_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } @@ -311,8 +335,8 @@ impl WeightInfo for HydraWeight { // Storage: EmaOracle Accumulator (r:1 w:1) // Proof: EmaOracle Accumulator (max_values: Some(1), max_size: Some(5921), added: 6416, mode: MaxEncodedLen) fn withdraw_protocol_liquidity() -> Weight { - // Minimum execution time: 168_287 nanoseconds. - Weight::from_ref_time(171_781_000 as u64) + // Minimum execution time: 163_392 nanoseconds. + Weight::from_ref_time(164_478_000 as u64) .saturating_add(T::DbWeight::get().reads(13 as u64)) .saturating_add(T::DbWeight::get().writes(8 as u64)) } @@ -333,8 +357,8 @@ impl WeightInfo for HydraWeight { // Storage: MultiTransactionPayment AcceptedCurrencies (r:1 w:0) // Proof: MultiTransactionPayment AcceptedCurrencies (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) fn remove_token() -> Weight { - // Minimum execution time: 162_935 nanoseconds. - Weight::from_ref_time(167_702_000 as u64) + // Minimum execution time: 161_410 nanoseconds. + Weight::from_ref_time(162_579_000 as u64) .saturating_add(T::DbWeight::get().reads(14 as u64)) .saturating_add(T::DbWeight::get().writes(8 as u64)) } @@ -366,16 +390,18 @@ impl WeightInfo for HydraWeight { // Proof: CircuitBreaker LiquidityRemoveLimitPerAsset (max_values: None, max_size: Some(29), added: 2504, mode: MaxEncodedLen) // Storage: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (r:1 w:0) // Proof: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + // Storage: Referrals LinkedAccounts (r:1 w:0) + // Proof: Referrals LinkedAccounts (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) /// The range of component `c` is `[0, 1]`. /// The range of component `e` is `[0, 1]`. fn router_execution_sell(c: u32, e: u32) -> Weight { - // Minimum execution time: 47_302 nanoseconds. - Weight::from_ref_time(38_172_125 as u64) // Standard Error: 136_198 - .saturating_add(Weight::from_ref_time(10_307_875 as u64).saturating_mul(c as u64)) - // Standard Error: 136_198 - .saturating_add(Weight::from_ref_time(216_455_950 as u64).saturating_mul(e as u64)) + // Minimum execution time: 46_488 nanoseconds. + Weight::from_ref_time(36_800_250 as u64) // Standard Error: 121_871 + .saturating_add(Weight::from_ref_time(10_721_750 as u64).saturating_mul(c as u64)) + // Standard Error: 121_871 + .saturating_add(Weight::from_ref_time(220_720_900 as u64).saturating_mul(e as u64)) .saturating_add(T::DbWeight::get().reads(6 as u64)) - .saturating_add(T::DbWeight::get().reads((16 as u64).saturating_mul(e as u64))) + .saturating_add(T::DbWeight::get().reads((17 as u64).saturating_mul(e as u64))) .saturating_add(T::DbWeight::get().writes((14 as u64).saturating_mul(e as u64))) } // Storage: Omnipool Assets (r:3 w:3) @@ -408,15 +434,17 @@ impl WeightInfo for HydraWeight { // Proof: CircuitBreaker LiquidityRemoveLimitPerAsset (max_values: None, max_size: Some(29), added: 2504, mode: MaxEncodedLen) // Storage: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (r:1 w:0) // Proof: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + // Storage: Referrals LinkedAccounts (r:1 w:0) + // Proof: Referrals LinkedAccounts (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) /// The range of component `c` is `[1, 2]`. /// The range of component `e` is `[0, 1]`. fn router_execution_buy(c: u32, e: u32) -> Weight { - // Minimum execution time: 281_574 nanoseconds. - Weight::from_ref_time(269_724_625 as u64) // Standard Error: 346_676 - .saturating_add(Weight::from_ref_time(13_487_775 as u64).saturating_mul(c as u64)) - // Standard Error: 346_676 - .saturating_add(Weight::from_ref_time(558_675 as u64).saturating_mul(e as u64)) - .saturating_add(T::DbWeight::get().reads(24 as u64)) + // Minimum execution time: 284_953 nanoseconds. + Weight::from_ref_time(267_973_525 as u64) // Standard Error: 501_278 + .saturating_add(Weight::from_ref_time(14_832_175 as u64).saturating_mul(c as u64)) + // Standard Error: 501_278 + .saturating_add(Weight::from_ref_time(5_424_675 as u64).saturating_mul(e as u64)) + .saturating_add(T::DbWeight::get().reads(25 as u64)) .saturating_add(T::DbWeight::get().writes(15 as u64)) } } diff --git a/runtime/hydradx/src/weights/referrals.rs b/runtime/hydradx/src/weights/referrals.rs new file mode 100644 index 000000000..eb37fc229 --- /dev/null +++ b/runtime/hydradx/src/weights/referrals.rs @@ -0,0 +1,140 @@ +// This file is part of HydraDX. + +// Copyright (C) 2020-2023 Intergalactic, Limited (GIB). +// 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_referrals +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-13, STEPS: 5, REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/hydradx +// benchmark +// pallet +// --pallet=pallet-referrals +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --chain=dev +// --extrinsic=* +// --steps=5 +// --repeat=20 +// --output +// referrals.rs +// --template +// .maintain/pallet-weight-template-no-back.hbs + +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(clippy::unnecessary_cast)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +use pallet_referrals::weights::WeightInfo; + +/// Weights for pallet_referrals using the hydraDX node and recommended hardware. +pub struct HydraWeight(PhantomData); + +impl WeightInfo for HydraWeight { + // Storage: Referrals ReferralCodes (r:1 w:1) + // Proof: Referrals ReferralCodes (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + // Storage: System Account (r:2 w:2) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Referrals Referrer (r:0 w:1) + // Proof: Referrals Referrer (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + fn register_code() -> Weight { + // Minimum execution time: 42_186 nanoseconds. + Weight::from_ref_time(42_832_000 as u64) .saturating_add(T::DbWeight::get().reads(3 as u64)) + .saturating_add(T::DbWeight::get().writes(4 as u64)) + } + // Storage: Referrals ReferralCodes (r:1 w:0) + // Proof: Referrals ReferralCodes (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + // Storage: Referrals LinkedAccounts (r:1 w:1) + // Proof: Referrals LinkedAccounts (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + fn link_code() -> Weight { + // Minimum execution time: 21_324 nanoseconds. + Weight::from_ref_time(21_932_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: Tokens Accounts (r:2 w:2) + // Proof: Tokens Accounts (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + // Storage: System Account (r:2 w:2) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Omnipool Assets (r:2 w:2) + // Proof: Omnipool Assets (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) + // Storage: Omnipool HubAssetImbalance (r:1 w:1) + // Proof: Omnipool HubAssetImbalance (max_values: Some(1), max_size: Some(17), added: 512, mode: MaxEncodedLen) + // Storage: DynamicFees AssetFee (r:1 w:0) + // Proof: DynamicFees AssetFee (max_values: None, max_size: Some(24), added: 2499, mode: MaxEncodedLen) + // Storage: EmaOracle Oracles (r:1 w:0) + // Proof: EmaOracle Oracles (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + // Storage: AssetRegistry Assets (r:1 w:0) + // Proof: AssetRegistry Assets (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) + // Storage: EmaOracle Accumulator (r:1 w:1) + // Proof: EmaOracle Accumulator (max_values: Some(1), max_size: Some(5921), added: 6416, mode: MaxEncodedLen) + // Storage: CircuitBreaker AllowedTradeVolumeLimitPerAsset (r:2 w:2) + // Proof: CircuitBreaker AllowedTradeVolumeLimitPerAsset (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + // Storage: CircuitBreaker TradeVolumeLimitPerAsset (r:2 w:0) + // Proof: CircuitBreaker TradeVolumeLimitPerAsset (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + // Storage: CircuitBreaker LiquidityAddLimitPerAsset (r:1 w:0) + // Proof: CircuitBreaker LiquidityAddLimitPerAsset (max_values: None, max_size: Some(29), added: 2504, mode: MaxEncodedLen) + // Storage: CircuitBreaker AllowedAddLiquidityAmountPerAsset (r:1 w:1) + // Proof: CircuitBreaker AllowedAddLiquidityAmountPerAsset (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + // Storage: CircuitBreaker LiquidityRemoveLimitPerAsset (r:1 w:0) + // Proof: CircuitBreaker LiquidityRemoveLimitPerAsset (max_values: None, max_size: Some(29), added: 2504, mode: MaxEncodedLen) + // Storage: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (r:1 w:1) + // Proof: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + // Storage: Referrals LinkedAccounts (r:1 w:0) + // Proof: Referrals LinkedAccounts (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + // Storage: Staking Staking (r:1 w:0) + // Proof: Staking Staking (max_values: Some(1), max_size: Some(48), added: 543, mode: MaxEncodedLen) + // Storage: MultiTransactionPayment AccountCurrencyMap (r:0 w:1) + // Proof: MultiTransactionPayment AccountCurrencyMap (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + // Storage: Referrals Assets (r:0 w:1) + // Proof: Referrals Assets (max_values: None, max_size: Some(20), added: 2495, mode: MaxEncodedLen) + fn convert() -> Weight { + // Minimum execution time: 249_840 nanoseconds. + Weight::from_ref_time(251_054_000 as u64) .saturating_add(T::DbWeight::get().reads(21 as u64)) + .saturating_add(T::DbWeight::get().writes(14 as u64)) + } + // Storage: Referrals Assets (r:1 w:0) + // Proof: Referrals Assets (max_values: None, max_size: Some(20), added: 2495, mode: MaxEncodedLen) + // Storage: Referrals Shares (r:1 w:1) + // Proof: Referrals Shares (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + // Storage: System Account (r:2 w:2) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: Referrals TotalShares (r:1 w:1) + // Proof: Referrals TotalShares (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + // Storage: Referrals Referrer (r:1 w:1) + // Proof: Referrals Referrer (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + fn claim_rewards() -> Weight { + // Minimum execution time: 62_058 nanoseconds. + Weight::from_ref_time(62_527_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(5 as u64)) + } + // Storage: Referrals AssetTier (r:1 w:1) + // Proof: Referrals AssetTier (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) + fn set_reward_percentage() -> Weight { + // Minimum execution time: 15_537 nanoseconds. + Weight::from_ref_time(16_059_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } +} From 623693e23adcbea983e7759b4d137136bca023fb Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Wed, 13 Dec 2023 14:43:29 +0100 Subject: [PATCH 63/90] happy clippy happy life --- pallets/referrals/src/tests/register.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/referrals/src/tests/register.rs b/pallets/referrals/src/tests/register.rs index 245c1dc03..15f021e75 100644 --- a/pallets/referrals/src/tests/register.rs +++ b/pallets/referrals/src/tests/register.rs @@ -177,10 +177,10 @@ fn register_code_should_fail_when_account_has_already_code_registered() { ExtBuilder::default().build().execute_with(|| { // Arrange let code = b"BALLS69".to_vec(); - assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code)); let code = b"SECOND".to_vec(); assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone()), + Referrals::register_code(RuntimeOrigin::signed(ALICE), code), Error::::AlreadyRegistered ); }); From 3b5d9a833ca38684e678496d959f14a3414c6080 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Wed, 13 Dec 2023 14:50:33 +0100 Subject: [PATCH 64/90] reformat --- pallets/referrals/src/lib.rs | 2 - runtime/hydradx/src/weights/mod.rs | 2 +- runtime/hydradx/src/weights/referrals.rs | 59 +++++++++++++----------- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 21637c5b7..f35e04031 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -569,8 +569,6 @@ impl Pallet { let trader_shares = multiply_by_rational_with_rounding(trader_reward, price.n, price.d, Rounding::Down) .ok_or(ArithmeticError::Overflow)?; - //let referrer_shares = price.saturating_mul_int(referrer_reward); - //let trader_shares = price.saturating_mul_int(trader_reward); TotalShares::::mutate(|v| { *v = v.saturating_add(referrer_shares.saturating_add(trader_shares)); }); diff --git a/runtime/hydradx/src/weights/mod.rs b/runtime/hydradx/src/weights/mod.rs index 4aa531d56..9117b1c57 100644 --- a/runtime/hydradx/src/weights/mod.rs +++ b/runtime/hydradx/src/weights/mod.rs @@ -17,6 +17,7 @@ pub mod otc; pub mod payment; pub mod preimage; pub mod proxy; +pub mod referrals; pub mod registry; pub mod route_executor; pub mod scheduler; @@ -33,4 +34,3 @@ pub mod vesting; pub mod xcm; pub mod xcmp_queue; pub mod xyk; -pub mod referrals; diff --git a/runtime/hydradx/src/weights/referrals.rs b/runtime/hydradx/src/weights/referrals.rs index eb37fc229..e166658ec 100644 --- a/runtime/hydradx/src/weights/referrals.rs +++ b/runtime/hydradx/src/weights/referrals.rs @@ -43,8 +43,8 @@ #![allow(clippy::unnecessary_cast)] use frame_support::{ - traits::Get, - weights::{constants::RocksDbWeight, Weight}, + traits::Get, + weights::{constants::RocksDbWeight, Weight}, }; use sp_std::marker::PhantomData; @@ -60,20 +60,22 @@ impl WeightInfo for HydraWeight { // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) // Storage: Referrals Referrer (r:0 w:1) // Proof: Referrals Referrer (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) - fn register_code() -> Weight { - // Minimum execution time: 42_186 nanoseconds. - Weight::from_ref_time(42_832_000 as u64) .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) - } + fn register_code() -> Weight { + // Minimum execution time: 42_186 nanoseconds. + Weight::from_ref_time(42_832_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) + .saturating_add(T::DbWeight::get().writes(4 as u64)) + } // Storage: Referrals ReferralCodes (r:1 w:0) // Proof: Referrals ReferralCodes (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) // Storage: Referrals LinkedAccounts (r:1 w:1) // Proof: Referrals LinkedAccounts (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) - fn link_code() -> Weight { - // Minimum execution time: 21_324 nanoseconds. - Weight::from_ref_time(21_932_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } + fn link_code() -> Weight { + // Minimum execution time: 21_324 nanoseconds. + Weight::from_ref_time(21_932_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } // Storage: Tokens Accounts (r:2 w:2) // Proof: Tokens Accounts (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) // Storage: System Account (r:2 w:2) @@ -110,11 +112,12 @@ impl WeightInfo for HydraWeight { // Proof: MultiTransactionPayment AccountCurrencyMap (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) // Storage: Referrals Assets (r:0 w:1) // Proof: Referrals Assets (max_values: None, max_size: Some(20), added: 2495, mode: MaxEncodedLen) - fn convert() -> Weight { - // Minimum execution time: 249_840 nanoseconds. - Weight::from_ref_time(251_054_000 as u64) .saturating_add(T::DbWeight::get().reads(21 as u64)) - .saturating_add(T::DbWeight::get().writes(14 as u64)) - } + fn convert() -> Weight { + // Minimum execution time: 249_840 nanoseconds. + Weight::from_ref_time(251_054_000 as u64) + .saturating_add(T::DbWeight::get().reads(21 as u64)) + .saturating_add(T::DbWeight::get().writes(14 as u64)) + } // Storage: Referrals Assets (r:1 w:0) // Proof: Referrals Assets (max_values: None, max_size: Some(20), added: 2495, mode: MaxEncodedLen) // Storage: Referrals Shares (r:1 w:1) @@ -125,16 +128,18 @@ impl WeightInfo for HydraWeight { // Proof: Referrals TotalShares (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) // Storage: Referrals Referrer (r:1 w:1) // Proof: Referrals Referrer (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) - fn claim_rewards() -> Weight { - // Minimum execution time: 62_058 nanoseconds. - Weight::from_ref_time(62_527_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) - .saturating_add(T::DbWeight::get().writes(5 as u64)) - } + fn claim_rewards() -> Weight { + // Minimum execution time: 62_058 nanoseconds. + Weight::from_ref_time(62_527_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(5 as u64)) + } // Storage: Referrals AssetTier (r:1 w:1) // Proof: Referrals AssetTier (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - fn set_reward_percentage() -> Weight { - // Minimum execution time: 15_537 nanoseconds. - Weight::from_ref_time(16_059_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } + fn set_reward_percentage() -> Weight { + // Minimum execution time: 15_537 nanoseconds. + Weight::from_ref_time(16_059_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } } From 2f72623e06d4a706bb4cb9cd76804a49d7c297fa Mon Sep 17 00:00:00 2001 From: dmoka Date: Wed, 13 Dec 2023 15:17:59 +0100 Subject: [PATCH 65/90] fix test - make dxa fee check more permissive --- integration-tests/src/dca.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration-tests/src/dca.rs b/integration-tests/src/dca.rs index 4d2799d0d..6db21c20b 100644 --- a/integration-tests/src/dca.rs +++ b/integration-tests/src/dca.rs @@ -2987,8 +2987,8 @@ mod with_onchain_route { assert!(fee > 0, "The treasury did not receive the fee"); //The fee would be 5310255478763 in HDX, so it is less in DOT, which checks out - assert!(fee < 38 * UNITS / 10); - assert!(fee > 37 * UNITS / 10); + assert!(fee < 40 * UNITS / 10); + assert!(fee > 36 * UNITS / 10); assert_balance!(ALICE.into(), HDX, alice_init_hdx_balance + 278060378846663); assert_reserved_balance!(&ALICE.into(), DOT, dca_budget - amount_to_sell - fee); From 6d82112dd8c4ec7ba49f2474afa99e9ddff41db0 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Wed, 13 Dec 2023 15:57:23 +0100 Subject: [PATCH 66/90] change oracle period to 10mins --- runtime/hydradx/src/assets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 31cb5da7a..da17ed8c4 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -998,7 +998,7 @@ parameter_types! { pub const ReferralsPalletId: PalletId = PalletId(*b"referral"); pub RegistrationFee: (AssetId,Balance, AccountId)= (NativeAssetId::get(), 222_000_000_000_000, TreasuryAccount::get()); pub const MaxCodeLength: u32 = 7; - pub const ReferralsOraclePeriod: OraclePeriod = OraclePeriod::Short; + pub const ReferralsOraclePeriod: OraclePeriod = OraclePeriod::TenMinutes; pub const ReferralsSeedAmount: Balance = 100_000_000_000_000; } From c261548e4437f3893b6bb14360a5c33c6faf8ff8 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Wed, 13 Dec 2023 16:12:31 +0100 Subject: [PATCH 67/90] happy clippy happy life --- runtime/hydradx/src/benchmarking/omnipool.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/hydradx/src/benchmarking/omnipool.rs b/runtime/hydradx/src/benchmarking/omnipool.rs index d974e7680..8ceb29221 100644 --- a/runtime/hydradx/src/benchmarking/omnipool.rs +++ b/runtime/hydradx/src/benchmarking/omnipool.rs @@ -195,7 +195,7 @@ runtime_benchmarks! { let token_amount = 200_000_000_000_000_u128; update_balance(token_id, &acc, token_amount); - update_balance(0, &owner, 1000_000_000_000_000_u128); + update_balance(0, &owner, 1_000_000_000_000_000_u128); // Add the token to the pool Omnipool::add_token(RawOrigin::Root.into(), token_id, token_price, Permill::from_percent(100), owner.clone())?; @@ -244,7 +244,7 @@ runtime_benchmarks! { let token_amount = 200_000_000_000_000_u128; update_balance(token_id, &acc, token_amount); - update_balance(0, &owner, 1000_000_000_000_000_u128); + update_balance(0, &owner, 1_000_000_000_000_000_u128); // Add the token to the pool Omnipool::add_token(RawOrigin::Root.into(), token_id, token_price, Permill::from_percent(100), owner.clone())?; From dc5431d5c366080f624f161343c264a3290df079 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Thu, 14 Dec 2023 11:31:03 +0100 Subject: [PATCH 68/90] change code param type, add min buy amount --- integration-tests/src/referrals.rs | 22 +++-- pallets/referrals/src/benchmarking.rs | 8 +- pallets/referrals/src/lib.rs | 35 ++++---- pallets/referrals/src/tests/claim.rs | 32 +++---- pallets/referrals/src/tests/convert.rs | 6 +- pallets/referrals/src/tests/flow.rs | 13 +-- pallets/referrals/src/tests/link.rs | 60 +++++--------- pallets/referrals/src/tests/register.rs | 101 +++++++++-------------- pallets/referrals/src/tests/trade_fee.rs | 54 +++++------- runtime/hydradx/src/assets.rs | 25 ++++-- 10 files changed, 155 insertions(+), 201 deletions(-) diff --git a/integration-tests/src/referrals.rs b/integration-tests/src/referrals.rs index f08f2967c..4d172d505 100644 --- a/integration-tests/src/referrals.rs +++ b/integration-tests/src/referrals.rs @@ -4,6 +4,7 @@ use frame_support::assert_ok; use frame_system::RawOrigin; use hydradx_runtime::{Currencies, Omnipool, Referrals, Runtime, RuntimeOrigin, Tokens}; use orml_traits::MultiCurrency; +use pallet_referrals::ReferralCode; use primitives::AccountId; use sp_runtime::Permill; use xcm_emulator::TestExt; @@ -11,7 +12,8 @@ use xcm_emulator::TestExt; #[test] fn registering_a_code_should_charge_registration_fee() { Hydra::execute_with(|| { - let code = b"BALLS69".to_vec(); + let code = + ReferralCode::<::CodeLength>::truncate_from(b"BALLS69".to_vec()); let (reg_asset, reg_fee, reg_account) = ::RegistrationFee::get(); let balance = Currencies::free_balance(reg_asset, ®_account); assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE.into()), code)); @@ -26,7 +28,8 @@ fn trading_in_omnipool_should_transfer_portion_of_fee_to_reward_pot() { Hydra::execute_with(|| { init_omnipool_with_oracle_for_block_10(); init_referrals_program(); - let code = b"BALLS69".to_vec(); + let code = + ReferralCode::<::CodeLength>::truncate_from(b"BALLS69".to_vec()); assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE.into()), code.clone() @@ -49,7 +52,8 @@ fn trading_in_omnipool_should_increase_referrer_shares() { Hydra::execute_with(|| { init_omnipool_with_oracle_for_block_10(); init_referrals_program(); - let code = b"BALLS69".to_vec(); + let code = + ReferralCode::<::CodeLength>::truncate_from(b"BALLS69".to_vec()); assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE.into()), code.clone() @@ -71,7 +75,8 @@ fn trading_in_omnipool_should_increase_trader_shares() { Hydra::execute_with(|| { init_omnipool_with_oracle_for_block_10(); init_referrals_program(); - let code = b"BALLS69".to_vec(); + let code = + ReferralCode::<::CodeLength>::truncate_from(b"BALLS69".to_vec()); assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE.into()), code.clone() @@ -94,7 +99,8 @@ fn trading_in_omnipool_should_increase_total_shares_correctly() { Hydra::execute_with(|| { init_omnipool_with_oracle_for_block_10(); init_referrals_program(); - let code = b"BALLS69".to_vec(); + let code = + ReferralCode::<::CodeLength>::truncate_from(b"BALLS69".to_vec()); assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE.into()), code.clone() @@ -117,7 +123,8 @@ fn claiming_rewards_should_convert_all_assets_to_reward_asset() { Hydra::execute_with(|| { init_omnipool_with_oracle_for_block_10(); init_referrals_program(); - let code = b"BALLS69".to_vec(); + let code = + ReferralCode::<::CodeLength>::truncate_from(b"BALLS69".to_vec()); assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE.into()), code.clone() @@ -144,7 +151,8 @@ fn trading_hdx_in_omnipool_should_work_when_fee_is_below_existential_deposit() { Hydra::execute_with(|| { init_omnipool_with_oracle_for_block_10(); init_referrals_program(); - let code = b"BALLS69".to_vec(); + let code = + ReferralCode::<::CodeLength>::truncate_from(b"BALLS69".to_vec()); assert_ok!(Referrals::register_code( RuntimeOrigin::signed(ALICE.into()), code.clone() diff --git a/pallets/referrals/src/benchmarking.rs b/pallets/referrals/src/benchmarking.rs index da6ae5f1a..fa4cce2ba 100644 --- a/pallets/referrals/src/benchmarking.rs +++ b/pallets/referrals/src/benchmarking.rs @@ -31,7 +31,7 @@ benchmarks! { register_code{ let caller: T::AccountId = account("caller", 0, 1); - let code = vec![b'x'; T::CodeLength::get() as usize]; + let code: ReferralCode = vec![b'x'; T::CodeLength::get() as usize].try_into().unwrap(); let (asset, fee, _) = T::RegistrationFee::get(); T::Currency::mint_into(asset, &caller, 2 * fee)?; @@ -39,7 +39,7 @@ benchmarks! { verify { let entry = Pallet::::referrer_level(caller.clone()); assert_eq!(entry, Some((Level::Novice, 0))); - let c = Pallet::::normalize_code(ReferralCode::::truncate_from(code)); + let c = Pallet::::normalize_code(code); let entry = Pallet::::referral_account(c); assert_eq!(entry, Some(caller)); } @@ -47,7 +47,7 @@ benchmarks! { link_code{ let caller: T::AccountId = account("caller", 0, 1); let user: T::AccountId = account("user", 0, 1); - let code = vec![b'x'; T::CodeLength::get() as usize]; + let code: ReferralCode = vec![b'x'; T::CodeLength::get() as usize].try_into().unwrap(); let (asset, fee, _) = T::RegistrationFee::get(); T::Currency::mint_into(asset, &caller, 2 * fee)?; Pallet::::register_code(RawOrigin::Signed(caller.clone()).into(), code.clone())?; @@ -72,7 +72,7 @@ benchmarks! { claim_rewards{ let caller: T::AccountId = account("caller", 0, 1); - let code = vec![b'x'; T::CodeLength::get() as usize]; + let code: ReferralCode = vec![b'x'; T::CodeLength::get() as usize].try_into().unwrap(); let (asset, fee, _) = T::RegistrationFee::get(); T::Currency::mint_into(asset, &caller, 2 * fee)?; Pallet::::register_code(RawOrigin::Signed(caller.clone()).into(), code)?; diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index f35e04031..9ee2276d4 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -61,7 +61,6 @@ use sp_runtime::helpers_128bit::multiply_by_rational_with_rounding; use sp_runtime::traits::AccountIdConversion; use sp_runtime::Rounding; use sp_runtime::{traits::CheckedAdd, ArithmeticError, DispatchError, Permill}; -use sp_std::vec::Vec; #[cfg(feature = "runtime-benchmarks")] pub use crate::traits::BenchmarkHelper; @@ -103,6 +102,18 @@ pub struct Tier { trader: Permill, } +#[derive(Clone, Debug, PartialEq, Encode, Decode, TypeInfo)] +pub struct AssetAmount { + asset_id: AssetId, + amount: Balance, +} + +impl AssetAmount { + pub fn new(asset_id: AssetId, amount: Balance) -> Self { + Self { asset_id, amount } + } +} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -234,10 +245,8 @@ pub mod pallet { }, /// Asset has been converted to RewardAsset. Converted { - from: T::AssetId, - to: T::AssetId, - amount: Balance, - received: Balance, + from: AssetAmount, + to: AssetAmount, }, /// Rewards claimed. Claimed { who: T::AccountId, rewards: Balance }, @@ -275,6 +284,8 @@ pub mod pallet { IncorrectRewardPercentage, /// The account has already a code registered. AlreadyRegistered, + /// Price for given asset pair not found. + PriceNotFound, } #[pallet::call] @@ -294,16 +305,13 @@ pub mod pallet { /// Emits `CodeRegistered` event when successful. #[pallet::call_index(0)] #[pallet::weight(::WeightInfo::register_code())] - pub fn register_code(origin: OriginFor, code: Vec) -> DispatchResult { + pub fn register_code(origin: OriginFor, code: ReferralCode) -> DispatchResult { let who = ensure_signed(origin)?; - ensure!( ReferralAccounts::::get(&who).is_none(), Error::::AlreadyRegistered ); - let code: ReferralCode = code.try_into().map_err(|_| Error::::TooLong)?; - ensure!(code.len() >= MIN_CODE_LENGTH, Error::::TooShort); ensure!( @@ -342,9 +350,8 @@ pub mod pallet { /// Emits `CodeLinked` event when successful. #[pallet::call_index(1)] #[pallet::weight(::WeightInfo::link_code())] - pub fn link_code(origin: OriginFor, code: Vec) -> DispatchResult { + pub fn link_code(origin: OriginFor, code: ReferralCode) -> DispatchResult { let who = ensure_signed(origin)?; - let code: ReferralCode = code.try_into().map_err(|_| Error::::InvalidCode)?; let code = Self::normalize_code(code); let ref_account = Self::referral_account(&code).ok_or(Error::::InvalidCode)?; @@ -384,10 +391,8 @@ pub mod pallet { Assets::::remove(asset_id); Self::deposit_event(Event::Converted { - from: asset_id, - to: T::RewardAsset::get(), - amount: asset_balance, - received: total_reward_asset, + from: AssetAmount::new(asset_id, asset_balance), + to: AssetAmount::new(T::RewardAsset::get(), total_reward_asset), }); Ok(()) diff --git a/pallets/referrals/src/tests/claim.rs b/pallets/referrals/src/tests/claim.rs index e8bb8213f..07ac960c0 100644 --- a/pallets/referrals/src/tests/claim.rs +++ b/pallets/referrals/src/tests/claim.rs @@ -120,11 +120,9 @@ fn claim_rewards_update_total_accumulated_for_referrer_account() { .build() .execute_with(|| { // ARRANGE - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); - assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code)); // Act assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE))); // Assert @@ -142,11 +140,9 @@ fn claim_rewards_should_exclude_seed_amount() { .build() .execute_with(|| { // ARRANGE - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); - assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code)); // Act assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE))); // Assert @@ -169,11 +165,9 @@ fn claim_rewards_should_increase_referrer_level_when_limit_is_reached() { .build() .execute_with(|| { // ARRANGE - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); - assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code)); // Act assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE))); // Assert @@ -197,11 +191,9 @@ fn claim_rewards_should_increase_referrer_level_directly_to_top_tier_when_limit_ .build() .execute_with(|| { // ARRANGE - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); - assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code)); // Act assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE))); // Assert diff --git a/pallets/referrals/src/tests/convert.rs b/pallets/referrals/src/tests/convert.rs index 21d012954..ab3eccba6 100644 --- a/pallets/referrals/src/tests/convert.rs +++ b/pallets/referrals/src/tests/convert.rs @@ -58,10 +58,8 @@ fn convert_should_emit_event_when_successful() { assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); // Assert expect_events(vec![Event::Converted { - from: DAI, - to: RewardAsset::get(), - amount: 1_000_000_000_000_000_000, - received: 1_000_000_000_000, + from: AssetAmount::new(DAI, 1_000_000_000_000_000_000), + to: AssetAmount::new(RewardAsset::get(), 1_000_000_000_000), } .into()]); }); diff --git a/pallets/referrals/src/tests/flow.rs b/pallets/referrals/src/tests/flow.rs index b05c3713d..6e47471b3 100644 --- a/pallets/referrals/src/tests/flow.rs +++ b/pallets/referrals/src/tests/flow.rs @@ -72,15 +72,10 @@ fn complete_referral_flow_should_work_as_expected() { .build() .execute_with(|| { // ARRANGE - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); - assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); - assert_ok!(Referrals::link_code( - RuntimeOrigin::signed(CHARLIE), - b"BALLS69".to_vec() - )); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone())); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code.clone())); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(CHARLIE), code,)); // TRADES assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000)); assert_ok!(MockAmm::trade( diff --git a/pallets/referrals/src/tests/link.rs b/pallets/referrals/src/tests/link.rs index d0e514050..bb4183dd9 100644 --- a/pallets/referrals/src/tests/link.rs +++ b/pallets/referrals/src/tests/link.rs @@ -5,30 +5,19 @@ use pretty_assertions::assert_eq; fn link_code_should_work_when_code_is_valid() { ExtBuilder::default().build().execute_with(|| { // Arrange - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone())); // ACT - assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); - }); -} - -#[test] -fn link_code_should_fail_when_code_is_too_long() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - Referrals::link_code(RuntimeOrigin::signed(ALICE), b"TOOMANYBALLS69".to_vec(),), - Error::::InvalidCode - ); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code)); }); } #[test] fn link_code_should_fail_when_code_does_not_exist() { ExtBuilder::default().build().execute_with(|| { + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); assert_noop!( - Referrals::link_code(RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec(),), + Referrals::link_code(RuntimeOrigin::signed(ALICE), code), Error::::InvalidCode ); }); @@ -38,13 +27,11 @@ fn link_code_should_fail_when_code_does_not_exist() { fn link_code_should_link_correctly_when_code_is_valid() { ExtBuilder::default().build().execute_with(|| { // ARRANGE - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); // ACT - assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code)); // ASSERT let entry = Pallet::::linked_referral_account::(BOB); @@ -56,14 +43,12 @@ fn link_code_should_link_correctly_when_code_is_valid() { fn link_code_should_fail_when_linking_to_same_acccount() { ExtBuilder::default().build().execute_with(|| { // ARRANGE - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); // ACT assert_noop!( - Referrals::link_code(RuntimeOrigin::signed(ALICE), b"BALLS69".to_vec()), + Referrals::link_code(RuntimeOrigin::signed(ALICE), code), Error::::LinkNotAllowed ); }); @@ -73,13 +58,12 @@ fn link_code_should_fail_when_linking_to_same_acccount() { fn link_code_should_link_correctly_when_code_is_lowercase() { ExtBuilder::default().build().execute_with(|| { // ARRANGE - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code,)); // ACT - assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"balls69".to_vec())); + let code: ReferralCode<::CodeLength> = b"balls69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code)); // ASSERT let entry = Pallet::::linked_referral_account::(BOB); @@ -91,15 +75,13 @@ fn link_code_should_link_correctly_when_code_is_lowercase() { fn link_code_should_fail_when_account_is_already_linked() { ExtBuilder::default().build().execute_with(|| { // ARRANGE - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); - assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code.clone())); // ACT assert_noop!( - Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec()), + Referrals::link_code(RuntimeOrigin::signed(BOB), code), Error::::AlreadyLinked ); }); @@ -109,8 +91,8 @@ fn link_code_should_fail_when_account_is_already_linked() { fn link_code_should_emit_event_when_successful() { ExtBuilder::default().build().execute_with(|| { //ARRANGE - let code = b"BALLS69".to_vec(); - assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone())); // ACT assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code.clone())); // ASSERT diff --git a/pallets/referrals/src/tests/register.rs b/pallets/referrals/src/tests/register.rs index 15f021e75..9ee619ce4 100644 --- a/pallets/referrals/src/tests/register.rs +++ b/pallets/referrals/src/tests/register.rs @@ -5,56 +5,31 @@ use sp_runtime::traits::Zero; #[test] fn register_code_should_work_when_code_is_max_length() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); + let code: ReferralCode<::CodeLength> = vec![b'x'; ::CodeLength::get() as usize] + .try_into() + .unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code,)); }); } #[test] fn register_code_should_work_when_code_is_min_length() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"ABCDE".to_vec(), - )); - }); -} - -#[test] -fn register_code_should_fail_when_code_is_too_long() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"TOOMANYBALLS69".to_vec()), - Error::::TooLong - ); + let code: ReferralCode<::CodeLength> = vec![b'x'; crate::MIN_CODE_LENGTH].try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code,)); }); } #[test] fn register_code_should_fail_when_code_is_too_short() { ExtBuilder::default().build().execute_with(|| { - assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"".to_vec()), - Error::::TooShort - ); - assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"A".to_vec()), - Error::::TooShort - ); - assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"AB".to_vec()), - Error::::TooShort - ); - assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"ABC".to_vec()), - Error::::TooShort - ); - assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"ABCD".to_vec()), - Error::::TooShort - ); + for len in 0..MIN_CODE_LENGTH { + let code: ReferralCode<::CodeLength> = vec![b'x'; len].try_into().unwrap(); + assert_noop!( + Referrals::register_code(RuntimeOrigin::signed(ALICE), code), + Error::::TooShort + ); + } }); } @@ -62,13 +37,13 @@ fn register_code_should_fail_when_code_is_too_short() { fn register_code_should_fail_when_code_already_exists() { ExtBuilder::default().build().execute_with(|| { // Arrange - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); + let code: ReferralCode<::CodeLength> = vec![b'x'; ::CodeLength::get() as usize] + .try_into() + .unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); // Act assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec()), + Referrals::register_code(RuntimeOrigin::signed(BOB), code), Error::::AlreadyExists ); }); @@ -78,13 +53,12 @@ fn register_code_should_fail_when_code_already_exists() { fn register_code_should_fail_when_code_is_lowercase_and_already_exists() { ExtBuilder::default().build().execute_with(|| { // Arrange - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code,)); // Act + let code: ReferralCode<::CodeLength> = b"balls69".to_vec().try_into().unwrap(); assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(BOB), b"balls69".to_vec()), + Referrals::register_code(RuntimeOrigin::signed(BOB), code), Error::::AlreadyExists ); }); @@ -93,8 +67,9 @@ fn register_code_should_fail_when_code_is_lowercase_and_already_exists() { #[test] fn register_code_should_fail_when_code_contains_invalid_char() { ExtBuilder::default().build().execute_with(|| { + let code: ReferralCode<::CodeLength> = b"ABCD?".to_vec().try_into().unwrap(); assert_noop!( - Referrals::register_code(RuntimeOrigin::signed(ALICE), b"ABCD?".to_vec()), + Referrals::register_code(RuntimeOrigin::signed(ALICE), code), Error::::InvalidCharacter ); }); @@ -104,11 +79,11 @@ fn register_code_should_fail_when_code_contains_invalid_char() { fn register_code_should_store_account_mapping_to_code_correctly() { ExtBuilder::default().build().execute_with(|| { // Arrange - let code = b"BALLS69".to_vec(); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); // Act - assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone())); // Assert - let entry = Pallet::::referral_account::>(code.try_into().unwrap()); + let entry = Pallet::::referral_account::>(code); assert_eq!(entry, Some(ALICE)); }); } @@ -117,13 +92,13 @@ fn register_code_should_store_account_mapping_to_code_correctly() { fn register_code_should_convert_to_upper_case_when_code_is_lower_case() { ExtBuilder::default().build().execute_with(|| { // Arrange - let code = b"balls69".to_vec(); + let code: ReferralCode<::CodeLength> = b"balls69".to_vec().try_into().unwrap(); // Act - assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone())); // Assert - let entry = Pallet::::referral_account::>(code.clone().try_into().unwrap()); + let entry = Pallet::::referral_account::>(code.clone()); assert_eq!(entry, None); - let normalized = Pallet::::normalize_code(code.try_into().unwrap()); + let normalized = Pallet::::normalize_code(code); let entry = Pallet::::referral_account::>(normalized); assert_eq!(entry, Some(ALICE)); }); @@ -133,12 +108,12 @@ fn register_code_should_convert_to_upper_case_when_code_is_lower_case() { fn register_code_should_emit_event_when_successful() { ExtBuilder::default().build().execute_with(|| { // Arrange - let code = b"BALLS69".to_vec(); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); // Act - assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone())); // Assert expect_events(vec![Event::CodeRegistered { - code: code.try_into().unwrap(), + code: code, account: ALICE, } .into()]); @@ -149,7 +124,7 @@ fn register_code_should_emit_event_when_successful() { fn signer_should_pay_the_registration_fee() { ExtBuilder::default().build().execute_with(|| { // Arrange - let code = b"BALLS69".to_vec(); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); // Act assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code)); // Assert @@ -163,7 +138,7 @@ fn signer_should_pay_the_registration_fee() { fn singer_should_set_default_level_for_referrer() { ExtBuilder::default().build().execute_with(|| { // Arrange - let code = b"BALLS69".to_vec(); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); // Act assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code)); // Assert @@ -176,9 +151,9 @@ fn singer_should_set_default_level_for_referrer() { fn register_code_should_fail_when_account_has_already_code_registered() { ExtBuilder::default().build().execute_with(|| { // Arrange - let code = b"BALLS69".to_vec(); + let code: ReferralCode<::CodeLength> = b"FIRST".to_vec().try_into().unwrap(); assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code)); - let code = b"SECOND".to_vec(); + let code: ReferralCode<::CodeLength> = b"SECOND".to_vec().try_into().unwrap(); assert_noop!( Referrals::register_code(RuntimeOrigin::signed(ALICE), code), Error::::AlreadyRegistered diff --git a/pallets/referrals/src/tests/trade_fee.rs b/pallets/referrals/src/tests/trade_fee.rs index 8201c965e..6f469e425 100644 --- a/pallets/referrals/src/tests/trade_fee.rs +++ b/pallets/referrals/src/tests/trade_fee.rs @@ -17,11 +17,9 @@ fn process_trade_fee_should_increased_referrer_shares() { .build() .execute_with(|| { // ARRANGE - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); - assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code)); // Act assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000,)); // Assert @@ -46,11 +44,9 @@ fn process_trade_fee_should_increased_trader_shares() { .build() .execute_with(|| { // ARRANGE - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); - assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code)); // Act assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000,)); // Assert @@ -75,11 +71,9 @@ fn process_trade_fee_should_increased_total_share_issuance() { .build() .execute_with(|| { // ARRANGE - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); - assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code)); // Act assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000,)); // Assert @@ -104,11 +98,9 @@ fn process_trade_fee_should_fail_when_taken_amount_is_greated_than_fee_amount() .build() .execute_with(|| { // ARRANGE - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); - assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code)); // Act assert_noop!( MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000,), @@ -125,10 +117,8 @@ fn process_trade_should_not_increase_shares_when_trader_does_not_have_linked_acc .build() .execute_with(|| { // ARRANGE - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code,)); // Assert assert_ok!(MockAmm::trade( RuntimeOrigin::signed(ALICE), @@ -157,11 +147,9 @@ fn process_trade_fee_should_add_asset_to_asset_list() { .build() .execute_with(|| { // ARRANGE - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); - assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code)); // Act assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000,)); // Assert @@ -186,11 +174,9 @@ fn process_trade_fee_should_not_add_reward_asset_to_asset_list() { .build() .execute_with(|| { // ARRANGE - assert_ok!(Referrals::register_code( - RuntimeOrigin::signed(ALICE), - b"BALLS69".to_vec(), - )); - assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), b"BALLS69".to_vec())); + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code)); // Act assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), DAI, HDX, 1_000_000_000_000,)); // Assert diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index da17ed8c4..d0c27b0a5 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -61,7 +61,7 @@ use pallet_route_executor::{weights::WeightInfo as RouterWeights, AmmTradeWeight use pallet_staking::types::Action; use pallet_staking::SigmoidPercentage; use pallet_xyk::weights::WeightInfo as XykWeights; -use sp_runtime::DispatchError; +use sp_runtime::{DispatchError, FixedPointNumber}; use sp_std::num::NonZeroU16; parameter_types! { @@ -415,8 +415,9 @@ where } } +use hydradx_traits::pools::SpotPriceProvider; #[cfg(feature = "runtime-benchmarks")] -use hydradx_traits::{pools::SpotPriceProvider, PriceOracle}; +use hydradx_traits::PriceOracle; #[cfg(feature = "runtime-benchmarks")] use hydra_dx_math::ema::EmaPrice; @@ -1007,7 +1008,7 @@ impl pallet_referrals::Config for Runtime { type AuthorityOrigin = EnsureRoot; type AssetId = AssetId; type Currency = FungibleCurrencies; - type Convert = ConvertViaOmnipool; + type Convert = ConvertViaOmnipool; #[cfg(not(feature = "runtime-benchmarks"))] type PriceProvider = OraclePriceProviderUsingRoute, ReferralsOraclePeriod>; @@ -1024,8 +1025,11 @@ impl pallet_referrals::Config for Runtime { type BenchmarkHelper = ReferralsBenchmarkHelper; } -pub struct ConvertViaOmnipool; -impl Convert for ConvertViaOmnipool { +pub struct ConvertViaOmnipool(PhantomData); +impl Convert for ConvertViaOmnipool +where + SP: SpotPriceProvider, +{ type Error = DispatchError; fn convert( @@ -1034,8 +1038,17 @@ impl Convert for ConvertViaOmnipool { asset_to: AssetId, amount: Balance, ) -> Result { + let price = SP::spot_price(asset_to, asset_from).ok_or(pallet_referrals::Error::::PriceNotFound)?; + let amount_to_receive = price.saturating_mul_int(amount); + let min_expected = amount_to_receive.saturating_sub(Permill::from_percent(1).mul_floor(amount_to_receive)); let balance = Currencies::free_balance(asset_to, &who); - Omnipool::sell(RuntimeOrigin::signed(who.clone()), asset_from, asset_to, amount, 0)?; + Omnipool::sell( + RuntimeOrigin::signed(who.clone()), + asset_from, + asset_to, + amount, + min_expected, + )?; let balance_after = Currencies::free_balance(asset_to, &who); let received = balance_after.saturating_sub(balance); Ok(received) From 6eee1a925980b91d6dd817337dd746b662212d26 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Thu, 14 Dec 2023 11:44:38 +0100 Subject: [PATCH 69/90] fix omnipool benchmakrs --- runtime/hydradx/src/benchmarking/omnipool.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/runtime/hydradx/src/benchmarking/omnipool.rs b/runtime/hydradx/src/benchmarking/omnipool.rs index 8ceb29221..fbd0f0c93 100644 --- a/runtime/hydradx/src/benchmarking/omnipool.rs +++ b/runtime/hydradx/src/benchmarking/omnipool.rs @@ -22,6 +22,7 @@ use hydradx_traits::{ use orml_benchmarking::runtime_benchmarks; use orml_traits::{MultiCurrency, MultiCurrencyExtended}; use pallet_omnipool::types::Tradability; +use pallet_referrals::ReferralCode; pub fn update_balance(currency_id: AssetId, who: &AccountId, balance: Balance) { assert_ok!( @@ -222,7 +223,7 @@ runtime_benchmarks! { let buy_min_amount = 10_000_000_000_u128; // Register and link referral code to account for the weight too - let code = b"MYCODE".to_vec(); + let code = ReferralCode::<::CodeLength>::truncate_from(b"MYCODE".to_vec()); Referrals::register_code(RawOrigin::Signed(owner).into(), code.clone())?; Referrals::link_code(RawOrigin::Signed(seller.clone()).into(), code)?; Referrals::set_reward_percentage(RawOrigin::Root.into(), DAI, pallet_referrals::Level::Novice, Permill::from_percent(1), Permill::from_percent(1))?; @@ -270,7 +271,7 @@ runtime_benchmarks! { let amount_buy = 1_000_000_000_000_u128; let sell_max_limit = 2_000_000_000_000_u128; // Register and link referral code to account for the weight too - let code = b"MYCODE".to_vec(); + let code = ReferralCode::<::CodeLength>::truncate_from(b"MYCODE".to_vec()); Referrals::register_code(RawOrigin::Signed(owner).into(), code.clone())?; Referrals::link_code(RawOrigin::Signed(seller.clone()).into(), code)?; Referrals::set_reward_percentage(RawOrigin::Root.into(), token_id, pallet_referrals::Level::Novice, Permill::from_percent(1), Permill::from_percent(1))?; From 09889e34d27afdc007385d4f5c438986d06127f0 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Thu, 14 Dec 2023 11:59:23 +0100 Subject: [PATCH 70/90] happy clippy happy life --- pallets/referrals/src/tests/link.rs | 2 +- pallets/referrals/src/tests/register.rs | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/pallets/referrals/src/tests/link.rs b/pallets/referrals/src/tests/link.rs index bb4183dd9..4e6195821 100644 --- a/pallets/referrals/src/tests/link.rs +++ b/pallets/referrals/src/tests/link.rs @@ -98,7 +98,7 @@ fn link_code_should_emit_event_when_successful() { // ASSERT expect_events(vec![Event::CodeLinked { account: BOB, - code: code.try_into().unwrap(), + code, referral_account: ALICE, } .into()]); diff --git a/pallets/referrals/src/tests/register.rs b/pallets/referrals/src/tests/register.rs index 9ee619ce4..02864f294 100644 --- a/pallets/referrals/src/tests/register.rs +++ b/pallets/referrals/src/tests/register.rs @@ -112,11 +112,7 @@ fn register_code_should_emit_event_when_successful() { // Act assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone())); // Assert - expect_events(vec![Event::CodeRegistered { - code: code, - account: ALICE, - } - .into()]); + expect_events(vec![Event::CodeRegistered { code, account: ALICE }.into()]); }); } From fee2f332497a48fb74c8dee1bede3c5d683e9742 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Thu, 14 Dec 2023 18:02:42 +0100 Subject: [PATCH 71/90] on_idle convert and permissive conversions in claim --- pallets/omnipool/src/lib.rs | 7 ++++++ pallets/referrals/src/lib.rs | 40 ++++++++++++++++++++++++++++++++++- runtime/hydradx/src/assets.rs | 20 +++++++++++++++--- 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index 7951e078a..c5766e439 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -404,6 +404,8 @@ pub mod pallet { AssetNotFrozen, /// Configured stable asset cannot be removed from Omnipool. StableAssetCannotBeRemoved, + /// Calculated amount out from sell trade is zero. + ZeroAmountOut, } #[pallet::call] @@ -983,6 +985,11 @@ pub mod pallet { ) .ok_or(ArithmeticError::Overflow)?; + ensure!( + *state_changes.asset_out.delta_reserve > Balance::zero(), + Error::::ZeroAmountOut + ); + ensure!( *state_changes.asset_out.delta_reserve >= min_buy_amount, Error::::BuyLimitNotReached diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 9ee2276d4..b7a04994c 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -286,6 +286,10 @@ pub mod pallet { AlreadyRegistered, /// Price for given asset pair not found. PriceNotFound, + /// Minimum trading amount for conversion has not been reached. + ConversionMinTradingAmountNotReached, + /// Zero amount received from conversion. + ConversionZeroAmountReceived, } #[pallet::call] @@ -419,7 +423,20 @@ pub mod pallet { let who = ensure_signed(origin)?; for (asset_id, _) in Assets::::drain() { let asset_balance = T::Currency::balance(asset_id, &Self::pot_account_id()); - T::Convert::convert(Self::pot_account_id(), asset_id, T::RewardAsset::get(), asset_balance)?; + let r = T::Convert::convert(Self::pot_account_id(), asset_id, T::RewardAsset::get(), asset_balance); + match r { + Err(error) => { + if error == Error::::ConversionMinTradingAmountNotReached.into() + || error == Error::::ConversionZeroAmountReceived.into() + { + // We allow these errors to continue claiming as the current amount of asset that needed to be converted + // has very low impact on the rewards. + } else { + return Err(error); + } + } + Ok(_) => {} + } } let shares = Shares::::take(&who); if shares == Balance::zero() { @@ -518,6 +535,27 @@ pub mod pallet { Ok(()) } } + + #[pallet::hooks] + impl Hooks for Pallet { + fn on_idle(_n: T::BlockNumber, remaining_weight: Weight) -> Weight { + let convert_weight = T::WeightInfo::convert(); + if convert_weight.is_zero() { + return Weight::zero(); + } + let one_read = T::DbWeight::get().reads(1u64); + let max_converts = remaining_weight.saturating_sub(one_read).ref_time() / convert_weight.ref_time(); + + for asset_id in Assets::::iter_keys().take(max_converts as usize) { + let asset_balance = T::Currency::balance(asset_id, &Self::pot_account_id()); + let r = T::Convert::convert(Self::pot_account_id(), asset_id, T::RewardAsset::get(), asset_balance); + if r.is_ok() { + Assets::::remove(asset_id); + } + } + convert_weight.saturating_mul(max_converts).saturating_add(one_read) + } + } } impl Pallet { diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index d0c27b0a5..a373aa3d8 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -1038,17 +1038,31 @@ where asset_to: AssetId, amount: Balance, ) -> Result { + if amount < ::MinimumTradingLimit::get() { + return Err(pallet_referrals::Error::::ConversionMinTradingAmountNotReached.into()); + } let price = SP::spot_price(asset_to, asset_from).ok_or(pallet_referrals::Error::::PriceNotFound)?; let amount_to_receive = price.saturating_mul_int(amount); - let min_expected = amount_to_receive.saturating_sub(Permill::from_percent(1).mul_floor(amount_to_receive)); + let min_expected = amount_to_receive + .saturating_sub(Permill::from_percent(1).mul_floor(amount_to_receive)) + .max(1); let balance = Currencies::free_balance(asset_to, &who); - Omnipool::sell( + let r = Omnipool::sell( RuntimeOrigin::signed(who.clone()), asset_from, asset_to, amount, min_expected, - )?; + ); + match r { + Err(error) => { + if error == pallet_omnipool::Error::::ZeroAmountOut.into() { + return Err(pallet_referrals::Error::::ConversionZeroAmountReceived.into()); + } + return Err(error); + } + Ok(()) => {} + } let balance_after = Currencies::free_balance(asset_to, &who); let received = balance_after.saturating_sub(balance); Ok(received) From 55e0a43032812d41a7f890fa8b0cfcbdc5eb6d79 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Fri, 15 Dec 2023 14:06:13 +0100 Subject: [PATCH 72/90] rework levels --- integration-tests/src/referrals.rs | 12 ++--- pallets/referrals/src/benchmarking.rs | 10 ++-- pallets/referrals/src/lib.rs | 52 +++++++++++--------- pallets/referrals/src/tests.rs | 12 +++-- pallets/referrals/src/tests/claim.rs | 18 ++++--- pallets/referrals/src/tests/flow.rs | 22 +++++---- pallets/referrals/src/tests/tiers.rs | 12 ++--- pallets/referrals/src/tests/trade_fee.rs | 12 ++--- runtime/hydradx/src/assets.rs | 8 +-- runtime/hydradx/src/benchmarking/omnipool.rs | 4 +- 10 files changed, 91 insertions(+), 71 deletions(-) diff --git a/integration-tests/src/referrals.rs b/integration-tests/src/referrals.rs index 4d172d505..d07a81bd3 100644 --- a/integration-tests/src/referrals.rs +++ b/integration-tests/src/referrals.rs @@ -216,42 +216,42 @@ fn init_referrals_program() { assert_ok!(Referrals::set_reward_percentage( RuntimeOrigin::root(), HDX, - pallet_referrals::Level::Novice, + pallet_referrals::Level::Tier0, Permill::from_percent(2), Permill::from_percent(1), )); assert_ok!(Referrals::set_reward_percentage( RuntimeOrigin::root(), HDX, - pallet_referrals::Level::Advanced, + pallet_referrals::Level::Tier1, Permill::from_percent(5), Permill::from_percent(2), )); assert_ok!(Referrals::set_reward_percentage( RuntimeOrigin::root(), HDX, - pallet_referrals::Level::Expert, + pallet_referrals::Level::Tier2, Permill::from_percent(10), Permill::from_percent(5), )); assert_ok!(Referrals::set_reward_percentage( RuntimeOrigin::root(), DAI, - pallet_referrals::Level::Novice, + pallet_referrals::Level::Tier0, Permill::from_percent(2), Permill::from_percent(1), )); assert_ok!(Referrals::set_reward_percentage( RuntimeOrigin::root(), DAI, - pallet_referrals::Level::Advanced, + pallet_referrals::Level::Tier1, Permill::from_percent(5), Permill::from_percent(2), )); assert_ok!(Referrals::set_reward_percentage( RuntimeOrigin::root(), DAI, - pallet_referrals::Level::Expert, + pallet_referrals::Level::Tier2, Permill::from_percent(10), Permill::from_percent(5), )); diff --git a/pallets/referrals/src/benchmarking.rs b/pallets/referrals/src/benchmarking.rs index fa4cce2ba..26d7557cf 100644 --- a/pallets/referrals/src/benchmarking.rs +++ b/pallets/referrals/src/benchmarking.rs @@ -38,7 +38,7 @@ benchmarks! { }: _(RawOrigin::Signed(caller.clone()), code.clone()) verify { let entry = Pallet::::referrer_level(caller.clone()); - assert_eq!(entry, Some((Level::Novice, 0))); + assert_eq!(entry, Some((Level::Tier0, 0))); let c = Pallet::::normalize_code(code); let entry = Pallet::::referral_account(c); assert_eq!(entry, Some(caller)); @@ -80,7 +80,7 @@ benchmarks! { // The worst case is when referrer account is updated to the top tier in one call // So we need to have enough RewardAsset in the pot. And give all the shares to the caller. - let top_tier_volume = T::TierVolume::get(&Level::Advanced).expect("to have all level configured"); + let top_tier_volume = T::TierVolume::get(&Level::Tier3).expect("to have all level configured"); T::Currency::mint_into(T::RewardAsset::get(), &Pallet::::pot_account_id(), top_tier_volume + T::SeedNativeAmount::get())?; Shares::::insert(caller.clone(), 1_000_000_000_000); TotalShares::::put(1_000_000_000_000); @@ -91,16 +91,16 @@ benchmarks! { let balance = T::Currency::balance(T::RewardAsset::get(), &caller); assert!(balance > caller_balance); let (level, total) = Referrer::::get(&caller).expect("correct entry"); - assert_eq!(level, Level::Expert); + assert_eq!(level, Level::Tier2); assert_eq!(total, top_tier_volume); } set_reward_percentage{ let referrer_percentage = Permill::from_percent(70); let trader_percentage = Permill::from_percent(30); - }: _(RawOrigin::Root, T::RewardAsset::get(), Level::Expert, referrer_percentage, trader_percentage) + }: _(RawOrigin::Root, T::RewardAsset::get(), Level::Tier2, referrer_percentage, trader_percentage) verify { - let entry = Pallet::::asset_tier(T::RewardAsset::get(), Level::Expert); + let entry = Pallet::::asset_tier(T::RewardAsset::get(), Level::Tier2); assert_eq!(entry, Some(Tier{ referrer: referrer_percentage, trader: trader_percentage, diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index b7a04994c..9add52039 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -79,17 +79,40 @@ const MIN_CODE_LENGTH: usize = 5; #[derive(Hash, Clone, Copy, Default, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] pub enum Level { #[default] - Novice, - Advanced, - Expert, + Tier0 = 0, + Tier1, + Tier2, + Tier3, + Tier4, } impl Level { pub fn next_level(&self) -> Self { match self { - Self::Novice => Self::Advanced, - Self::Advanced => Self::Expert, - Self::Expert => Self::Expert, + Self::Tier0 => Self::Tier1, + Self::Tier1 => Self::Tier2, + Self::Tier2 => Self::Tier3, + Self::Tier3 => Self::Tier4, + Self::Tier4 => Self::Tier4, + } + } + + pub fn is_max_level(&self) -> bool { + *self == Self::Tier4 + } + + pub fn increase(self, amount: Balance) -> Self { + if self.is_max_level() { + self + } else { + let next_level = self.next_level(); + let maybe_required = T::TierVolume::get(&next_level); + if let Some(required) = maybe_required { + if amount >= required { + return next_level.increase::(amount); + } + } + self } } } @@ -474,22 +497,7 @@ pub mod pallet { Referrer::::mutate(who.clone(), |v| { if let Some((level, total)) = v { *total = total.saturating_add(rewards); - - if *level != Level::Expert { - let next_tier = T::TierVolume::get(level); - if let Some(amount_needed) = next_tier { - if *total >= amount_needed { - *level = level.next_level(); - // let's check if we can skip two levels - let next_tier = T::TierVolume::get(level); - if let Some(amount_needed) = next_tier { - if *total >= amount_needed { - *level = level.next_level(); - } - } - } - } - } + *level = level.increase::(*total); } }); diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 51b94af08..3263bb64c 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -107,11 +107,17 @@ impl GetByKey> for Volume { if let Some(l) = c { l } else { + None + /* match level { - Level::Novice => Some(1), - Level::Advanced => Some(1_000_000_000), - Level::Expert => None, + Level::Tier0 => Some(1), + Level::Tier1 => Some(1_000), + Level::Tier2 => Some(1_000_000), + Level::Tier3 => Some(1_000_000_000), + Level::Tier4 => None, } + + */ } } } diff --git a/pallets/referrals/src/tests/claim.rs b/pallets/referrals/src/tests/claim.rs index 07ac960c0..ffea7bae8 100644 --- a/pallets/referrals/src/tests/claim.rs +++ b/pallets/referrals/src/tests/claim.rs @@ -154,9 +154,9 @@ fn claim_rewards_should_exclude_seed_amount() { #[test] fn claim_rewards_should_increase_referrer_level_when_limit_is_reached() { let mut volumes = HashMap::new(); - volumes.insert(Level::Novice, Some(10_000_000_000_000)); - volumes.insert(Level::Advanced, Some(20_000_000_000_000)); - volumes.insert(Level::Expert, None); + volumes.insert(Level::Tier0, Some(0)); + volumes.insert(Level::Tier1, Some(10_000_000_000_000)); + volumes.insert(Level::Tier2, Some(20_000_000_000_000)); ExtBuilder::default() .with_endowed_accounts(vec![(Pallet::::pot_account_id(), HDX, 20_000_000_000_000)]) @@ -172,7 +172,7 @@ fn claim_rewards_should_increase_referrer_level_when_limit_is_reached() { assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE))); // Assert let (level, total) = Referrer::::get(ALICE).unwrap(); - assert_eq!(level, Level::Advanced); + assert_eq!(level, Level::Tier1); assert_eq!(total, 15_000_000_000_000); }); } @@ -180,9 +180,11 @@ fn claim_rewards_should_increase_referrer_level_when_limit_is_reached() { #[test] fn claim_rewards_should_increase_referrer_level_directly_to_top_tier_when_limit_is_reached() { let mut volumes = HashMap::new(); - volumes.insert(Level::Novice, Some(10_000_000_000_000)); - volumes.insert(Level::Advanced, Some(13_000_000_000_000)); - volumes.insert(Level::Expert, None); + volumes.insert(Level::Tier0, Some(0)); + volumes.insert(Level::Tier1, Some(10_000_000_000_000)); + volumes.insert(Level::Tier2, Some(11_000_000_000_000)); + volumes.insert(Level::Tier3, Some(12_000_000_000_000)); + volumes.insert(Level::Tier4, Some(13_000_000_000_000)); ExtBuilder::default() .with_endowed_accounts(vec![(Pallet::::pot_account_id(), HDX, 20_000_000_000_000)]) @@ -198,7 +200,7 @@ fn claim_rewards_should_increase_referrer_level_directly_to_top_tier_when_limit_ assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(ALICE))); // Assert let (level, total) = Referrer::::get(ALICE).unwrap(); - assert_eq!(level, Level::Expert); + assert_eq!(level, Level::Tier4); assert_eq!(total, 15_000_000_000_000); }); } diff --git a/pallets/referrals/src/tests/flow.rs b/pallets/referrals/src/tests/flow.rs index 6e47471b3..1f121a48d 100644 --- a/pallets/referrals/src/tests/flow.rs +++ b/pallets/referrals/src/tests/flow.rs @@ -4,9 +4,11 @@ use pretty_assertions::assert_eq; #[test] fn complete_referral_flow_should_work_as_expected() { let mut volumes = HashMap::new(); - volumes.insert(Level::Novice, Some(100_000_000)); - volumes.insert(Level::Advanced, Some(200_000_000)); - volumes.insert(Level::Expert, None); + volumes.insert(Level::Tier0, Some(0)); + volumes.insert(Level::Tier1, Some(100_000_000)); + volumes.insert(Level::Tier2, Some(200_000_000)); + volumes.insert(Level::Tier3, Some(300_000_000)); + volumes.insert(Level::Tier4, Some(400_000_000)); let bob_initial_hdx = 10_000_000_000_000; @@ -21,7 +23,7 @@ fn complete_referral_flow_should_work_as_expected() { .with_tiers(vec![ ( DAI, - Level::Novice, + Level::Tier0, Tier { referrer: Permill::from_float(0.005), trader: Permill::from_float(0.002), @@ -29,7 +31,7 @@ fn complete_referral_flow_should_work_as_expected() { ), ( DOT, - Level::Novice, + Level::Tier0, Tier { referrer: Permill::from_float(0.005), trader: Permill::from_float(0.002), @@ -37,7 +39,7 @@ fn complete_referral_flow_should_work_as_expected() { ), ( DAI, - Level::Advanced, + Level::Tier1, Tier { referrer: Permill::from_float(0.03), trader: Permill::from_float(0.01), @@ -45,7 +47,7 @@ fn complete_referral_flow_should_work_as_expected() { ), ( DOT, - Level::Advanced, + Level::Tier1, Tier { referrer: Permill::from_float(0.03), trader: Permill::from_float(0.01), @@ -53,7 +55,7 @@ fn complete_referral_flow_should_work_as_expected() { ), ( HDX, - Level::Novice, + Level::Tier0, Tier { referrer: Permill::from_float(0.002), trader: Permill::from_float(0.001), @@ -61,7 +63,7 @@ fn complete_referral_flow_should_work_as_expected() { ), ( HDX, - Level::Advanced, + Level::Tier1, Tier { referrer: Permill::from_float(0.03), trader: Permill::from_float(0.01), @@ -129,7 +131,7 @@ fn complete_referral_flow_should_work_as_expected() { let alice_balance = Tokens::free_balance(HDX, &ALICE); assert_eq!(alice_balance, 778_000_120_000_000); let (level, total) = Referrer::::get(ALICE).unwrap(); - assert_eq!(level, Level::Advanced); + assert_eq!(level, Level::Tier1); assert_eq!(total, 120_000_000); }); } diff --git a/pallets/referrals/src/tests/tiers.rs b/pallets/referrals/src/tests/tiers.rs index dc186561c..c48007bee 100644 --- a/pallets/referrals/src/tests/tiers.rs +++ b/pallets/referrals/src/tests/tiers.rs @@ -9,7 +9,7 @@ fn setting_asset_tier_should_fail_when_not_correct_origin() { Referrals::set_reward_percentage( RuntimeOrigin::signed(BOB), DAI, - Level::Novice, + Level::Tier0, Permill::from_percent(1), Permill::from_percent(2), ), @@ -24,11 +24,11 @@ fn setting_asset_tier_should_correctly_update_storage() { assert_ok!(Referrals::set_reward_percentage( RuntimeOrigin::root(), DAI, - Level::Novice, + Level::Tier0, Permill::from_percent(1), Permill::from_percent(2), )); - let d = AssetTier::::get(DAI, Level::Novice); + let d = AssetTier::::get(DAI, Level::Tier0); assert_eq!( d, Some(Tier { @@ -46,7 +46,7 @@ fn setting_asset_tier_should_fail_when_total_percentage_exceeds_hundred_percent( Referrals::set_reward_percentage( RuntimeOrigin::root(), DAI, - Level::Novice, + Level::Tier0, Permill::from_percent(70), Permill::from_percent(40), ), @@ -61,13 +61,13 @@ fn setting_asset_tier_should_emit_event() { assert_ok!(Referrals::set_reward_percentage( RuntimeOrigin::root(), DAI, - Level::Novice, + Level::Tier0, Permill::from_percent(1), Permill::from_percent(2), )); expect_events(vec![Event::TierRewardSet { asset_id: DAI, - level: Level::Novice, + level: Level::Tier0, referrer: Permill::from_percent(1), trader: Permill::from_percent(2), } diff --git a/pallets/referrals/src/tests/trade_fee.rs b/pallets/referrals/src/tests/trade_fee.rs index 6f469e425..3f0445204 100644 --- a/pallets/referrals/src/tests/trade_fee.rs +++ b/pallets/referrals/src/tests/trade_fee.rs @@ -8,7 +8,7 @@ fn process_trade_fee_should_increased_referrer_shares() { .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_tiers(vec![( DAI, - Level::Novice, + Level::Tier0, Tier { referrer: Permill::from_percent(50), trader: Permill::zero(), @@ -35,7 +35,7 @@ fn process_trade_fee_should_increased_trader_shares() { .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_tiers(vec![( DAI, - Level::Novice, + Level::Tier0, Tier { referrer: Permill::from_percent(50), trader: Permill::from_percent(20), @@ -62,7 +62,7 @@ fn process_trade_fee_should_increased_total_share_issuance() { .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_tiers(vec![( DAI, - Level::Novice, + Level::Tier0, Tier { referrer: Permill::from_percent(50), trader: Permill::from_percent(20), @@ -89,7 +89,7 @@ fn process_trade_fee_should_fail_when_taken_amount_is_greated_than_fee_amount() .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_tiers(vec![( DAI, - Level::Novice, + Level::Tier0, Tier { referrer: Permill::from_percent(50), trader: Permill::from_percent(70), @@ -138,7 +138,7 @@ fn process_trade_fee_should_add_asset_to_asset_list() { .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_tiers(vec![( DAI, - Level::Novice, + Level::Tier0, Tier { referrer: Permill::from_percent(50), trader: Permill::from_percent(20), @@ -165,7 +165,7 @@ fn process_trade_fee_should_not_add_reward_asset_to_asset_list() { .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_tiers(vec![( DAI, - Level::Novice, + Level::Tier0, Tier { referrer: Permill::from_percent(50), trader: Permill::from_percent(20), diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index a373aa3d8..dd686c48d 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -1074,9 +1074,11 @@ pub struct ReferralsLevelTiers; impl GetByKey> for ReferralsLevelTiers { fn get(k: &Level) -> Option { match k { - Level::Novice => Some(10_000_000_000_000), - Level::Advanced => Some(100_000_000_000_000), - Level::Expert => None, + Level::Tier0 => Some(2_000_000_000_000_000), + Level::Tier1 => Some(22_000_000_000_000_000), + Level::Tier2 => Some(222_000_000_000_000_000), + Level::Tier3 => Some(2_222_000_000_000_000_000), + Level::Tier4 => None, } } } diff --git a/runtime/hydradx/src/benchmarking/omnipool.rs b/runtime/hydradx/src/benchmarking/omnipool.rs index fbd0f0c93..0b421d528 100644 --- a/runtime/hydradx/src/benchmarking/omnipool.rs +++ b/runtime/hydradx/src/benchmarking/omnipool.rs @@ -226,7 +226,7 @@ runtime_benchmarks! { let code = ReferralCode::<::CodeLength>::truncate_from(b"MYCODE".to_vec()); Referrals::register_code(RawOrigin::Signed(owner).into(), code.clone())?; Referrals::link_code(RawOrigin::Signed(seller.clone()).into(), code)?; - Referrals::set_reward_percentage(RawOrigin::Root.into(), DAI, pallet_referrals::Level::Novice, Permill::from_percent(1), Permill::from_percent(1))?; + Referrals::set_reward_percentage(RawOrigin::Root.into(), DAI, pallet_referrals::Level::Tier0, Permill::from_percent(1), Permill::from_percent(1))?; }: { Omnipool::sell(RawOrigin::Signed(seller.clone()).into(), token_id, DAI, amount_sell, buy_min_amount)? } verify { assert!(::Currency::free_balance(DAI, &seller) >= buy_min_amount); @@ -274,7 +274,7 @@ runtime_benchmarks! { let code = ReferralCode::<::CodeLength>::truncate_from(b"MYCODE".to_vec()); Referrals::register_code(RawOrigin::Signed(owner).into(), code.clone())?; Referrals::link_code(RawOrigin::Signed(seller.clone()).into(), code)?; - Referrals::set_reward_percentage(RawOrigin::Root.into(), token_id, pallet_referrals::Level::Novice, Permill::from_percent(1), Permill::from_percent(1))?; + Referrals::set_reward_percentage(RawOrigin::Root.into(), token_id, pallet_referrals::Level::Tier0, Permill::from_percent(1), Permill::from_percent(1))?; }: { Omnipool::buy(RawOrigin::Signed(seller.clone()).into(), DAI, token_id, amount_buy, sell_max_limit)? } verify { assert!(::Currency::free_balance(DAI, &seller) >= Balance::zero()); From cd0362e4bb0f588f27c5527bdc6b72496dd30fef Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Fri, 15 Dec 2023 14:24:24 +0100 Subject: [PATCH 73/90] rework tier volume in config --- pallets/referrals/src/benchmarking.rs | 11 +++++---- pallets/referrals/src/lib.rs | 12 +++++----- pallets/referrals/src/tests.rs | 32 +++++++++++++++------------ runtime/hydradx/src/assets.rs | 14 ++++++------ 4 files changed, 37 insertions(+), 32 deletions(-) diff --git a/pallets/referrals/src/benchmarking.rs b/pallets/referrals/src/benchmarking.rs index 26d7557cf..4d2b2f8d0 100644 --- a/pallets/referrals/src/benchmarking.rs +++ b/pallets/referrals/src/benchmarking.rs @@ -80,7 +80,7 @@ benchmarks! { // The worst case is when referrer account is updated to the top tier in one call // So we need to have enough RewardAsset in the pot. And give all the shares to the caller. - let top_tier_volume = T::TierVolume::get(&Level::Tier3).expect("to have all level configured"); + let top_tier_volume = T::TierVolume::get(&Level::Tier4); T::Currency::mint_into(T::RewardAsset::get(), &Pallet::::pot_account_id(), top_tier_volume + T::SeedNativeAmount::get())?; Shares::::insert(caller.clone(), 1_000_000_000_000); TotalShares::::put(1_000_000_000_000); @@ -91,7 +91,7 @@ benchmarks! { let balance = T::Currency::balance(T::RewardAsset::get(), &caller); assert!(balance > caller_balance); let (level, total) = Referrer::::get(&caller).expect("correct entry"); - assert_eq!(level, Level::Tier2); + assert_eq!(level, Level::Tier4); assert_eq!(total, top_tier_volume); } @@ -113,6 +113,9 @@ mod tests { use super::Pallet; use crate::tests::*; use frame_benchmarking::impl_benchmark_test_suite; - - impl_benchmark_test_suite!(Pallet, super::ExtBuilder::default().build(), super::Test); + impl_benchmark_test_suite!( + Pallet, + super::ExtBuilder::default().with_default_volumes().build(), + super::Test + ); } diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 9add52039..7c08aa3d5 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -106,11 +106,9 @@ impl Level { self } else { let next_level = self.next_level(); - let maybe_required = T::TierVolume::get(&next_level); - if let Some(required) = maybe_required { - if amount >= required { - return next_level.increase::(amount); - } + let required = T::TierVolume::get(&next_level); + if amount >= required { + return next_level.increase::(amount); } self } @@ -189,8 +187,8 @@ pub mod pallet { #[pallet::constant] type CodeLength: Get; - /// Volume needed to next tier. If None returned, it is the last tier. - type TierVolume: GetByKey>; + /// Volume needed to reach given level. + type TierVolume: GetByKey; /// Seed amount that was sent to the reward pot. #[pallet::constant] diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 3263bb64c..c68708496 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -100,24 +100,15 @@ parameter_types! { pub struct Volume; -impl GetByKey> for Volume { - fn get(level: &Level) -> Option { +impl GetByKey for Volume { + fn get(level: &Level) -> Balance { let c = TIER_VOLUME.with(|v| v.borrow().get(level).copied()); if let Some(l) = c { - l + l.unwrap() } else { - None - /* - match level { - Level::Tier0 => Some(1), - Level::Tier1 => Some(1_000), - Level::Tier2 => Some(1_000_000), - Level::Tier3 => Some(1_000_000_000), - Level::Tier4 => None, - } - - */ + // if not explicitly set, we dont care about this in the test + 0 } } } @@ -270,6 +261,19 @@ impl ExtBuilder { self } + pub fn with_default_volumes(self) -> Self { + let mut volumes = HashMap::new(); + volumes.insert(Level::Tier0, Some(0)); + volumes.insert(Level::Tier1, Some(10_000_000_000_000)); + volumes.insert(Level::Tier2, Some(11_000_000_000_000)); + volumes.insert(Level::Tier3, Some(12_000_000_000_000)); + volumes.insert(Level::Tier4, Some(13_000_000_000_000)); + TIER_VOLUME.with(|v| { + v.swap(&RefCell::new(volumes)); + }); + self + } + pub fn build(self) -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index dd686c48d..b9dc0d4bb 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -1071,14 +1071,14 @@ where pub struct ReferralsLevelTiers; -impl GetByKey> for ReferralsLevelTiers { - fn get(k: &Level) -> Option { +impl GetByKey for ReferralsLevelTiers { + fn get(k: &Level) -> Balance { match k { - Level::Tier0 => Some(2_000_000_000_000_000), - Level::Tier1 => Some(22_000_000_000_000_000), - Level::Tier2 => Some(222_000_000_000_000_000), - Level::Tier3 => Some(2_222_000_000_000_000_000), - Level::Tier4 => None, + Level::Tier0 => 0, + Level::Tier1 => 2_000_000_000_000_000, + Level::Tier2 => 22_000_000_000_000_000, + Level::Tier3 => 222_000_000_000_000_000, + Level::Tier4 => 2_222_000_000_000_000_000, } } } From 0ae4a92559897224d0b7074d9255b45f5a4a3b76 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Fri, 15 Dec 2023 14:49:40 +0100 Subject: [PATCH 74/90] omnipool fee amount check --- Cargo.lock | 2 +- pallets/omnipool/Cargo.toml | 2 +- pallets/omnipool/src/lib.rs | 11 ++++------- pallets/referrals/src/lib.rs | 19 ++++++++----------- 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 69839ca0e..94d8869ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7643,7 +7643,7 @@ dependencies = [ [[package]] name = "pallet-omnipool" -version = "4.0.2" +version = "4.0.3" dependencies = [ "bitflags", "frame-benchmarking", diff --git a/pallets/omnipool/Cargo.toml b/pallets/omnipool/Cargo.toml index 838a8663e..1771f3e56 100644 --- a/pallets/omnipool/Cargo.toml +++ b/pallets/omnipool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-omnipool" -version = "4.0.2" +version = "4.0.3" authors = ['GalacticCouncil'] edition = "2021" license = "Apache-2.0" diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index c5766e439..6eda0d3cc 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -988,7 +988,7 @@ pub mod pallet { ensure!( *state_changes.asset_out.delta_reserve > Balance::zero(), Error::::ZeroAmountOut - ); + ); ensure!( *state_changes.asset_out.delta_reserve >= min_buy_amount, @@ -1955,13 +1955,10 @@ impl Pallet { fn process_trade_fee(trader: &T::AccountId, asset: T::AssetId, amount: Balance) -> DispatchResult { let account = Self::protocol_account(); let original_asset_reserve = T::Currency::free_balance(asset, &account); - let unused = T::OmnipoolHooks::on_trade_fee(account.clone(), trader.clone(), asset, amount)?; + let _ = T::OmnipoolHooks::on_trade_fee(account.clone(), trader.clone(), asset, amount)?; let asset_reserve = T::Currency::free_balance(asset, &account); - let updated_asset_reserve = asset_reserve.saturating_add(amount.saturating_sub(unused)); - ensure!( - updated_asset_reserve == original_asset_reserve, - Error::::FeeOverdraft - ); + let diff = original_asset_reserve.saturating_sub(asset_reserve); + ensure!(diff <= amount, Error::::FeeOverdraft); Ok(()) } diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 7c08aa3d5..b63c8c7c6 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -445,18 +445,15 @@ pub mod pallet { for (asset_id, _) in Assets::::drain() { let asset_balance = T::Currency::balance(asset_id, &Self::pot_account_id()); let r = T::Convert::convert(Self::pot_account_id(), asset_id, T::RewardAsset::get(), asset_balance); - match r { - Err(error) => { - if error == Error::::ConversionMinTradingAmountNotReached.into() - || error == Error::::ConversionZeroAmountReceived.into() - { - // We allow these errors to continue claiming as the current amount of asset that needed to be converted - // has very low impact on the rewards. - } else { - return Err(error); - } + if let Err(error) = r { + if error == Error::::ConversionMinTradingAmountNotReached.into() + || error == Error::::ConversionZeroAmountReceived.into() + { + // We allow these errors to continue claiming as the current amount of asset that needed to be converted + // has very low impact on the rewards. + } else { + return Err(error); } - Ok(_) => {} } } let shares = Shares::::take(&who); From e1f3b747941945b976343a2315f1e98aa74d1abe Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Fri, 15 Dec 2023 15:13:00 +0100 Subject: [PATCH 75/90] happy clippy happy life --- runtime/hydradx/src/assets.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index b9dc0d4bb..4beb8d4c9 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -1054,14 +1054,11 @@ where amount, min_expected, ); - match r { - Err(error) => { - if error == pallet_omnipool::Error::::ZeroAmountOut.into() { - return Err(pallet_referrals::Error::::ConversionZeroAmountReceived.into()); - } - return Err(error); + if let Err(error) = r { + if error == pallet_omnipool::Error::::ZeroAmountOut.into() { + return Err(pallet_referrals::Error::::ConversionZeroAmountReceived.into()); } - Ok(()) => {} + return Err(error); } let balance_after = Currencies::free_balance(asset_to, &who); let received = balance_after.saturating_sub(balance); From 39cbdb025ee3c423d316bc179b3696e554b60d19 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 18 Dec 2023 12:30:14 +0100 Subject: [PATCH 76/90] integration tests --- integration-tests/src/referrals.rs | 157 +++++++++++++++++++--- pallets/omnipool/src/lib.rs | 3 +- pallets/omnipool/src/traits.rs | 2 +- pallets/referrals/src/benchmarking.rs | 6 +- pallets/referrals/src/lib.rs | 124 +++++++++++------ pallets/referrals/src/tests.rs | 49 ++++++- pallets/referrals/src/tests/flow.rs | 6 + pallets/referrals/src/tests/tiers.rs | 38 ++++-- pallets/referrals/src/tests/trade_fee.rs | 161 ++++++++++++++++++++++- pallets/staking/src/lib.rs | 4 +- runtime/adapters/src/lib.rs | 5 +- runtime/hydradx/src/assets.rs | 47 ++++++- 12 files changed, 520 insertions(+), 82 deletions(-) diff --git a/integration-tests/src/referrals.rs b/integration-tests/src/referrals.rs index d07a81bd3..07341c959 100644 --- a/integration-tests/src/referrals.rs +++ b/integration-tests/src/referrals.rs @@ -2,9 +2,9 @@ use crate::polkadot_test_net::*; use frame_support::assert_ok; use frame_system::RawOrigin; -use hydradx_runtime::{Currencies, Omnipool, Referrals, Runtime, RuntimeOrigin, Tokens}; +use hydradx_runtime::{Currencies, Omnipool, Referrals, Runtime, RuntimeOrigin, Staking, Tokens}; use orml_traits::MultiCurrency; -use pallet_referrals::ReferralCode; +use pallet_referrals::{ReferralCode, Tier}; use primitives::AccountId; use sp_runtime::Permill; use xcm_emulator::TestExt; @@ -27,7 +27,6 @@ fn registering_a_code_should_charge_registration_fee() { fn trading_in_omnipool_should_transfer_portion_of_fee_to_reward_pot() { Hydra::execute_with(|| { init_omnipool_with_oracle_for_block_10(); - init_referrals_program(); let code = ReferralCode::<::CodeLength>::truncate_from(b"BALLS69".to_vec()); assert_ok!(Referrals::register_code( @@ -43,7 +42,7 @@ fn trading_in_omnipool_should_transfer_portion_of_fee_to_reward_pot() { 0 )); let pot_balance = Currencies::free_balance(DAI, &Referrals::pot_account_id()); - assert_eq!(pot_balance, 2_060_386_836_081); + assert_eq!(pot_balance, 28_540_796_091_592_978); }); } @@ -51,7 +50,6 @@ fn trading_in_omnipool_should_transfer_portion_of_fee_to_reward_pot() { fn trading_in_omnipool_should_increase_referrer_shares() { Hydra::execute_with(|| { init_omnipool_with_oracle_for_block_10(); - init_referrals_program(); let code = ReferralCode::<::CodeLength>::truncate_from(b"BALLS69".to_vec()); assert_ok!(Referrals::register_code( @@ -67,14 +65,13 @@ fn trading_in_omnipool_should_increase_referrer_shares() { 0 )); let referrer_shares = Referrals::account_shares::(ALICE.into()); - assert_eq!(referrer_shares, 51_399_742); + assert_eq!(referrer_shares, 128_499_434); }); } #[test] fn trading_in_omnipool_should_increase_trader_shares() { Hydra::execute_with(|| { init_omnipool_with_oracle_for_block_10(); - init_referrals_program(); let code = ReferralCode::<::CodeLength>::truncate_from(b"BALLS69".to_vec()); assert_ok!(Referrals::register_code( @@ -89,8 +86,30 @@ fn trading_in_omnipool_should_increase_trader_shares() { 1_000_000_000_000, 0 )); - let referrer_shares = Referrals::account_shares::(BOB.into()); - assert_eq!(referrer_shares, 25_699_871); + let trader_shares = Referrals::account_shares::(BOB.into()); + assert_eq!(trader_shares, 256_998_869); + }); +} +#[test] +fn trading_in_omnipool_should_increase_external_shares() { + Hydra::execute_with(|| { + init_omnipool_with_oracle_for_block_10(); + let code = + ReferralCode::<::CodeLength>::truncate_from(b"BALLS69".to_vec()); + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE.into()), + code.clone() + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB.into()), code)); + assert_ok!(Omnipool::sell( + RuntimeOrigin::signed(BOB.into()), + HDX, + DAI, + 1_000_000_000_000, + 0 + )); + let external_shares = Referrals::account_shares::(Staking::pot_account_id().into()); + assert_eq!(external_shares, 2_164_560_909_660); }); } @@ -98,7 +117,6 @@ fn trading_in_omnipool_should_increase_trader_shares() { fn trading_in_omnipool_should_increase_total_shares_correctly() { Hydra::execute_with(|| { init_omnipool_with_oracle_for_block_10(); - init_referrals_program(); let code = ReferralCode::<::CodeLength>::truncate_from(b"BALLS69".to_vec()); assert_ok!(Referrals::register_code( @@ -113,8 +131,8 @@ fn trading_in_omnipool_should_increase_total_shares_correctly() { 1_000_000_000_000, 0 )); - let referrer_shares = Referrals::total_shares(); - assert_eq!(referrer_shares, 25_699_871 + 51_399_742); + let total_shares = Referrals::total_shares(); + assert_eq!(total_shares, 256_998_869 + 128_499_434 + 2_164_560_909_660); }); } @@ -122,7 +140,6 @@ fn trading_in_omnipool_should_increase_total_shares_correctly() { fn claiming_rewards_should_convert_all_assets_to_reward_asset() { Hydra::execute_with(|| { init_omnipool_with_oracle_for_block_10(); - init_referrals_program(); let code = ReferralCode::<::CodeLength>::truncate_from(b"BALLS69".to_vec()); assert_ok!(Referrals::register_code( @@ -150,7 +167,6 @@ fn claiming_rewards_should_convert_all_assets_to_reward_asset() { fn trading_hdx_in_omnipool_should_work_when_fee_is_below_existential_deposit() { Hydra::execute_with(|| { init_omnipool_with_oracle_for_block_10(); - init_referrals_program(); let code = ReferralCode::<::CodeLength>::truncate_from(b"BALLS69".to_vec()); assert_ok!(Referrals::register_code( @@ -166,7 +182,113 @@ fn trading_hdx_in_omnipool_should_work_when_fee_is_below_existential_deposit() { 0 )); let referrer_shares = Referrals::account_shares::(BOB.into()); - assert_eq!(referrer_shares, 9_870_477_594); + assert_eq!(referrer_shares, 98_704_716_390); + }); +} + +#[test] +fn trading_in_omnipool_should_transfer_some_portion_of_fee_when_no_code_linked() { + Hydra::execute_with(|| { + init_omnipool_with_oracle_for_block_10(); + assert_ok!(Omnipool::sell( + RuntimeOrigin::signed(BOB.into()), + HDX, + DAI, + 1_000_000_000_000, + 0 + )); + let pot_balance = Currencies::free_balance(DAI, &Referrals::pot_account_id()); + assert_eq!(pot_balance, 28_540_796_091_592_980); + let external_shares = Referrals::account_shares::(Staking::pot_account_id()); + let total_shares = Referrals::total_shares(); + assert_eq!(total_shares, external_shares); + }); +} + +#[test] +fn trading_in_omnipool_should_use_global_rewards_when_not_set() { + Hydra::execute_with(|| { + init_omnipool_with_oracle_for_block_10(); + let code = + ReferralCode::<::CodeLength>::truncate_from(b"BALLS69".to_vec()); + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE.into()), + code.clone() + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB.into()), code)); + assert_ok!(Omnipool::sell( + RuntimeOrigin::signed(BOB.into()), + HDX, + DAI, + 1_000_000_000_000, + 0 + )); + let referrer_shares = Referrals::account_shares::(ALICE.into()); + assert_eq!(referrer_shares, 128_499_434); + let trader_shares = Referrals::account_shares::(BOB.into()); + assert_eq!(trader_shares, 256_998_869); + let external_shares = Referrals::account_shares::(Staking::pot_account_id()); + assert_eq!(external_shares, 2_164_560_909_660); + let total_shares = Referrals::total_shares(); + assert_eq!(total_shares, referrer_shares + trader_shares + external_shares); + }); +} + +#[test] +fn trading_in_omnipool_should_use_asset_rewards_when_set() { + Hydra::execute_with(|| { + init_omnipool_with_oracle_for_block_10(); + assert_ok!(Referrals::set_reward_percentage( + RuntimeOrigin::root(), + DAI, + pallet_referrals::Level::Tier0, + Tier { + referrer: Permill::from_percent(2), + trader: Permill::from_percent(1), + external: Permill::from_percent(10), + } + )); + let code = + ReferralCode::<::CodeLength>::truncate_from(b"BALLS69".to_vec()); + assert_ok!(Referrals::register_code( + RuntimeOrigin::signed(ALICE.into()), + code.clone() + )); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB.into()), code)); + assert_ok!(Omnipool::sell( + RuntimeOrigin::signed(BOB.into()), + HDX, + DAI, + 1_000_000_000_000, + 0 + )); + let referrer_shares = Referrals::account_shares::(ALICE.into()); + assert_eq!(referrer_shares, 51_399_773); + let trader_shares = Referrals::account_shares::(BOB.into()); + assert_eq!(trader_shares, 25_699_886); + let external_shares = Referrals::account_shares::(Staking::pot_account_id()); + assert_eq!(external_shares, 2_163_918_412_488); + let total_shares = Referrals::total_shares(); + assert_eq!(total_shares, referrer_shares + trader_shares + external_shares); + }); +} + +#[test] +fn trading_in_omnipool_should_increase_staking_shares_when_no_code_linked() { + Hydra::execute_with(|| { + init_omnipool_with_oracle_for_block_10(); + assert_ok!(Omnipool::sell( + RuntimeOrigin::signed(BOB.into()), + HDX, + DAI, + 1_000_000_000_000, + 0 + )); + let staking_acc = Staking::pot_account_id(); + let staking_shares = Referrals::account_shares::(staking_acc.into()); + assert_eq!(staking_shares, 2_164_946_407_964); + let total_shares = Referrals::total_shares(); + assert_eq!(total_shares, staking_shares); }); } @@ -201,6 +323,7 @@ fn do_trade_to_populate_oracle(asset_1: AssetId, asset_2: AssetId, amount: Balan amount, Balance::MIN )); + seed_pot_account(); } fn seed_pot_account() { @@ -208,10 +331,11 @@ fn seed_pot_account() { RawOrigin::Root.into(), Referrals::pot_account_id(), HDX, - (100 * UNITS) as i128, + (10 * UNITS) as i128, )); } +/* fn init_referrals_program() { assert_ok!(Referrals::set_reward_percentage( RuntimeOrigin::root(), @@ -258,3 +382,4 @@ fn init_referrals_program() { seed_pot_account(); } + */ diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index 6eda0d3cc..dba5693d7 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -1955,10 +1955,11 @@ impl Pallet { fn process_trade_fee(trader: &T::AccountId, asset: T::AssetId, amount: Balance) -> DispatchResult { let account = Self::protocol_account(); let original_asset_reserve = T::Currency::free_balance(asset, &account); - let _ = T::OmnipoolHooks::on_trade_fee(account.clone(), trader.clone(), asset, amount)?; + let used = T::OmnipoolHooks::on_trade_fee(account.clone(), trader.clone(), asset, amount)?; let asset_reserve = T::Currency::free_balance(asset, &account); let diff = original_asset_reserve.saturating_sub(asset_reserve); ensure!(diff <= amount, Error::::FeeOverdraft); + ensure!(diff == used , Error::::FeeOverdraft); Ok(()) } diff --git a/pallets/omnipool/src/traits.rs b/pallets/omnipool/src/traits.rs index fa484f50e..729c1a222 100644 --- a/pallets/omnipool/src/traits.rs +++ b/pallets/omnipool/src/traits.rs @@ -57,7 +57,7 @@ where fn on_liquidity_changed_weight() -> Weight; fn on_trade_weight() -> Weight; - /// Returns unused amount + /// Returns used amount fn on_trade_fee( fee_account: AccountId, trader: AccountId, diff --git a/pallets/referrals/src/benchmarking.rs b/pallets/referrals/src/benchmarking.rs index 4d2b2f8d0..5c61dc3a8 100644 --- a/pallets/referrals/src/benchmarking.rs +++ b/pallets/referrals/src/benchmarking.rs @@ -96,14 +96,16 @@ benchmarks! { } set_reward_percentage{ - let referrer_percentage = Permill::from_percent(70); + let referrer_percentage = Permill::from_percent(40); let trader_percentage = Permill::from_percent(30); - }: _(RawOrigin::Root, T::RewardAsset::get(), Level::Tier2, referrer_percentage, trader_percentage) + let external_percentage = Permill::from_percent(30); + }: _(RawOrigin::Root, T::RewardAsset::get(), Level::Tier2, Tier{referrer: referrer_percentage, trader: trader_percentage, external: external_percentage}) verify { let entry = Pallet::::asset_tier(T::RewardAsset::get(), Level::Tier2); assert_eq!(entry, Some(Tier{ referrer: referrer_percentage, trader: trader_percentage, + external: external_percentage, })); } } diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index b63c8c7c6..f7f9716e9 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -60,7 +60,7 @@ use sp_core::U256; use sp_runtime::helpers_128bit::multiply_by_rational_with_rounding; use sp_runtime::traits::AccountIdConversion; use sp_runtime::Rounding; -use sp_runtime::{traits::CheckedAdd, ArithmeticError, DispatchError, Permill}; +use sp_runtime::{traits::{CheckedAdd,Zero}, ArithmeticError, DispatchError, Permill}; #[cfg(feature = "runtime-benchmarks")] pub use crate::traits::BenchmarkHelper; @@ -78,8 +78,9 @@ const MIN_CODE_LENGTH: usize = 5; /// Indicates current level of the referrer to determine which reward percentages are used. #[derive(Hash, Clone, Copy, Default, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] pub enum Level { + None, #[default] - Tier0 = 0, + Tier0, Tier1, Tier2, Tier3, @@ -94,6 +95,7 @@ impl Level { Self::Tier2 => Self::Tier3, Self::Tier3 => Self::Tier4, Self::Tier4 => Self::Tier4, + Self::None => Self::None, } } @@ -115,12 +117,14 @@ impl Level { } } -#[derive(Clone, Default, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +#[derive(Clone, Copy, Default, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] pub struct Tier { /// Percentage of the fee that goes to the referrer. - referrer: Permill, + pub referrer: Permill, /// Percentage of the fee that goes back to the trader. - trader: Permill, + pub trader: Permill, + /// Percentage of the fee that goes to specific account given by `ExternalAccount` config parameter as reward.r + pub external: Permill, } #[derive(Clone, Debug, PartialEq, Encode, Decode, TypeInfo)] @@ -190,6 +194,12 @@ pub mod pallet { /// Volume needed to reach given level. type TierVolume: GetByKey; + /// Global reward percentages for all assets if not specified explicitly for the asset. + type TierRewardPercentages: GetByKey; + + /// External account that receives some percentage of the fee. Usually something like staking. + type ExternalAccount: Get>; + /// Seed amount that was sent to the reward pot. #[pallet::constant] type SeedNativeAmount: Get; @@ -275,8 +285,7 @@ pub mod pallet { TierRewardSet { asset_id: T::AssetId, level: Level, - referrer: Permill, - trader: Permill, + tier: Tier, }, } @@ -515,26 +524,24 @@ pub mod pallet { origin: OriginFor, asset_id: T::AssetId, level: Level, - referrer: Permill, - trader: Permill, + tier: Tier, ) -> DispatchResult { T::AuthorityOrigin::ensure_origin(origin)?; //ensure that total percentage does not exceed 100% ensure!( - referrer.checked_add(&trader).is_some(), + tier.referrer + .checked_add(&tier.trader) + .ok_or(Error::::IncorrectRewardPercentage)? + .checked_add(&tier.external) + .is_some(), Error::::IncorrectRewardPercentage ); AssetTier::::mutate(asset_id, level, |v| { - *v = Some(Tier { referrer, trader }); - }); - Self::deposit_event(Event::TierRewardSet { - asset_id, - level, - referrer, - trader, + *v = Some(tier); }); + Self::deposit_event(Event::TierRewardSet { asset_id, level, tier }); Ok(()) } } @@ -583,50 +590,83 @@ impl Pallet { asset_id: T::AssetId, amount: Balance, ) -> Result { - // Does trader have a linked referral account ? - let Some(ref_account) = Self::linked_referral_account(&trader) else { - return Ok(amount); - }; - // What is the referer level? - let Some((level,_)) = Self::referrer_level(&ref_account) else { - // Should not really happen, the ref entry should be always there. - defensive!("Referrer details not found"); - return Ok(amount); + let Some(price) = T::PriceProvider::get_price(T::RewardAsset::get(), asset_id) else { + // no price, no fun. + return Ok(Balance::zero()); }; - // What is asset fee for this level? if any. - let Some(tier) = Self::asset_tier(asset_id, level) else { - return Ok(amount); + let (level,ref_account) = if let Some(acc) = Self::linked_referral_account(&trader) { + if let Some((level,_)) = Self::referrer_level(&acc) { + // Should not really happen, the ref entry should be always there. + (level, Some(acc)) + }else{ + defensive!("Referrer details not found"); + return Ok(Balance::zero()); + } + }else{ + (Level::None, None) }; - let Some(price) = T::PriceProvider::get_price(T::RewardAsset::get(), asset_id) else { - // no price, no fun. - return Ok(amount); - }; + // What is asset fee for this level? if not explicitly set, use global parameter. + let tier = Self::asset_tier(asset_id, level).unwrap_or(T::TierRewardPercentages::get(&level)); - let referrer_reward = tier.referrer.mul_floor(amount); + // Rewards + let external_account = T::ExternalAccount::get(); + let referrer_reward = if ref_account.is_some() { + tier.referrer.mul_floor(amount) + }else{ + 0 + }; let trader_reward = tier.trader.mul_floor(amount); - let total_taken = referrer_reward.saturating_add(trader_reward); + let external_reward = if external_account.is_some() { + tier.external.mul_floor(amount) + } else { + 0 + }; + let total_taken = referrer_reward + .saturating_add(trader_reward) + .saturating_add(external_reward); ensure!(total_taken <= amount, Error::::IncorrectRewardCalculation); T::Currency::transfer(asset_id, &source, &Self::pot_account_id(), total_taken, true)?; - let referrer_shares = multiply_by_rational_with_rounding(referrer_reward, price.n, price.d, Rounding::Down) - .ok_or(ArithmeticError::Overflow)?; + let referrer_shares = if ref_account.is_some() { + multiply_by_rational_with_rounding(referrer_reward, price.n, price.d, Rounding::Down) + .ok_or(ArithmeticError::Overflow)? + }else{ + 0 + }; let trader_shares = multiply_by_rational_with_rounding(trader_reward, price.n, price.d, Rounding::Down) .ok_or(ArithmeticError::Overflow)?; + let external_shares = if external_account.is_some() { + multiply_by_rational_with_rounding(external_reward, price.n, price.d, Rounding::Down) + .ok_or(ArithmeticError::Overflow)? + } else { + 0 + }; TotalShares::::mutate(|v| { - *v = v.saturating_add(referrer_shares.saturating_add(trader_shares)); - }); - Shares::::mutate(ref_account, |v| { - *v = v.saturating_add(referrer_shares); + *v = v.saturating_add( + referrer_shares + .saturating_add(trader_shares) + .saturating_add(external_shares), + ); }); + if let Some(acc) = ref_account { + Shares::::mutate(acc, |v| { + *v = v.saturating_add(referrer_shares); + }); + } Shares::::mutate(trader, |v| { *v = v.saturating_add(trader_shares); }); + if let Some(acc) = external_account { + Shares::::mutate(acc, |v| { + *v = v.saturating_add(external_shares); + }); + } if asset_id != T::RewardAsset::get() { Assets::::insert(asset_id, ()); } - Ok(amount.saturating_sub(total_taken)) + Ok(total_taken) } } diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index c68708496..31049ac27 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -75,7 +75,9 @@ pub(crate) const INITIAL_ALICE_BALANCE: Balance = 1_000 * ONE; thread_local! { pub static CONVERSION_RATE: RefCell> = RefCell::new(HashMap::default()); pub static TIER_VOLUME: RefCell>> = RefCell::new(HashMap::default()); + pub static TIER_REWARDS: RefCell> = RefCell::new(HashMap::default()); pub static SEED_AMOUNT: RefCell = RefCell::new(Balance::zero()); + pub static EXTERNAL_ACCOUNT: RefCell> = RefCell::new(None); } construct_runtime!( @@ -113,6 +115,16 @@ impl GetByKey for Volume { } } +pub struct TierRewards; + +impl GetByKey for TierRewards { + fn get(level: &Level) -> Tier { + TIER_REWARDS + .with(|v| v.borrow().get(level).copied()) + .unwrap_or_default() + } +} + pub struct SeedAmount; impl Get for SeedAmount { @@ -121,6 +133,14 @@ impl Get for SeedAmount { } } +pub struct ExtAccount; + +impl Get> for ExtAccount { + fn get() -> Option { + EXTERNAL_ACCOUNT.with(|v| *v.borrow()) + } +} + impl Config for Test { type RuntimeEvent = RuntimeEvent; type AuthorityOrigin = EnsureRoot; @@ -133,6 +153,8 @@ impl Config for Test { type RegistrationFee = RegistrationFee; type CodeLength = CodeLength; type TierVolume = Volume; + type TierRewardPercentages = TierRewards; + type ExternalAccount = ExtAccount; type SeedNativeAmount = SeedAmount; type WeightInfo = (); @@ -205,11 +227,21 @@ impl Default for ExtBuilder { CONVERSION_RATE.with(|v| { v.borrow_mut().clear(); }); - SEED_AMOUNT.with(|v| { let mut c = v.borrow_mut(); *c = 0u128; }); + TIER_VOLUME.with(|v| { + v.borrow_mut().clear(); + }); + TIER_REWARDS.with(|v| { + v.borrow_mut().clear(); + }); + EXTERNAL_ACCOUNT.with(|v| { + let mut c = v.borrow_mut(); + *c = None; + }); + Self { endowed_accounts: vec![(ALICE, HDX, INITIAL_ALICE_BALANCE)], shares: vec![], @@ -261,6 +293,21 @@ impl ExtBuilder { self } + pub fn with_global_tier_rewards(self, rewards: HashMap) -> Self { + TIER_REWARDS.with(|v| { + v.swap(&RefCell::new(rewards)); + }); + self + } + + pub fn with_external_account(self, acc: AccountId) -> Self { + EXTERNAL_ACCOUNT.with(|v| { + let mut m = v.borrow_mut(); + *m = Some(acc); + }); + self + } + pub fn with_default_volumes(self) -> Self { let mut volumes = HashMap::new(); volumes.insert(Level::Tier0, Some(0)); diff --git a/pallets/referrals/src/tests/flow.rs b/pallets/referrals/src/tests/flow.rs index 1f121a48d..4d6a1fc9a 100644 --- a/pallets/referrals/src/tests/flow.rs +++ b/pallets/referrals/src/tests/flow.rs @@ -27,6 +27,7 @@ fn complete_referral_flow_should_work_as_expected() { Tier { referrer: Permill::from_float(0.005), trader: Permill::from_float(0.002), + external: Permill::from_float(0.002), }, ), ( @@ -35,6 +36,7 @@ fn complete_referral_flow_should_work_as_expected() { Tier { referrer: Permill::from_float(0.005), trader: Permill::from_float(0.002), + external: Permill::from_float(0.002), }, ), ( @@ -43,6 +45,7 @@ fn complete_referral_flow_should_work_as_expected() { Tier { referrer: Permill::from_float(0.03), trader: Permill::from_float(0.01), + external: Permill::from_float(0.002), }, ), ( @@ -51,6 +54,7 @@ fn complete_referral_flow_should_work_as_expected() { Tier { referrer: Permill::from_float(0.03), trader: Permill::from_float(0.01), + external: Permill::from_float(0.002), }, ), ( @@ -59,6 +63,7 @@ fn complete_referral_flow_should_work_as_expected() { Tier { referrer: Permill::from_float(0.002), trader: Permill::from_float(0.001), + external: Permill::from_float(0.002), }, ), ( @@ -67,6 +72,7 @@ fn complete_referral_flow_should_work_as_expected() { Tier { referrer: Permill::from_float(0.03), trader: Permill::from_float(0.01), + external: Permill::from_float(0.002), }, ), ]) diff --git a/pallets/referrals/src/tests/tiers.rs b/pallets/referrals/src/tests/tiers.rs index c48007bee..c219c43ef 100644 --- a/pallets/referrals/src/tests/tiers.rs +++ b/pallets/referrals/src/tests/tiers.rs @@ -10,8 +10,11 @@ fn setting_asset_tier_should_fail_when_not_correct_origin() { RuntimeOrigin::signed(BOB), DAI, Level::Tier0, - Permill::from_percent(1), - Permill::from_percent(2), + Tier { + referrer: Permill::from_percent(1), + trader: Permill::from_percent(2), + external: Permill::from_percent(2), + } ), BadOrigin ); @@ -25,15 +28,19 @@ fn setting_asset_tier_should_correctly_update_storage() { RuntimeOrigin::root(), DAI, Level::Tier0, - Permill::from_percent(1), - Permill::from_percent(2), + Tier { + referrer: Permill::from_percent(1), + trader: Permill::from_percent(2), + external: Permill::from_percent(3), + } )); let d = AssetTier::::get(DAI, Level::Tier0); assert_eq!( d, Some(Tier { referrer: Permill::from_percent(1), - trader: Permill::from_percent(2) + trader: Permill::from_percent(2), + external: Permill::from_percent(3), }) ) }); @@ -47,8 +54,11 @@ fn setting_asset_tier_should_fail_when_total_percentage_exceeds_hundred_percent( RuntimeOrigin::root(), DAI, Level::Tier0, - Permill::from_percent(70), - Permill::from_percent(40), + Tier { + referrer: Permill::from_percent(60), + trader: Permill::from_percent(40), + external: Permill::from_percent(10), + } ), Error::::IncorrectRewardPercentage ); @@ -62,14 +72,20 @@ fn setting_asset_tier_should_emit_event() { RuntimeOrigin::root(), DAI, Level::Tier0, - Permill::from_percent(1), - Permill::from_percent(2), + Tier { + referrer: Permill::from_percent(1), + trader: Permill::from_percent(2), + external: Permill::from_percent(3), + } )); expect_events(vec![Event::TierRewardSet { asset_id: DAI, level: Level::Tier0, - referrer: Permill::from_percent(1), - trader: Permill::from_percent(2), + tier: Tier { + referrer: Permill::from_percent(1), + trader: Permill::from_percent(2), + external: Permill::from_percent(3), + }, } .into()]); }); diff --git a/pallets/referrals/src/tests/trade_fee.rs b/pallets/referrals/src/tests/trade_fee.rs index 3f0445204..f77719682 100644 --- a/pallets/referrals/src/tests/trade_fee.rs +++ b/pallets/referrals/src/tests/trade_fee.rs @@ -12,6 +12,7 @@ fn process_trade_fee_should_increased_referrer_shares() { Tier { referrer: Permill::from_percent(50), trader: Permill::zero(), + external: Permill::zero(), }, )]) .build() @@ -39,6 +40,7 @@ fn process_trade_fee_should_increased_trader_shares() { Tier { referrer: Permill::from_percent(50), trader: Permill::from_percent(20), + external: Permill::zero(), }, )]) .build() @@ -66,6 +68,7 @@ fn process_trade_fee_should_increased_total_share_issuance() { Tier { referrer: Permill::from_percent(50), trader: Permill::from_percent(20), + external: Permill::zero(), }, )]) .build() @@ -83,7 +86,7 @@ fn process_trade_fee_should_increased_total_share_issuance() { } #[test] -fn process_trade_fee_should_fail_when_taken_amount_is_greated_than_fee_amount() { +fn process_trade_fee_should_fail_when_taken_amount_is_greater_than_fee_amount() { ExtBuilder::default() .with_endowed_accounts(vec![(BOB, DAI, 2_000_000_000_000_000_000)]) .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) @@ -93,6 +96,7 @@ fn process_trade_fee_should_fail_when_taken_amount_is_greated_than_fee_amount() Tier { referrer: Permill::from_percent(50), trader: Permill::from_percent(70), + external: Permill::zero(), }, )]) .build() @@ -142,6 +146,7 @@ fn process_trade_fee_should_add_asset_to_asset_list() { Tier { referrer: Permill::from_percent(50), trader: Permill::from_percent(20), + external: Permill::zero(), }, )]) .build() @@ -169,6 +174,7 @@ fn process_trade_fee_should_not_add_reward_asset_to_asset_list() { Tier { referrer: Permill::from_percent(50), trader: Permill::from_percent(20), + external: Permill::zero(), }, )]) .build() @@ -184,3 +190,156 @@ fn process_trade_fee_should_not_add_reward_asset_to_asset_list() { assert_eq!(asset, None); }); } + +#[test] +fn process_trade_fee_should_increase_external_account_shares_when_trader_has_no_code_linked() { + let mut none_rewards = HashMap::new(); + none_rewards.insert( + Level::None, + Tier { + referrer: Default::default(), + trader: Default::default(), + external: Permill::from_percent(50), + }, + ); + + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, DAI, 2_000_000_000_000_000_000)]) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) + .with_global_tier_rewards(none_rewards) + .with_external_account(12345) + .build() + .execute_with(|| { + // Act + assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000)); + // Assert + let shares = Shares::::get(12345); + assert_eq!(shares, 5_000_000_000); + let shares = TotalShares::::get(); + assert_eq!(shares, 5_000_000_000); + }); +} + +#[test] +fn process_trade_fee_should_transfer_fee_to_pot_when_no_code_linked() { + let mut none_rewards = HashMap::new(); + none_rewards.insert( + Level::None, + Tier { + referrer: Default::default(), + trader: Default::default(), + external: Permill::from_percent(50), + }, + ); + + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, DAI, 2_000_000_000_000_000_000)]) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) + .with_global_tier_rewards(none_rewards) + .with_external_account(12345) + .build() + .execute_with(|| { + // Act + assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000)); + // Assert + let reserve = Tokens::free_balance(DAI, &Referrals::pot_account_id()); + assert_eq!(reserve, 5_000_000_000_000_000); + }); +} + +#[test] +fn process_trade_fee_should_reward_all_parties_based_on_global_config_when_asset_not_set_explicitly() { + let mut global_rewards = HashMap::new(); + global_rewards.insert( + Level::None, + Tier { + referrer: Default::default(), + trader: Default::default(), + external: Permill::from_percent(50), + }, + ); + global_rewards.insert( + Level::Tier0, + Tier { + referrer: Permill::from_percent(5), + trader: Permill::from_percent(5), + external: Permill::from_percent(40), + }, + ); + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, HDX, 2_000_000_000_000)]) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) + .with_global_tier_rewards(global_rewards) + .with_external_account(12345) + .build() + .execute_with(|| { + // ARRANGE + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code)); + // Act + assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), DAI, HDX, 1_000_000_000_000_000_000)); + // Assert + let referrer_shares = Shares::::get(ALICE); + assert_eq!(referrer_shares, 500_000_000); + let trader_shares = Shares::::get(BOB); + assert_eq!(trader_shares, 500_000_000); + let external_shares = Shares::::get(12345); + assert_eq!(external_shares, 4_000_000_000); + let shares = TotalShares::::get(); + assert_eq!(shares, 5_000_000_000); + }); +} + +#[test] +fn process_trade_fee_should_use_configured_asset_instead_of_global_when_set() { + let mut global_rewards = HashMap::new(); + global_rewards.insert( + Level::None, + Tier { + referrer: Default::default(), + trader: Default::default(), + external: Permill::from_percent(50), + }, + ); + global_rewards.insert( + Level::Tier0, + Tier { + referrer: Permill::from_percent(5), + trader: Permill::from_percent(5), + external: Permill::from_percent(40), + }, + ); + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, DAI, 2_000_000_000_000_000_000)]) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) + .with_tiers(vec![( + DAI, + Level::Tier0, + Tier { + referrer: Permill::from_percent(10), + trader: Permill::from_percent(5), + external: Permill::from_percent(30), + }, + )]) + .with_global_tier_rewards(global_rewards) + .with_external_account(12345) + .build() + .execute_with(|| { + // ARRANGE + let code: ReferralCode<::CodeLength> = b"BALLS69".to_vec().try_into().unwrap(); + assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); + assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code)); + // Act + assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000)); + // Assert + let referrer_shares = Shares::::get(ALICE); + assert_eq!(referrer_shares, 1_000_000_000); + let trader_shares = Shares::::get(BOB); + assert_eq!(trader_shares, 500_000_000); + let external_shares = Shares::::get(12345); + assert_eq!(external_shares, 3_000_000_000); + let shares = TotalShares::::get(); + assert_eq!(shares, 3_000_000_000 + 1_000_000_000 + 500_000_000); + }); +} diff --git a/pallets/staking/src/lib.rs b/pallets/staking/src/lib.rs index 6ea350a30..fe46b2287 100644 --- a/pallets/staking/src/lib.rs +++ b/pallets/staking/src/lib.rs @@ -938,9 +938,9 @@ impl Pallet { ) -> Result { if asset == T::NativeAssetId::get() && Self::is_initialized() { T::Currency::transfer(asset, &source, &Self::pot_account_id(), amount)?; - Ok(Balance::zero()) - } else { Ok(amount) + } else { + Ok(Balance::zero()) } } diff --git a/runtime/adapters/src/lib.rs b/runtime/adapters/src/lib.rs index b6721abe3..5fc4b5823 100644 --- a/runtime/adapters/src/lib.rs +++ b/runtime/adapters/src/lib.rs @@ -469,13 +469,14 @@ where asset: AssetId, amount: Balance, ) -> Result { - let unused = pallet_referrals::Pallet::::process_trade_fee( + let referrals_used = pallet_referrals::Pallet::::process_trade_fee( fee_account.clone().into(), trader.into(), asset.into(), amount, )?; - pallet_staking::Pallet::::process_trade_fee(fee_account.into(), asset.into(), unused) + let staking_used = pallet_staking::Pallet::::process_trade_fee(fee_account.into(), asset.into(), amount.saturating_sub(referrals_used))?; + Ok(staking_used.saturating_add(referrals_used)) } } diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 4beb8d4c9..a4c003c8e 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -805,7 +805,7 @@ use hydradx_adapters::price::OraclePriceProviderUsingRoute; #[cfg(feature = "runtime-benchmarks")] use hydradx_traits::price::PriceProvider; use pallet_referrals::traits::Convert; -use pallet_referrals::Level; +use pallet_referrals::{Level, Tier}; #[cfg(feature = "runtime-benchmarks")] use pallet_stableswap::BenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] @@ -1000,7 +1000,8 @@ parameter_types! { pub RegistrationFee: (AssetId,Balance, AccountId)= (NativeAssetId::get(), 222_000_000_000_000, TreasuryAccount::get()); pub const MaxCodeLength: u32 = 7; pub const ReferralsOraclePeriod: OraclePeriod = OraclePeriod::TenMinutes; - pub const ReferralsSeedAmount: Balance = 100_000_000_000_000; + pub const ReferralsSeedAmount: Balance = 10_000_000_000_000; + pub ReferralsExternalRewardAccount: Option = Some(StakingPalletId::get().into_account_truncating()); } impl pallet_referrals::Config for Runtime { @@ -1019,6 +1020,8 @@ impl pallet_referrals::Config for Runtime { type RegistrationFee = RegistrationFee; type CodeLength = MaxCodeLength; type TierVolume = ReferralsLevelTiers; + type TierRewardPercentages = ReferralsTierRewards; + type ExternalAccount = ReferralsExternalRewardAccount; type SeedNativeAmount = ReferralsSeedAmount; type WeightInfo = weights::referrals::HydraWeight; #[cfg(feature = "runtime-benchmarks")] @@ -1071,7 +1074,7 @@ pub struct ReferralsLevelTiers; impl GetByKey for ReferralsLevelTiers { fn get(k: &Level) -> Balance { match k { - Level::Tier0 => 0, + Level::Tier0 | Level::None => 0, Level::Tier1 => 2_000_000_000_000_000, Level::Tier2 => 22_000_000_000_000_000, Level::Tier3 => 222_000_000_000_000_000, @@ -1080,6 +1083,44 @@ impl GetByKey for ReferralsLevelTiers { } } +pub struct ReferralsTierRewards; +impl GetByKey for ReferralsTierRewards { + fn get(k: &Level) -> Tier { + match k { + Level::None => Tier{ + referrer: Permill::zero(), + trader: Permill::zero(), + external: Permill::from_percent(50), + }, + Level::Tier0 => Tier{ + referrer: Permill::from_percent(5), + trader: Permill::from_percent(10), + external: Permill::from_percent(35), + }, + Level::Tier1 => Tier{ + referrer: Permill::from_percent(10), + trader: Permill::from_percent(11), + external: Permill::from_percent(29), + }, + Level::Tier2 => Tier{ + referrer: Permill::from_percent(15), + trader: Permill::from_percent(12), + external: Permill::from_percent(23), + }, + Level::Tier3 => Tier{ + referrer: Permill::from_percent(20), + trader: Permill::from_percent(13), + external: Permill::from_percent(17), + }, + Level::Tier4 => Tier{ + referrer: Permill::from_percent(25), + trader: Permill::from_percent(15), + external: Permill::from_percent(10), + }, + } + } +} + #[cfg(feature = "runtime-benchmarks")] use pallet_referrals::BenchmarkHelper as RefBenchmarkHelper; From 76b0dfce77bbaaddc56abadb05a04a7fa57c58cc Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 18 Dec 2023 12:35:26 +0100 Subject: [PATCH 77/90] reformat --- pallets/referrals/src/lib.rs | 17 ++++++++++------- pallets/referrals/src/tests/trade_fee.rs | 7 ++++++- runtime/adapters/src/lib.rs | 6 +++++- runtime/hydradx/src/assets.rs | 12 ++++++------ 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index f7f9716e9..42eaa7441 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -60,7 +60,10 @@ use sp_core::U256; use sp_runtime::helpers_128bit::multiply_by_rational_with_rounding; use sp_runtime::traits::AccountIdConversion; use sp_runtime::Rounding; -use sp_runtime::{traits::{CheckedAdd,Zero}, ArithmeticError, DispatchError, Permill}; +use sp_runtime::{ + traits::{CheckedAdd, Zero}, + ArithmeticError, DispatchError, Permill, +}; #[cfg(feature = "runtime-benchmarks")] pub use crate::traits::BenchmarkHelper; @@ -595,15 +598,15 @@ impl Pallet { return Ok(Balance::zero()); }; - let (level,ref_account) = if let Some(acc) = Self::linked_referral_account(&trader) { - if let Some((level,_)) = Self::referrer_level(&acc) { + let (level, ref_account) = if let Some(acc) = Self::linked_referral_account(&trader) { + if let Some((level, _)) = Self::referrer_level(&acc) { // Should not really happen, the ref entry should be always there. (level, Some(acc)) - }else{ + } else { defensive!("Referrer details not found"); return Ok(Balance::zero()); } - }else{ + } else { (Level::None, None) }; @@ -614,7 +617,7 @@ impl Pallet { let external_account = T::ExternalAccount::get(); let referrer_reward = if ref_account.is_some() { tier.referrer.mul_floor(amount) - }else{ + } else { 0 }; let trader_reward = tier.trader.mul_floor(amount); @@ -632,7 +635,7 @@ impl Pallet { let referrer_shares = if ref_account.is_some() { multiply_by_rational_with_rounding(referrer_reward, price.n, price.d, Rounding::Down) .ok_or(ArithmeticError::Overflow)? - }else{ + } else { 0 }; let trader_shares = multiply_by_rational_with_rounding(trader_reward, price.n, price.d, Rounding::Down) diff --git a/pallets/referrals/src/tests/trade_fee.rs b/pallets/referrals/src/tests/trade_fee.rs index f77719682..4eef7ac99 100644 --- a/pallets/referrals/src/tests/trade_fee.rs +++ b/pallets/referrals/src/tests/trade_fee.rs @@ -278,7 +278,12 @@ fn process_trade_fee_should_reward_all_parties_based_on_global_config_when_asset assert_ok!(Referrals::register_code(RuntimeOrigin::signed(ALICE), code.clone(),)); assert_ok!(Referrals::link_code(RuntimeOrigin::signed(BOB), code)); // Act - assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), DAI, HDX, 1_000_000_000_000_000_000)); + assert_ok!(MockAmm::trade( + RuntimeOrigin::signed(BOB), + DAI, + HDX, + 1_000_000_000_000_000_000 + )); // Assert let referrer_shares = Shares::::get(ALICE); assert_eq!(referrer_shares, 500_000_000); diff --git a/runtime/adapters/src/lib.rs b/runtime/adapters/src/lib.rs index 5fc4b5823..32b648e73 100644 --- a/runtime/adapters/src/lib.rs +++ b/runtime/adapters/src/lib.rs @@ -475,7 +475,11 @@ where asset.into(), amount, )?; - let staking_used = pallet_staking::Pallet::::process_trade_fee(fee_account.into(), asset.into(), amount.saturating_sub(referrals_used))?; + let staking_used = pallet_staking::Pallet::::process_trade_fee( + fee_account.into(), + asset.into(), + amount.saturating_sub(referrals_used), + )?; Ok(staking_used.saturating_add(referrals_used)) } } diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index a4c003c8e..8d250db3f 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -1087,32 +1087,32 @@ pub struct ReferralsTierRewards; impl GetByKey for ReferralsTierRewards { fn get(k: &Level) -> Tier { match k { - Level::None => Tier{ + Level::None => Tier { referrer: Permill::zero(), trader: Permill::zero(), external: Permill::from_percent(50), }, - Level::Tier0 => Tier{ + Level::Tier0 => Tier { referrer: Permill::from_percent(5), trader: Permill::from_percent(10), external: Permill::from_percent(35), }, - Level::Tier1 => Tier{ + Level::Tier1 => Tier { referrer: Permill::from_percent(10), trader: Permill::from_percent(11), external: Permill::from_percent(29), }, - Level::Tier2 => Tier{ + Level::Tier2 => Tier { referrer: Permill::from_percent(15), trader: Permill::from_percent(12), external: Permill::from_percent(23), }, - Level::Tier3 => Tier{ + Level::Tier3 => Tier { referrer: Permill::from_percent(20), trader: Permill::from_percent(13), external: Permill::from_percent(17), }, - Level::Tier4 => Tier{ + Level::Tier4 => Tier { referrer: Permill::from_percent(25), trader: Permill::from_percent(15), external: Permill::from_percent(10), From 2fb7ddd6581ccbd84f95c7c18bfe31ed687e6d10 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 18 Dec 2023 12:39:56 +0100 Subject: [PATCH 78/90] happy clippy happy life --- pallets/omnipool/src/lib.rs | 2 +- pallets/referrals/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index dba5693d7..4192807ed 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -1959,7 +1959,7 @@ impl Pallet { let asset_reserve = T::Currency::free_balance(asset, &account); let diff = original_asset_reserve.saturating_sub(asset_reserve); ensure!(diff <= amount, Error::::FeeOverdraft); - ensure!(diff == used , Error::::FeeOverdraft); + ensure!(diff == used, Error::::FeeOverdraft); Ok(()) } diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 42eaa7441..2fd10a8ee 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -611,7 +611,7 @@ impl Pallet { }; // What is asset fee for this level? if not explicitly set, use global parameter. - let tier = Self::asset_tier(asset_id, level).unwrap_or(T::TierRewardPercentages::get(&level)); + let tier = Self::asset_tier(asset_id, level).unwrap_or_else(|| T::TierRewardPercentages::get(&level)); // Rewards let external_account = T::ExternalAccount::get(); From ff092034fc9bfd7b7f340853dd78bad1d6c02206 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 18 Dec 2023 12:50:24 +0100 Subject: [PATCH 79/90] happy clippy happy life --- pallets/referrals/src/lib.rs | 3 +-- runtime/hydradx/src/benchmarking/omnipool.rs | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 2fd10a8ee..8f6e11f04 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -517,8 +517,7 @@ pub mod pallet { /// Parameters: /// - `asset_id`: asset id /// - `level`: level - /// - `referrer`: referrer percentage that goes to the referrer. - /// - `trader`: trader percentage that goes back to the trader. + /// - `tier`: reward tier percentages /// /// Emits `TierRewardSet` event when successful. #[pallet::call_index(4)] diff --git a/runtime/hydradx/src/benchmarking/omnipool.rs b/runtime/hydradx/src/benchmarking/omnipool.rs index 0b421d528..921a5eba1 100644 --- a/runtime/hydradx/src/benchmarking/omnipool.rs +++ b/runtime/hydradx/src/benchmarking/omnipool.rs @@ -226,7 +226,6 @@ runtime_benchmarks! { let code = ReferralCode::<::CodeLength>::truncate_from(b"MYCODE".to_vec()); Referrals::register_code(RawOrigin::Signed(owner).into(), code.clone())?; Referrals::link_code(RawOrigin::Signed(seller.clone()).into(), code)?; - Referrals::set_reward_percentage(RawOrigin::Root.into(), DAI, pallet_referrals::Level::Tier0, Permill::from_percent(1), Permill::from_percent(1))?; }: { Omnipool::sell(RawOrigin::Signed(seller.clone()).into(), token_id, DAI, amount_sell, buy_min_amount)? } verify { assert!(::Currency::free_balance(DAI, &seller) >= buy_min_amount); @@ -274,7 +273,6 @@ runtime_benchmarks! { let code = ReferralCode::<::CodeLength>::truncate_from(b"MYCODE".to_vec()); Referrals::register_code(RawOrigin::Signed(owner).into(), code.clone())?; Referrals::link_code(RawOrigin::Signed(seller.clone()).into(), code)?; - Referrals::set_reward_percentage(RawOrigin::Root.into(), token_id, pallet_referrals::Level::Tier0, Permill::from_percent(1), Permill::from_percent(1))?; }: { Omnipool::buy(RawOrigin::Signed(seller.clone()).into(), DAI, token_id, amount_buy, sell_max_limit)? } verify { assert!(::Currency::free_balance(DAI, &seller) >= Balance::zero()); From f9329c9719d108aec585eb7aa7878fc8423b8006 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 18 Dec 2023 13:17:08 +0100 Subject: [PATCH 80/90] on trade fee hook result change --- pallets/omnipool/src/traits.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pallets/omnipool/src/traits.rs b/pallets/omnipool/src/traits.rs index 729c1a222..0265edd61 100644 --- a/pallets/omnipool/src/traits.rs +++ b/pallets/omnipool/src/traits.rs @@ -5,7 +5,7 @@ use frame_support::traits::Contains; use frame_support::weights::Weight; use hydra_dx_math::ema::EmaPrice; use hydra_dx_math::omnipool::types::AssetStateChange; -use sp_runtime::traits::{CheckedAdd, CheckedMul, Get, Saturating}; +use sp_runtime::traits::{CheckedAdd, CheckedMul, Get, Saturating, Zero}; use sp_runtime::{DispatchError, FixedPointNumber, FixedU128, Permill}; pub struct AssetInfo @@ -68,7 +68,7 @@ where impl OmnipoolHooks for () where - Balance: Default + Clone, + Balance: Default + Clone + Zero, { type Error = DispatchError; @@ -100,9 +100,9 @@ where _fee_account: AccountId, _trader: AccountId, _asset: AssetId, - amount: Balance, + _amount: Balance, ) -> Result { - Ok(amount) + Ok(Balance::zero()) } } From 3913f5040c90b6e3d279308a3b1c1d6fee32c610 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 18 Dec 2023 13:17:29 +0100 Subject: [PATCH 81/90] version bump --- Cargo.lock | 8 ++++---- pallets/staking/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 94d8869ca..60286d6d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4202,7 +4202,7 @@ dependencies = [ "pallet-referrals", "pallet-route-executor", "pallet-stableswap", - "pallet-staking 2.1.0", + "pallet-staking 2.1.1", "pallet-transaction-multi-payment", "pallet-uniques", "pallet-xyk", @@ -4295,7 +4295,7 @@ dependencies = [ "pallet-scheduler", "pallet-session", "pallet-stableswap", - "pallet-staking 2.1.0", + "pallet-staking 2.1.1", "pallet-timestamp", "pallet-tips", "pallet-transaction-multi-payment", @@ -7957,7 +7957,7 @@ dependencies = [ [[package]] name = "pallet-staking" -version = "2.1.0" +version = "2.1.1" dependencies = [ "frame-benchmarking", "frame-support", @@ -10849,7 +10849,7 @@ dependencies = [ "pallet-scheduler", "pallet-session", "pallet-stableswap", - "pallet-staking 2.1.0", + "pallet-staking 2.1.1", "pallet-sudo", "pallet-timestamp", "pallet-tips", diff --git a/pallets/staking/Cargo.toml b/pallets/staking/Cargo.toml index f48b997ff..992c459e0 100644 --- a/pallets/staking/Cargo.toml +++ b/pallets/staking/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-staking" -version = "2.1.0" +version = "2.1.1" authors = ['GalacticCouncil'] edition = "2021" license = "Apache-2.0" From 716cce2324334169d5fc004594a6ef1da321745a Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 18 Dec 2023 15:44:20 +0100 Subject: [PATCH 82/90] set referrals rewards to 0 for some integration tests --- integration-tests/src/dca.rs | 19 ++++- integration-tests/src/dynamic_fees.rs | 4 + integration-tests/src/polkadot_test_net.rs | 15 ++++ integration-tests/src/referrals.rs | 85 +++++++++------------- 4 files changed, 71 insertions(+), 52 deletions(-) diff --git a/integration-tests/src/dca.rs b/integration-tests/src/dca.rs index 6db21c20b..b5c230aff 100644 --- a/integration-tests/src/dca.rs +++ b/integration-tests/src/dca.rs @@ -1326,6 +1326,7 @@ mod stableswap { Permill::from_percent(100), AccountId::from(BOB), )); + set_zero_reward_for_referrals(pool_id); do_trade_to_populate_oracle(DAI, HDX, UNITS); set_relaychain_block_number(10); @@ -1426,7 +1427,7 @@ mod stableswap { Permill::from_percent(100), AccountId::from(BOB), )); - + set_zero_reward_for_referrals(pool_id); do_trade_to_populate_oracle(DAI, HDX, UNITS); set_relaychain_block_number(10); @@ -1490,6 +1491,7 @@ mod stableswap { Permill::from_percent(100), AccountId::from(BOB), )); + set_zero_reward_for_referrals(pool_id); do_trade_to_populate_oracle(DAI, HDX, UNITS); set_relaychain_block_number(10); @@ -1578,7 +1580,7 @@ mod stableswap { Permill::from_percent(100), AccountId::from(BOB), )); - + set_zero_reward_for_referrals(pool_id); //Populate oracle with omnipool source assert_ok!(Tokens::set_balance( RawOrigin::Root.into(), @@ -1693,6 +1695,7 @@ mod stableswap { Permill::from_percent(100), AccountId::from(BOB), )); + set_zero_reward_for_referrals(pool_id); //Populate oracle with omnipool source assert_ok!(Tokens::set_balance( @@ -1788,6 +1791,7 @@ mod stableswap { Permill::from_percent(100), AccountId::from(BOB), )); + set_zero_reward_for_referrals(pool_id); //Populate oracle with omnipool source assert_ok!(Tokens::set_balance( @@ -1890,6 +1894,7 @@ mod stableswap { AccountId::from(BOB), )); do_trade_to_populate_oracle(DAI, HDX, UNITS); + set_zero_reward_for_referrals(pool_id); set_relaychain_block_number(10); @@ -2061,7 +2066,7 @@ mod stableswap { Permill::from_percent(100), AccountId::from(BOB), )); - + set_zero_reward_for_referrals(pool_id); do_trade_to_populate_oracle(pool_id, HDX, 100 * UNITS); set_relaychain_block_number(10); @@ -2293,6 +2298,7 @@ mod all_pools { Permill::from_percent(100), AccountId::from(BOB), )); + set_zero_reward_for_referrals(pool_id); do_trade_to_populate_oracle(DAI, HDX, UNITS); //Create xyk and populate oracle @@ -2445,6 +2451,7 @@ mod with_onchain_route { Permill::from_percent(100), AccountId::from(BOB), )); + set_zero_reward_for_referrals(pool_id); do_trade_to_populate_oracle(DAI, HDX, UNITS); set_relaychain_block_number(10); @@ -2555,6 +2562,7 @@ mod with_onchain_route { Permill::from_percent(100), AccountId::from(BOB), )); + set_zero_reward_for_referrals(pool_id); do_trade_to_populate_oracle(DAI, HDX, UNITS); set_relaychain_block_number(10); @@ -2656,6 +2664,7 @@ mod with_onchain_route { Permill::from_percent(100), AccountId::from(BOB), )); + set_zero_reward_for_referrals(DOT); do_trade_to_populate_oracle(DAI, HDX, UNITS); assert_ok!(Currencies::update_balance( @@ -2775,6 +2784,7 @@ mod with_onchain_route { Permill::from_percent(100), AccountId::from(BOB), )); + set_zero_reward_for_referrals(pool_id); do_trade_to_populate_oracle(pool_id, HDX, 10000000 * UNITS); set_relaychain_block_number(10); @@ -3172,6 +3182,9 @@ pub fn init_omnipol() { TREASURY_ACCOUNT_INIT_BALANCE, 0, )); + + set_zero_reward_for_referrals(HDX); + set_zero_reward_for_referrals(DAI); } fn init_omnipool_with_oracle_for_block_10() { diff --git a/integration-tests/src/dynamic_fees.rs b/integration-tests/src/dynamic_fees.rs index 1f7eb55b5..4fde05718 100644 --- a/integration-tests/src/dynamic_fees.rs +++ b/integration-tests/src/dynamic_fees.rs @@ -365,6 +365,10 @@ fn init_omnipool() { Permill::from_percent(100), AccountId::from(BOB), )); + set_zero_reward_for_referrals(HDX); + set_zero_reward_for_referrals(DAI); + set_zero_reward_for_referrals(DOT); + set_zero_reward_for_referrals(ETH); } /// This function executes one sell and buy with HDX for all assets in the omnipool. This is necessary to diff --git a/integration-tests/src/polkadot_test_net.rs b/integration-tests/src/polkadot_test_net.rs index 9ac5e1dad..dc7ba724b 100644 --- a/integration-tests/src/polkadot_test_net.rs +++ b/integration-tests/src/polkadot_test_net.rs @@ -17,10 +17,13 @@ pub use primitives::{constants::chain::CORE_ASSET_ID, AssetId, Balance, Moment}; use cumulus_primitives_core::ParaId; use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; +use frame_system::RawOrigin; use hex_literal::hex; use hydradx_runtime::evm::WETH_ASSET_LOCATION; +use hydradx_runtime::Referrals; use hydradx_runtime::RuntimeOrigin; use pallet_evm::AddressMapping; +use pallet_referrals::{Level, Tier}; use polkadot_primitives::v2::{BlockNumber, MAX_CODE_SIZE, MAX_POV_SIZE}; use polkadot_runtime_parachains::configuration::HostConfiguration; use sp_core::H160; @@ -525,6 +528,9 @@ pub fn init_omnipool() { hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), stable_position_id, )); + + set_zero_reward_for_referrals(DAI); + set_zero_reward_for_referrals(HDX); } #[macro_export] @@ -540,3 +546,12 @@ macro_rules! assert_reserved_balance { assert_eq!(Currencies::reserved_balance($asset, &$who), $amount); }}; } + +pub fn set_zero_reward_for_referrals(asset_id: AssetId) { + assert_ok!(Referrals::set_reward_percentage( + RawOrigin::Root.into(), + asset_id, + Level::None, + Tier::default(), + )); +} diff --git a/integration-tests/src/referrals.rs b/integration-tests/src/referrals.rs index 07341c959..0125e6ede 100644 --- a/integration-tests/src/referrals.rs +++ b/integration-tests/src/referrals.rs @@ -6,6 +6,7 @@ use hydradx_runtime::{Currencies, Omnipool, Referrals, Runtime, RuntimeOrigin, S use orml_traits::MultiCurrency; use pallet_referrals::{ReferralCode, Tier}; use primitives::AccountId; +use sp_runtime::FixedU128; use sp_runtime::Permill; use xcm_emulator::TestExt; @@ -292,6 +293,41 @@ fn trading_in_omnipool_should_increase_staking_shares_when_no_code_linked() { }); } +fn init_omnipool() { + let native_price = FixedU128::from_inner(1201500000000000); + let stable_price = FixedU128::from_inner(45_000_000_000); + + let native_position_id = hydradx_runtime::Omnipool::next_position_id(); + + assert_ok!(hydradx_runtime::Omnipool::add_token( + hydradx_runtime::RuntimeOrigin::root(), + HDX, + native_price, + Permill::from_percent(10), + AccountId::from(ALICE), + )); + + let stable_position_id = hydradx_runtime::Omnipool::next_position_id(); + + assert_ok!(hydradx_runtime::Omnipool::add_token( + hydradx_runtime::RuntimeOrigin::root(), + DAI, + stable_price, + Permill::from_percent(100), + AccountId::from(ALICE), + )); + + assert_ok!(hydradx_runtime::Omnipool::sacrifice_position( + hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + native_position_id, + )); + + assert_ok!(hydradx_runtime::Omnipool::sacrifice_position( + hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + stable_position_id, + )); +} + fn init_omnipool_with_oracle_for_block_10() { init_omnipool(); do_trade_to_populate_oracle(DAI, HDX, UNITS); @@ -334,52 +370,3 @@ fn seed_pot_account() { (10 * UNITS) as i128, )); } - -/* -fn init_referrals_program() { - assert_ok!(Referrals::set_reward_percentage( - RuntimeOrigin::root(), - HDX, - pallet_referrals::Level::Tier0, - Permill::from_percent(2), - Permill::from_percent(1), - )); - assert_ok!(Referrals::set_reward_percentage( - RuntimeOrigin::root(), - HDX, - pallet_referrals::Level::Tier1, - Permill::from_percent(5), - Permill::from_percent(2), - )); - assert_ok!(Referrals::set_reward_percentage( - RuntimeOrigin::root(), - HDX, - pallet_referrals::Level::Tier2, - Permill::from_percent(10), - Permill::from_percent(5), - )); - assert_ok!(Referrals::set_reward_percentage( - RuntimeOrigin::root(), - DAI, - pallet_referrals::Level::Tier0, - Permill::from_percent(2), - Permill::from_percent(1), - )); - assert_ok!(Referrals::set_reward_percentage( - RuntimeOrigin::root(), - DAI, - pallet_referrals::Level::Tier1, - Permill::from_percent(5), - Permill::from_percent(2), - )); - assert_ok!(Referrals::set_reward_percentage( - RuntimeOrigin::root(), - DAI, - pallet_referrals::Level::Tier2, - Permill::from_percent(10), - Permill::from_percent(5), - )); - - seed_pot_account(); -} - */ From 5467e5ca93b0d5f5315c7c5eb446973ec41aae34 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 19 Dec 2023 08:50:04 +0100 Subject: [PATCH 83/90] update tier threshold --- runtime/hydradx/src/assets.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 8d250db3f..357a0ce6e 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -1075,10 +1075,10 @@ impl GetByKey for ReferralsLevelTiers { fn get(k: &Level) -> Balance { match k { Level::Tier0 | Level::None => 0, - Level::Tier1 => 2_000_000_000_000_000, - Level::Tier2 => 22_000_000_000_000_000, - Level::Tier3 => 222_000_000_000_000_000, - Level::Tier4 => 2_222_000_000_000_000_000, + Level::Tier1 => 1_222_222_000_000_000_000, + Level::Tier2 => 12_222_220_000_000_000_000, + Level::Tier3 => 122_222_200_000_000_000_000, + Level::Tier4 => 1_222_222_000_000_000_000_000, } } } From fe144ab43f91d783343d87c07bc2dea7f221915a Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 19 Dec 2023 11:13:14 +0100 Subject: [PATCH 84/90] Add tests of on idle --- pallets/referrals/src/tests/convert.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pallets/referrals/src/tests/convert.rs b/pallets/referrals/src/tests/convert.rs index ab3eccba6..d89f8bc87 100644 --- a/pallets/referrals/src/tests/convert.rs +++ b/pallets/referrals/src/tests/convert.rs @@ -1,4 +1,5 @@ use crate::tests::*; +use frame_support::traits::Hooks; use pretty_assertions::assert_eq; #[test] @@ -64,3 +65,23 @@ fn convert_should_emit_event_when_successful() { .into()]); }); } + +#[test] +fn on_idle_should_convert_all_asset_amount_when_successful() { + ExtBuilder::default() + .with_endowed_accounts(vec![(Pallet::::pot_account_id(), DAI, 1_000_000_000_000_000_000)]) + .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) + .with_assets(vec![DAI]) + .build() + .execute_with(|| { + // Arrange + Referrals::on_idle(10, 1_000_000_000_000.into()); + // Assert + let balance = Tokens::free_balance(DAI, &Pallet::::pot_account_id()); + assert_eq!(balance, 0); + let balance = Tokens::free_balance(HDX, &Pallet::::pot_account_id()); + assert_eq!(balance, 1_000_000_000_000); + let entry = Assets::::get(DAI); + assert_eq!(entry, None) + }); +} From 1ef78506cd631932550b80d94c8d79e78a089211 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 19 Dec 2023 11:16:47 +0100 Subject: [PATCH 85/90] mark function only for runtime-benchmakrs features --- pallets/referrals/src/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 31049ac27..57d3a0855 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -308,6 +308,7 @@ impl ExtBuilder { self } + #[cfg(feature = "runtime-benchmarks")] pub fn with_default_volumes(self) -> Self { let mut volumes = HashMap::new(); volumes.insert(Level::Tier0, Some(0)); From 3ee5e274a4b8deb71ca26131522c701abf9cf357 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 20 Dec 2023 09:45:29 +0100 Subject: [PATCH 86/90] fix draining assets in claims --- pallets/referrals/src/lib.rs | 4 +++- pallets/referrals/src/tests/claim.rs | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 8f6e11f04..6e4e2121c 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -454,7 +454,7 @@ pub mod pallet { })] pub fn claim_rewards(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - for (asset_id, _) in Assets::::drain() { + for (asset_id, _) in Assets::::iter() { let asset_balance = T::Currency::balance(asset_id, &Self::pot_account_id()); let r = T::Convert::convert(Self::pot_account_id(), asset_id, T::RewardAsset::get(), asset_balance); if let Err(error) = r { @@ -466,6 +466,8 @@ pub mod pallet { } else { return Err(error); } + } else { + Assets::::remove(asset_id); } } let shares = Shares::::take(&who); diff --git a/pallets/referrals/src/tests/claim.rs b/pallets/referrals/src/tests/claim.rs index ffea7bae8..e3b2dc779 100644 --- a/pallets/referrals/src/tests/claim.rs +++ b/pallets/referrals/src/tests/claim.rs @@ -39,6 +39,7 @@ fn claim_rewards_should_remove_assets_from_the_list() { (Pallet::::pot_account_id(), DAI, 3_000_000_000_000_000_000), (Pallet::::pot_account_id(), DOT, 4_000_000_000_000), ]) + .with_assets(vec![DAI, DOT]) .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) .with_conversion_price((HDX, DOT), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000)) .build() From 0c85184a8539cf22ee0f7e2a3f87aeaa691b50b8 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 20 Dec 2023 09:48:40 +0100 Subject: [PATCH 87/90] rename Assets to PendingConversions --- pallets/referrals/src/benchmarking.rs | 6 +++--- pallets/referrals/src/lib.rs | 18 +++++++++--------- pallets/referrals/src/tests.rs | 2 +- pallets/referrals/src/tests/claim.rs | 2 +- pallets/referrals/src/tests/convert.rs | 4 ++-- pallets/referrals/src/tests/trade_fee.rs | 4 ++-- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/pallets/referrals/src/benchmarking.rs b/pallets/referrals/src/benchmarking.rs index 5c61dc3a8..e37f3ecae 100644 --- a/pallets/referrals/src/benchmarking.rs +++ b/pallets/referrals/src/benchmarking.rs @@ -61,10 +61,10 @@ benchmarks! { let caller: T::AccountId = account("caller", 0, 1); let (asset_id, amount) = T::BenchmarkHelper::prepare_convertible_asset_and_amount(); T::Currency::mint_into(asset_id, &Pallet::::pot_account_id(), amount)?; - Assets::::insert(asset_id,()); + PendingConversions::::insert(asset_id,()); }: _(RawOrigin::Signed(caller), asset_id) verify { - let count = Assets::::iter().count(); + let count = PendingConversions::::iter().count(); assert_eq!(count , 0); let balance = T::Currency::balance(asset_id, &Pallet::::pot_account_id()); assert_eq!(balance, 0); @@ -86,7 +86,7 @@ benchmarks! { TotalShares::::put(1_000_000_000_000); }: _(RawOrigin::Signed(caller.clone())) verify { - let count = Assets::::iter().count(); + let count = PendingConversions::::iter().count(); assert_eq!(count , 0); let balance = T::Currency::balance(T::RewardAsset::get(), &caller); assert!(balance > caller_balance); diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 6e4e2121c..47b60710b 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -260,8 +260,8 @@ pub mod pallet { /// Information about assets that are currently in the rewards pot. /// Used to easily determine list of assets that need to be converted. #[pallet::storage] - #[pallet::getter(fn assets)] - pub(super) type Assets = StorageMap<_, Blake2_128Concat, T::AssetId, ()>; + #[pallet::getter(fn pending_conversions)] + pub(super) type PendingConversions = StorageMap<_, Blake2_128Concat, T::AssetId, ()>; #[pallet::event] #[pallet::generate_deposit(pub(crate) fn deposit_event)] @@ -425,7 +425,7 @@ pub mod pallet { let total_reward_asset = T::Convert::convert(Self::pot_account_id(), asset_id, T::RewardAsset::get(), asset_balance)?; - Assets::::remove(asset_id); + PendingConversions::::remove(asset_id); Self::deposit_event(Event::Converted { from: AssetAmount::new(asset_id, asset_balance), @@ -446,7 +446,7 @@ pub mod pallet { /// Emits `Claimed` event when successful. #[pallet::call_index(3)] #[pallet::weight( { - let c = Assets::::iter().count() as u64; + let c = PendingConversions::::iter().count() as u64; let convert_weight = (::WeightInfo::convert()).saturating_mul(c); let w = ::WeightInfo::claim_rewards(); let one_read = T::DbWeight::get().reads(1_u64); @@ -454,7 +454,7 @@ pub mod pallet { })] pub fn claim_rewards(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - for (asset_id, _) in Assets::::iter() { + for (asset_id, _) in PendingConversions::::iter() { let asset_balance = T::Currency::balance(asset_id, &Self::pot_account_id()); let r = T::Convert::convert(Self::pot_account_id(), asset_id, T::RewardAsset::get(), asset_balance); if let Err(error) = r { @@ -467,7 +467,7 @@ pub mod pallet { return Err(error); } } else { - Assets::::remove(asset_id); + PendingConversions::::remove(asset_id); } } let shares = Shares::::take(&who); @@ -560,11 +560,11 @@ pub mod pallet { let one_read = T::DbWeight::get().reads(1u64); let max_converts = remaining_weight.saturating_sub(one_read).ref_time() / convert_weight.ref_time(); - for asset_id in Assets::::iter_keys().take(max_converts as usize) { + for asset_id in PendingConversions::::iter_keys().take(max_converts as usize) { let asset_balance = T::Currency::balance(asset_id, &Self::pot_account_id()); let r = T::Convert::convert(Self::pot_account_id(), asset_id, T::RewardAsset::get(), asset_balance); if r.is_ok() { - Assets::::remove(asset_id); + PendingConversions::::remove(asset_id); } } convert_weight.saturating_mul(max_converts).saturating_add(one_read) @@ -669,7 +669,7 @@ impl Pallet { }); } if asset_id != T::RewardAsset::get() { - Assets::::insert(asset_id, ()); + PendingConversions::::insert(asset_id, ()); } Ok(total_taken) } diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 57d3a0855..b990d58dc 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -353,7 +353,7 @@ impl ExtBuilder { }); r.execute_with(|| { for asset in self.assets.iter() { - Assets::::insert(asset, ()); + PendingConversions::::insert(asset, ()); } }); r.execute_with(|| { diff --git a/pallets/referrals/src/tests/claim.rs b/pallets/referrals/src/tests/claim.rs index e3b2dc779..0a3470464 100644 --- a/pallets/referrals/src/tests/claim.rs +++ b/pallets/referrals/src/tests/claim.rs @@ -46,7 +46,7 @@ fn claim_rewards_should_remove_assets_from_the_list() { .execute_with(|| { assert_ok!(Referrals::claim_rewards(RuntimeOrigin::signed(BOB))); // Assert - let count = Assets::::iter().count(); + let count = PendingConversions::::iter().count(); assert_eq!(count, 0); }); } diff --git a/pallets/referrals/src/tests/convert.rs b/pallets/referrals/src/tests/convert.rs index d89f8bc87..7f30f6600 100644 --- a/pallets/referrals/src/tests/convert.rs +++ b/pallets/referrals/src/tests/convert.rs @@ -42,7 +42,7 @@ fn convert_should_remove_asset_from_the_asset_list() { // Arrange assert_ok!(Referrals::convert(RuntimeOrigin::signed(ALICE), DAI)); // Assert - let entry = Assets::::get(DAI); + let entry = PendingConversions::::get(DAI); assert_eq!(entry, None) }); } @@ -81,7 +81,7 @@ fn on_idle_should_convert_all_asset_amount_when_successful() { assert_eq!(balance, 0); let balance = Tokens::free_balance(HDX, &Pallet::::pot_account_id()); assert_eq!(balance, 1_000_000_000_000); - let entry = Assets::::get(DAI); + let entry = PendingConversions::::get(DAI); assert_eq!(entry, None) }); } diff --git a/pallets/referrals/src/tests/trade_fee.rs b/pallets/referrals/src/tests/trade_fee.rs index 4eef7ac99..f5b6dff17 100644 --- a/pallets/referrals/src/tests/trade_fee.rs +++ b/pallets/referrals/src/tests/trade_fee.rs @@ -158,7 +158,7 @@ fn process_trade_fee_should_add_asset_to_asset_list() { // Act assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), HDX, DAI, 1_000_000_000_000,)); // Assert - let asset = Assets::::get(DAI); + let asset = PendingConversions::::get(DAI); assert_eq!(asset, Some(())); }); } @@ -186,7 +186,7 @@ fn process_trade_fee_should_not_add_reward_asset_to_asset_list() { // Act assert_ok!(MockAmm::trade(RuntimeOrigin::signed(BOB), DAI, HDX, 1_000_000_000_000,)); // Assert - let asset = Assets::::get(HDX); + let asset = PendingConversions::::get(HDX); assert_eq!(asset, None); }); } From 206a1603b7c59aef95e76631f6d391911110cad8 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 20 Dec 2023 10:00:24 +0100 Subject: [PATCH 88/90] combine volume and rewards in config param --- pallets/referrals/src/benchmarking.rs | 2 +- pallets/referrals/src/lib.rs | 12 +++++------- pallets/referrals/src/tests.rs | 26 ++++++++++---------------- runtime/hydradx/src/assets.rs | 24 +++++++++--------------- 4 files changed, 25 insertions(+), 39 deletions(-) diff --git a/pallets/referrals/src/benchmarking.rs b/pallets/referrals/src/benchmarking.rs index e37f3ecae..2c943de73 100644 --- a/pallets/referrals/src/benchmarking.rs +++ b/pallets/referrals/src/benchmarking.rs @@ -80,7 +80,7 @@ benchmarks! { // The worst case is when referrer account is updated to the top tier in one call // So we need to have enough RewardAsset in the pot. And give all the shares to the caller. - let top_tier_volume = T::TierVolume::get(&Level::Tier4); + let top_tier_volume = T::LevelVolumeAndRewardPercentages::get(&Level::Tier4).0; T::Currency::mint_into(T::RewardAsset::get(), &Pallet::::pot_account_id(), top_tier_volume + T::SeedNativeAmount::get())?; Shares::::insert(caller.clone(), 1_000_000_000_000); TotalShares::::put(1_000_000_000_000); diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 47b60710b..13e13dcb1 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -111,7 +111,7 @@ impl Level { self } else { let next_level = self.next_level(); - let required = T::TierVolume::get(&next_level); + let required = T::LevelVolumeAndRewardPercentages::get(&next_level).0; if amount >= required { return next_level.increase::(amount); } @@ -194,11 +194,8 @@ pub mod pallet { #[pallet::constant] type CodeLength: Get; - /// Volume needed to reach given level. - type TierVolume: GetByKey; - - /// Global reward percentages for all assets if not specified explicitly for the asset. - type TierRewardPercentages: GetByKey; + /// Volume and Global reward percentages for all assets if not specified explicitly for the asset. + type LevelVolumeAndRewardPercentages: GetByKey; /// External account that receives some percentage of the fee. Usually something like staking. type ExternalAccount: Get>; @@ -612,7 +609,8 @@ impl Pallet { }; // What is asset fee for this level? if not explicitly set, use global parameter. - let tier = Self::asset_tier(asset_id, level).unwrap_or_else(|| T::TierRewardPercentages::get(&level)); + let tier = + Self::asset_tier(asset_id, level).unwrap_or_else(|| T::LevelVolumeAndRewardPercentages::get(&level).1); // Rewards let external_account = T::ExternalAccount::get(); diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index b990d58dc..380f33eec 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -100,28 +100,23 @@ parameter_types! { pub const RewardAsset: AssetId = HDX; } -pub struct Volume; +pub struct TierRewards; -impl GetByKey for Volume { - fn get(level: &Level) -> Balance { +impl GetByKey for TierRewards { + fn get(level: &Level) -> (Balance, Tier) { let c = TIER_VOLUME.with(|v| v.borrow().get(level).copied()); - if let Some(l) = c { + let volume = if let Some(l) = c { l.unwrap() } else { // if not explicitly set, we dont care about this in the test 0 - } - } -} - -pub struct TierRewards; - -impl GetByKey for TierRewards { - fn get(level: &Level) -> Tier { - TIER_REWARDS + }; + let rewards = TIER_REWARDS .with(|v| v.borrow().get(level).copied()) - .unwrap_or_default() + .unwrap_or_default(); + + (volume, rewards) } } @@ -152,8 +147,7 @@ impl Config for Test { type PalletId = RefarralPalletId; type RegistrationFee = RegistrationFee; type CodeLength = CodeLength; - type TierVolume = Volume; - type TierRewardPercentages = TierRewards; + type LevelVolumeAndRewardPercentages = TierRewards; type ExternalAccount = ExtAccount; type SeedNativeAmount = SeedAmount; type WeightInfo = (); diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 357a0ce6e..c4132aa31 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -1019,8 +1019,7 @@ impl pallet_referrals::Config for Runtime { type PalletId = ReferralsPalletId; type RegistrationFee = RegistrationFee; type CodeLength = MaxCodeLength; - type TierVolume = ReferralsLevelTiers; - type TierRewardPercentages = ReferralsTierRewards; + type LevelVolumeAndRewardPercentages = ReferralsLevelVolumeAndRewards; type ExternalAccount = ReferralsExternalRewardAccount; type SeedNativeAmount = ReferralsSeedAmount; type WeightInfo = weights::referrals::HydraWeight; @@ -1069,24 +1068,18 @@ where } } -pub struct ReferralsLevelTiers; +pub struct ReferralsLevelVolumeAndRewards; -impl GetByKey for ReferralsLevelTiers { - fn get(k: &Level) -> Balance { - match k { +impl GetByKey for ReferralsLevelVolumeAndRewards { + fn get(k: &Level) -> (Balance, Tier) { + let volume = match k { Level::Tier0 | Level::None => 0, Level::Tier1 => 1_222_222_000_000_000_000, Level::Tier2 => 12_222_220_000_000_000_000, Level::Tier3 => 122_222_200_000_000_000_000, Level::Tier4 => 1_222_222_000_000_000_000_000, - } - } -} - -pub struct ReferralsTierRewards; -impl GetByKey for ReferralsTierRewards { - fn get(k: &Level) -> Tier { - match k { + }; + let rewards = match k { Level::None => Tier { referrer: Permill::zero(), trader: Permill::zero(), @@ -1117,7 +1110,8 @@ impl GetByKey for ReferralsTierRewards { trader: Permill::from_percent(15), external: Permill::from_percent(10), }, - } + }; + (volume, rewards) } } From 91327a25081f72dfdcf200f6c7c6d4416634bd35 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 20 Dec 2023 10:21:09 +0100 Subject: [PATCH 89/90] renamed Tier --- integration-tests/src/polkadot_test_net.rs | 4 +- integration-tests/src/referrals.rs | 4 +- pallets/referrals/src/benchmarking.rs | 6 +- pallets/referrals/src/lib.rs | 70 +++++++++++++--------- pallets/referrals/src/tests.rs | 18 +++--- pallets/referrals/src/tests/flow.rs | 12 ++-- pallets/referrals/src/tests/tiers.rs | 16 ++--- pallets/referrals/src/tests/trade_fee.rs | 26 ++++---- runtime/hydradx/src/assets.rs | 18 +++--- 9 files changed, 94 insertions(+), 80 deletions(-) diff --git a/integration-tests/src/polkadot_test_net.rs b/integration-tests/src/polkadot_test_net.rs index dc7ba724b..20615999b 100644 --- a/integration-tests/src/polkadot_test_net.rs +++ b/integration-tests/src/polkadot_test_net.rs @@ -23,7 +23,7 @@ use hydradx_runtime::evm::WETH_ASSET_LOCATION; use hydradx_runtime::Referrals; use hydradx_runtime::RuntimeOrigin; use pallet_evm::AddressMapping; -use pallet_referrals::{Level, Tier}; +use pallet_referrals::{FeeDistribution, Level}; use polkadot_primitives::v2::{BlockNumber, MAX_CODE_SIZE, MAX_POV_SIZE}; use polkadot_runtime_parachains::configuration::HostConfiguration; use sp_core::H160; @@ -552,6 +552,6 @@ pub fn set_zero_reward_for_referrals(asset_id: AssetId) { RawOrigin::Root.into(), asset_id, Level::None, - Tier::default(), + FeeDistribution::default(), )); } diff --git a/integration-tests/src/referrals.rs b/integration-tests/src/referrals.rs index 0125e6ede..201e053f6 100644 --- a/integration-tests/src/referrals.rs +++ b/integration-tests/src/referrals.rs @@ -4,7 +4,7 @@ use frame_support::assert_ok; use frame_system::RawOrigin; use hydradx_runtime::{Currencies, Omnipool, Referrals, Runtime, RuntimeOrigin, Staking, Tokens}; use orml_traits::MultiCurrency; -use pallet_referrals::{ReferralCode, Tier}; +use pallet_referrals::{FeeDistribution, ReferralCode}; use primitives::AccountId; use sp_runtime::FixedU128; use sp_runtime::Permill; @@ -243,7 +243,7 @@ fn trading_in_omnipool_should_use_asset_rewards_when_set() { RuntimeOrigin::root(), DAI, pallet_referrals::Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_percent(2), trader: Permill::from_percent(1), external: Permill::from_percent(10), diff --git a/pallets/referrals/src/benchmarking.rs b/pallets/referrals/src/benchmarking.rs index 2c943de73..f76529a89 100644 --- a/pallets/referrals/src/benchmarking.rs +++ b/pallets/referrals/src/benchmarking.rs @@ -99,10 +99,10 @@ benchmarks! { let referrer_percentage = Permill::from_percent(40); let trader_percentage = Permill::from_percent(30); let external_percentage = Permill::from_percent(30); - }: _(RawOrigin::Root, T::RewardAsset::get(), Level::Tier2, Tier{referrer: referrer_percentage, trader: trader_percentage, external: external_percentage}) + }: _(RawOrigin::Root, T::RewardAsset::get(), Level::Tier2, FeeDistribution{referrer: referrer_percentage, trader: trader_percentage, external: external_percentage}) verify { - let entry = Pallet::::asset_tier(T::RewardAsset::get(), Level::Tier2); - assert_eq!(entry, Some(Tier{ + let entry = Pallet::::asset_rewards(T::RewardAsset::get(), Level::Tier2); + assert_eq!(entry, Some(FeeDistribution{ referrer: referrer_percentage, trader: trader_percentage, external: external_percentage, diff --git a/pallets/referrals/src/lib.rs b/pallets/referrals/src/lib.rs index 13e13dcb1..d81a3ad83 100644 --- a/pallets/referrals/src/lib.rs +++ b/pallets/referrals/src/lib.rs @@ -121,7 +121,7 @@ impl Level { } #[derive(Clone, Copy, Default, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub struct Tier { +pub struct FeeDistribution { /// Percentage of the fee that goes to the referrer. pub referrer: Permill, /// Percentage of the fee that goes back to the trader. @@ -162,7 +162,7 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Origin that can set asset tier reward percentages. + /// Origin that can set asset reward percentages. type AuthorityOrigin: EnsureOrigin; /// Asset type @@ -195,7 +195,7 @@ pub mod pallet { type CodeLength: Get; /// Volume and Global reward percentages for all assets if not specified explicitly for the asset. - type LevelVolumeAndRewardPercentages: GetByKey; + type LevelVolumeAndRewardPercentages: GetByKey; /// External account that receives some percentage of the fee. Usually something like staking. type ExternalAccount: Get>; @@ -241,18 +241,18 @@ pub mod pallet { pub(super) type TotalShares = StorageValue<_, Balance, ValueQuery>; /// Referer level and total accumulated rewards over time. - /// Maps referrer account to (Level, Balance). Level indicates current reward tier and Balance is used to unlock next tier level. + /// Maps referrer account to (Level, Balance). Level indicates current rewards and Balance is used to unlock next level. /// Dev note: we use OptionQuery here because this helps to easily determine that an account if referrer account. #[pallet::storage] #[pallet::getter(fn referrer_level)] pub(super) type Referrer = StorageMap<_, Blake2_128Concat, T::AccountId, (Level, Balance), OptionQuery>; - /// Asset tier information. - /// Maps (asset_id, level) to Tier which provides information about reward percentages. + /// Asset fee distribution rewards information. + /// Maps (asset_id, level) to asset reward percentages. #[pallet::storage] - #[pallet::getter(fn asset_tier)] - pub(super) type AssetTier = - StorageDoubleMap<_, Blake2_128Concat, T::AssetId, Blake2_128Concat, Level, Tier, OptionQuery>; + #[pallet::getter(fn asset_rewards)] + pub(super) type AssetRewards = + StorageDoubleMap<_, Blake2_128Concat, T::AssetId, Blake2_128Concat, Level, FeeDistribution, OptionQuery>; /// Information about assets that are currently in the rewards pot. /// Used to easily determine list of assets that need to be converted. @@ -281,12 +281,14 @@ pub mod pallet { }, /// Rewards claimed. Claimed { who: T::AccountId, rewards: Balance }, - /// New asset tier has been set. - TierRewardSet { + /// New asset rewards has been set. + AssetRewardsUpdated { asset_id: T::AssetId, level: Level, - tier: Tier, + rewards: FeeDistribution, }, + /// Referrer reached new level. + LevelUp { who: T::AccountId, level: Level }, } #[pallet::error] @@ -503,7 +505,14 @@ pub mod pallet { Referrer::::mutate(who.clone(), |v| { if let Some((level, total)) = v { *total = total.saturating_add(rewards); - *level = level.increase::(*total); + let new_level = level.increase::(*total); + if *level != new_level { + *level = new_level; + Self::deposit_event(Event::LevelUp { + who: who.clone(), + level: new_level, + }); + } } }); @@ -511,38 +520,43 @@ pub mod pallet { Ok(()) } - /// Set asset tier reward percentages + /// Set asset reward percentages /// /// Parameters: /// - `asset_id`: asset id /// - `level`: level - /// - `tier`: reward tier percentages + /// - `rewards`: reward fee percentages /// - /// Emits `TierRewardSet` event when successful. + /// Emits `AssetRewardsUpdated` event when successful. #[pallet::call_index(4)] #[pallet::weight(::WeightInfo::set_reward_percentage())] pub fn set_reward_percentage( origin: OriginFor, asset_id: T::AssetId, level: Level, - tier: Tier, + rewards: FeeDistribution, ) -> DispatchResult { T::AuthorityOrigin::ensure_origin(origin)?; //ensure that total percentage does not exceed 100% ensure!( - tier.referrer - .checked_add(&tier.trader) + rewards + .referrer + .checked_add(&rewards.trader) .ok_or(Error::::IncorrectRewardPercentage)? - .checked_add(&tier.external) + .checked_add(&rewards.external) .is_some(), Error::::IncorrectRewardPercentage ); - AssetTier::::mutate(asset_id, level, |v| { - *v = Some(tier); + AssetRewards::::mutate(asset_id, level, |v| { + *v = Some(rewards); + }); + Self::deposit_event(Event::AssetRewardsUpdated { + asset_id, + level, + rewards, }); - Self::deposit_event(Event::TierRewardSet { asset_id, level, tier }); Ok(()) } } @@ -609,19 +623,19 @@ impl Pallet { }; // What is asset fee for this level? if not explicitly set, use global parameter. - let tier = - Self::asset_tier(asset_id, level).unwrap_or_else(|| T::LevelVolumeAndRewardPercentages::get(&level).1); + let rewards = + Self::asset_rewards(asset_id, level).unwrap_or_else(|| T::LevelVolumeAndRewardPercentages::get(&level).1); // Rewards let external_account = T::ExternalAccount::get(); let referrer_reward = if ref_account.is_some() { - tier.referrer.mul_floor(amount) + rewards.referrer.mul_floor(amount) } else { 0 }; - let trader_reward = tier.trader.mul_floor(amount); + let trader_reward = rewards.trader.mul_floor(amount); let external_reward = if external_account.is_some() { - tier.external.mul_floor(amount) + rewards.external.mul_floor(amount) } else { 0 }; diff --git a/pallets/referrals/src/tests.rs b/pallets/referrals/src/tests.rs index 380f33eec..0f720f91f 100644 --- a/pallets/referrals/src/tests.rs +++ b/pallets/referrals/src/tests.rs @@ -75,7 +75,7 @@ pub(crate) const INITIAL_ALICE_BALANCE: Balance = 1_000 * ONE; thread_local! { pub static CONVERSION_RATE: RefCell> = RefCell::new(HashMap::default()); pub static TIER_VOLUME: RefCell>> = RefCell::new(HashMap::default()); - pub static TIER_REWARDS: RefCell> = RefCell::new(HashMap::default()); + pub static TIER_REWARDS: RefCell> = RefCell::new(HashMap::default()); pub static SEED_AMOUNT: RefCell = RefCell::new(Balance::zero()); pub static EXTERNAL_ACCOUNT: RefCell> = RefCell::new(None); } @@ -100,10 +100,10 @@ parameter_types! { pub const RewardAsset: AssetId = HDX; } -pub struct TierRewards; +pub struct LevelVolumeAndRewards; -impl GetByKey for TierRewards { - fn get(level: &Level) -> (Balance, Tier) { +impl GetByKey for LevelVolumeAndRewards { + fn get(level: &Level) -> (Balance, FeeDistribution) { let c = TIER_VOLUME.with(|v| v.borrow().get(level).copied()); let volume = if let Some(l) = c { @@ -147,7 +147,7 @@ impl Config for Test { type PalletId = RefarralPalletId; type RegistrationFee = RegistrationFee; type CodeLength = CodeLength; - type LevelVolumeAndRewardPercentages = TierRewards; + type LevelVolumeAndRewardPercentages = LevelVolumeAndRewards; type ExternalAccount = ExtAccount; type SeedNativeAmount = SeedAmount; type WeightInfo = (); @@ -212,7 +212,7 @@ impl mock_amm::pallet::Config for Test { pub struct ExtBuilder { endowed_accounts: Vec<(AccountId, AssetId, Balance)>, shares: Vec<(AccountId, Balance)>, - tiers: Vec<(AssetId, Level, Tier)>, + tiers: Vec<(AssetId, Level, FeeDistribution)>, assets: Vec, } @@ -260,7 +260,7 @@ impl ExtBuilder { self.assets.extend(shares); self } - pub fn with_tiers(mut self, shares: Vec<(AssetId, Level, Tier)>) -> Self { + pub fn with_tiers(mut self, shares: Vec<(AssetId, Level, FeeDistribution)>) -> Self { self.tiers.extend(shares); self } @@ -287,7 +287,7 @@ impl ExtBuilder { self } - pub fn with_global_tier_rewards(self, rewards: HashMap) -> Self { + pub fn with_global_tier_rewards(self, rewards: HashMap) -> Self { TIER_REWARDS.with(|v| { v.swap(&RefCell::new(rewards)); }); @@ -342,7 +342,7 @@ impl ExtBuilder { r.execute_with(|| { for (asset, level, tier) in self.tiers.iter() { - AssetTier::::insert(asset, level, tier); + AssetRewards::::insert(asset, level, tier); } }); r.execute_with(|| { diff --git a/pallets/referrals/src/tests/flow.rs b/pallets/referrals/src/tests/flow.rs index 4d6a1fc9a..14f782efb 100644 --- a/pallets/referrals/src/tests/flow.rs +++ b/pallets/referrals/src/tests/flow.rs @@ -24,7 +24,7 @@ fn complete_referral_flow_should_work_as_expected() { ( DAI, Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_float(0.005), trader: Permill::from_float(0.002), external: Permill::from_float(0.002), @@ -33,7 +33,7 @@ fn complete_referral_flow_should_work_as_expected() { ( DOT, Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_float(0.005), trader: Permill::from_float(0.002), external: Permill::from_float(0.002), @@ -42,7 +42,7 @@ fn complete_referral_flow_should_work_as_expected() { ( DAI, Level::Tier1, - Tier { + FeeDistribution { referrer: Permill::from_float(0.03), trader: Permill::from_float(0.01), external: Permill::from_float(0.002), @@ -51,7 +51,7 @@ fn complete_referral_flow_should_work_as_expected() { ( DOT, Level::Tier1, - Tier { + FeeDistribution { referrer: Permill::from_float(0.03), trader: Permill::from_float(0.01), external: Permill::from_float(0.002), @@ -60,7 +60,7 @@ fn complete_referral_flow_should_work_as_expected() { ( HDX, Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_float(0.002), trader: Permill::from_float(0.001), external: Permill::from_float(0.002), @@ -69,7 +69,7 @@ fn complete_referral_flow_should_work_as_expected() { ( HDX, Level::Tier1, - Tier { + FeeDistribution { referrer: Permill::from_float(0.03), trader: Permill::from_float(0.01), external: Permill::from_float(0.002), diff --git a/pallets/referrals/src/tests/tiers.rs b/pallets/referrals/src/tests/tiers.rs index c219c43ef..3c2ba5e00 100644 --- a/pallets/referrals/src/tests/tiers.rs +++ b/pallets/referrals/src/tests/tiers.rs @@ -10,7 +10,7 @@ fn setting_asset_tier_should_fail_when_not_correct_origin() { RuntimeOrigin::signed(BOB), DAI, Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_percent(1), trader: Permill::from_percent(2), external: Permill::from_percent(2), @@ -28,16 +28,16 @@ fn setting_asset_tier_should_correctly_update_storage() { RuntimeOrigin::root(), DAI, Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_percent(1), trader: Permill::from_percent(2), external: Permill::from_percent(3), } )); - let d = AssetTier::::get(DAI, Level::Tier0); + let d = AssetRewards::::get(DAI, Level::Tier0); assert_eq!( d, - Some(Tier { + Some(FeeDistribution { referrer: Permill::from_percent(1), trader: Permill::from_percent(2), external: Permill::from_percent(3), @@ -54,7 +54,7 @@ fn setting_asset_tier_should_fail_when_total_percentage_exceeds_hundred_percent( RuntimeOrigin::root(), DAI, Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_percent(60), trader: Permill::from_percent(40), external: Permill::from_percent(10), @@ -72,16 +72,16 @@ fn setting_asset_tier_should_emit_event() { RuntimeOrigin::root(), DAI, Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_percent(1), trader: Permill::from_percent(2), external: Permill::from_percent(3), } )); - expect_events(vec![Event::TierRewardSet { + expect_events(vec![Event::AssetRewardsUpdated { asset_id: DAI, level: Level::Tier0, - tier: Tier { + rewards: FeeDistribution { referrer: Permill::from_percent(1), trader: Permill::from_percent(2), external: Permill::from_percent(3), diff --git a/pallets/referrals/src/tests/trade_fee.rs b/pallets/referrals/src/tests/trade_fee.rs index f5b6dff17..941b6baf2 100644 --- a/pallets/referrals/src/tests/trade_fee.rs +++ b/pallets/referrals/src/tests/trade_fee.rs @@ -9,7 +9,7 @@ fn process_trade_fee_should_increased_referrer_shares() { .with_tiers(vec![( DAI, Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_percent(50), trader: Permill::zero(), external: Permill::zero(), @@ -37,7 +37,7 @@ fn process_trade_fee_should_increased_trader_shares() { .with_tiers(vec![( DAI, Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_percent(50), trader: Permill::from_percent(20), external: Permill::zero(), @@ -65,7 +65,7 @@ fn process_trade_fee_should_increased_total_share_issuance() { .with_tiers(vec![( DAI, Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_percent(50), trader: Permill::from_percent(20), external: Permill::zero(), @@ -93,7 +93,7 @@ fn process_trade_fee_should_fail_when_taken_amount_is_greater_than_fee_amount() .with_tiers(vec![( DAI, Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_percent(50), trader: Permill::from_percent(70), external: Permill::zero(), @@ -143,7 +143,7 @@ fn process_trade_fee_should_add_asset_to_asset_list() { .with_tiers(vec![( DAI, Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_percent(50), trader: Permill::from_percent(20), external: Permill::zero(), @@ -171,7 +171,7 @@ fn process_trade_fee_should_not_add_reward_asset_to_asset_list() { .with_tiers(vec![( DAI, Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_percent(50), trader: Permill::from_percent(20), external: Permill::zero(), @@ -196,7 +196,7 @@ fn process_trade_fee_should_increase_external_account_shares_when_trader_has_no_ let mut none_rewards = HashMap::new(); none_rewards.insert( Level::None, - Tier { + FeeDistribution { referrer: Default::default(), trader: Default::default(), external: Permill::from_percent(50), @@ -225,7 +225,7 @@ fn process_trade_fee_should_transfer_fee_to_pot_when_no_code_linked() { let mut none_rewards = HashMap::new(); none_rewards.insert( Level::None, - Tier { + FeeDistribution { referrer: Default::default(), trader: Default::default(), external: Permill::from_percent(50), @@ -252,7 +252,7 @@ fn process_trade_fee_should_reward_all_parties_based_on_global_config_when_asset let mut global_rewards = HashMap::new(); global_rewards.insert( Level::None, - Tier { + FeeDistribution { referrer: Default::default(), trader: Default::default(), external: Permill::from_percent(50), @@ -260,7 +260,7 @@ fn process_trade_fee_should_reward_all_parties_based_on_global_config_when_asset ); global_rewards.insert( Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_percent(5), trader: Permill::from_percent(5), external: Permill::from_percent(40), @@ -301,7 +301,7 @@ fn process_trade_fee_should_use_configured_asset_instead_of_global_when_set() { let mut global_rewards = HashMap::new(); global_rewards.insert( Level::None, - Tier { + FeeDistribution { referrer: Default::default(), trader: Default::default(), external: Permill::from_percent(50), @@ -309,7 +309,7 @@ fn process_trade_fee_should_use_configured_asset_instead_of_global_when_set() { ); global_rewards.insert( Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_percent(5), trader: Permill::from_percent(5), external: Permill::from_percent(40), @@ -321,7 +321,7 @@ fn process_trade_fee_should_use_configured_asset_instead_of_global_when_set() { .with_tiers(vec![( DAI, Level::Tier0, - Tier { + FeeDistribution { referrer: Permill::from_percent(10), trader: Permill::from_percent(5), external: Permill::from_percent(30), diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index c4132aa31..0dc366fb0 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -805,7 +805,7 @@ use hydradx_adapters::price::OraclePriceProviderUsingRoute; #[cfg(feature = "runtime-benchmarks")] use hydradx_traits::price::PriceProvider; use pallet_referrals::traits::Convert; -use pallet_referrals::{Level, Tier}; +use pallet_referrals::{FeeDistribution, Level}; #[cfg(feature = "runtime-benchmarks")] use pallet_stableswap::BenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] @@ -1070,8 +1070,8 @@ where pub struct ReferralsLevelVolumeAndRewards; -impl GetByKey for ReferralsLevelVolumeAndRewards { - fn get(k: &Level) -> (Balance, Tier) { +impl GetByKey for ReferralsLevelVolumeAndRewards { + fn get(k: &Level) -> (Balance, FeeDistribution) { let volume = match k { Level::Tier0 | Level::None => 0, Level::Tier1 => 1_222_222_000_000_000_000, @@ -1080,32 +1080,32 @@ impl GetByKey for ReferralsLevelVolumeAndRewards { Level::Tier4 => 1_222_222_000_000_000_000_000, }; let rewards = match k { - Level::None => Tier { + Level::None => FeeDistribution { referrer: Permill::zero(), trader: Permill::zero(), external: Permill::from_percent(50), }, - Level::Tier0 => Tier { + Level::Tier0 => FeeDistribution { referrer: Permill::from_percent(5), trader: Permill::from_percent(10), external: Permill::from_percent(35), }, - Level::Tier1 => Tier { + Level::Tier1 => FeeDistribution { referrer: Permill::from_percent(10), trader: Permill::from_percent(11), external: Permill::from_percent(29), }, - Level::Tier2 => Tier { + Level::Tier2 => FeeDistribution { referrer: Permill::from_percent(15), trader: Permill::from_percent(12), external: Permill::from_percent(23), }, - Level::Tier3 => Tier { + Level::Tier3 => FeeDistribution { referrer: Permill::from_percent(20), trader: Permill::from_percent(13), external: Permill::from_percent(17), }, - Level::Tier4 => Tier { + Level::Tier4 => FeeDistribution { referrer: Permill::from_percent(25), trader: Permill::from_percent(15), external: Permill::from_percent(10), From 55ce109c8c8917cc07b874f994ffeddd9c9d22e3 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Thu, 21 Dec 2023 10:59:35 +0100 Subject: [PATCH 90/90] treshold adjustment --- Cargo.lock | 4 +- integration-tests/src/referrals.rs | 30 +++++++------- integration-tests/src/router.rs | 60 +++++++++++++-------------- runtime/adapters/Cargo.toml | 2 +- runtime/adapters/src/lib.rs | 24 +++++++---- runtime/hydradx/Cargo.toml | 2 +- runtime/hydradx/src/assets.rs | 66 +++++++++++++++++------------- runtime/hydradx/src/lib.rs | 2 +- 8 files changed, 100 insertions(+), 90 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 92df311ea..974c2f8fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4176,7 +4176,7 @@ dependencies = [ [[package]] name = "hydradx-adapters" -version = "0.6.8" +version = "0.6.9" dependencies = [ "cumulus-pallet-parachain-system", "cumulus-primitives-core", @@ -4223,7 +4223,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "195.0.0" +version = "196.0.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", diff --git a/integration-tests/src/referrals.rs b/integration-tests/src/referrals.rs index 201e053f6..d74cc32f3 100644 --- a/integration-tests/src/referrals.rs +++ b/integration-tests/src/referrals.rs @@ -43,7 +43,7 @@ fn trading_in_omnipool_should_transfer_portion_of_fee_to_reward_pot() { 0 )); let pot_balance = Currencies::free_balance(DAI, &Referrals::pot_account_id()); - assert_eq!(pot_balance, 28_540_796_091_592_978); + assert_eq!(pot_balance, 28_540_796_051_302_768); }); } @@ -66,7 +66,7 @@ fn trading_in_omnipool_should_increase_referrer_shares() { 0 )); let referrer_shares = Referrals::account_shares::(ALICE.into()); - assert_eq!(referrer_shares, 128_499_434); + assert_eq!(referrer_shares, 128_499_283); }); } #[test] @@ -88,7 +88,7 @@ fn trading_in_omnipool_should_increase_trader_shares() { 0 )); let trader_shares = Referrals::account_shares::(BOB.into()); - assert_eq!(trader_shares, 256_998_869); + assert_eq!(trader_shares, 256_998_567); }); } #[test] @@ -110,7 +110,7 @@ fn trading_in_omnipool_should_increase_external_shares() { 0 )); let external_shares = Referrals::account_shares::(Staking::pot_account_id().into()); - assert_eq!(external_shares, 2_164_560_909_660); + assert_eq!(external_shares, 1_067_610_243_609); }); } @@ -133,7 +133,7 @@ fn trading_in_omnipool_should_increase_total_shares_correctly() { 0 )); let total_shares = Referrals::total_shares(); - assert_eq!(total_shares, 256_998_869 + 128_499_434 + 2_164_560_909_660); + assert_eq!(total_shares, 1_067_995_741_459); }); } @@ -165,7 +165,7 @@ fn claiming_rewards_should_convert_all_assets_to_reward_asset() { } #[test] -fn trading_hdx_in_omnipool_should_work_when_fee_is_below_existential_deposit() { +fn trading_hdx_in_omnipool_should_skip_referrals_program() { Hydra::execute_with(|| { init_omnipool_with_oracle_for_block_10(); let code = @@ -183,7 +183,7 @@ fn trading_hdx_in_omnipool_should_work_when_fee_is_below_existential_deposit() { 0 )); let referrer_shares = Referrals::account_shares::(BOB.into()); - assert_eq!(referrer_shares, 98_704_716_390); + assert_eq!(referrer_shares, 0); }); } @@ -199,7 +199,7 @@ fn trading_in_omnipool_should_transfer_some_portion_of_fee_when_no_code_linked() 0 )); let pot_balance = Currencies::free_balance(DAI, &Referrals::pot_account_id()); - assert_eq!(pot_balance, 28_540_796_091_592_980); + assert_eq!(pot_balance, 28_540_796_051_302_770); let external_shares = Referrals::account_shares::(Staking::pot_account_id()); let total_shares = Referrals::total_shares(); assert_eq!(total_shares, external_shares); @@ -225,11 +225,11 @@ fn trading_in_omnipool_should_use_global_rewards_when_not_set() { 0 )); let referrer_shares = Referrals::account_shares::(ALICE.into()); - assert_eq!(referrer_shares, 128_499_434); + assert_eq!(referrer_shares, 128_499_283); let trader_shares = Referrals::account_shares::(BOB.into()); - assert_eq!(trader_shares, 256_998_869); + assert_eq!(trader_shares, 256_998_567); let external_shares = Referrals::account_shares::(Staking::pot_account_id()); - assert_eq!(external_shares, 2_164_560_909_660); + assert_eq!(external_shares, 1_067_610_243_609); let total_shares = Referrals::total_shares(); assert_eq!(total_shares, referrer_shares + trader_shares + external_shares); }); @@ -264,11 +264,11 @@ fn trading_in_omnipool_should_use_asset_rewards_when_set() { 0 )); let referrer_shares = Referrals::account_shares::(ALICE.into()); - assert_eq!(referrer_shares, 51_399_773); + assert_eq!(referrer_shares, 51_399_713); let trader_shares = Referrals::account_shares::(BOB.into()); - assert_eq!(trader_shares, 25_699_886); + assert_eq!(trader_shares, 25_699_856); let external_shares = Referrals::account_shares::(Staking::pot_account_id()); - assert_eq!(external_shares, 2_163_918_412_488); + assert_eq!(external_shares, 1_066_967_747_190); let total_shares = Referrals::total_shares(); assert_eq!(total_shares, referrer_shares + trader_shares + external_shares); }); @@ -287,7 +287,7 @@ fn trading_in_omnipool_should_increase_staking_shares_when_no_code_linked() { )); let staking_acc = Staking::pot_account_id(); let staking_shares = Referrals::account_shares::(staking_acc.into()); - assert_eq!(staking_shares, 2_164_946_407_964); + assert_eq!(staking_shares, 1_067_995_741_461); let total_shares = Referrals::total_shares(); assert_eq!(total_shares, staking_shares); }); diff --git a/integration-tests/src/router.rs b/integration-tests/src/router.rs index 97fc4c94a..a71c55049 100644 --- a/integration-tests/src/router.rs +++ b/integration-tests/src/router.rs @@ -625,23 +625,21 @@ mod router_different_pools_tests { assert_eq!( RouterWeightInfo::sell_weight(trades.as_slice()), hydradx_runtime::weights::omnipool::HydraWeight::::router_execution_sell(1, 1) - .checked_add( - &, Runtime> as OmnipoolHooks::< - RuntimeOrigin, - AccountId, - AssetId, - Balance, - >>::on_trade_weight() - ) + .checked_add(&, + ConstU32, + Runtime, + > as OmnipoolHooks::>::on_trade_weight( + )) .unwrap() - .checked_add( - &, Runtime> as OmnipoolHooks::< - RuntimeOrigin, - AccountId, - AssetId, - Balance, - >>::on_liquidity_changed_weight() - ) + .checked_add(&, + ConstU32, + Runtime, + > as OmnipoolHooks::>::on_liquidity_changed_weight( + )) .unwrap() .checked_add(&hydradx_runtime::weights::lbp::HydraWeight::::router_execution_sell(1, 1)) .unwrap() @@ -655,23 +653,21 @@ mod router_different_pools_tests { assert_eq!( RouterWeightInfo::buy_weight(trades.as_slice()), hydradx_runtime::weights::omnipool::HydraWeight::::router_execution_buy(1, 1) - .checked_add( - &, Runtime> as OmnipoolHooks::< - RuntimeOrigin, - AccountId, - AssetId, - Balance, - >>::on_trade_weight() - ) + .checked_add(&, + ConstU32, + Runtime, + > as OmnipoolHooks::>::on_trade_weight( + )) .unwrap() - .checked_add( - &, Runtime> as OmnipoolHooks::< - RuntimeOrigin, - AccountId, - AssetId, - Balance, - >>::on_liquidity_changed_weight() - ) + .checked_add(&, + ConstU32, + Runtime, + > as OmnipoolHooks::>::on_liquidity_changed_weight( + )) .unwrap() .checked_add(&hydradx_runtime::weights::lbp::HydraWeight::::router_execution_buy(1, 1)) .unwrap() diff --git a/runtime/adapters/Cargo.toml b/runtime/adapters/Cargo.toml index 8920207d1..dd6811326 100644 --- a/runtime/adapters/Cargo.toml +++ b/runtime/adapters/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-adapters" -version = "0.6.8" +version = "0.6.9" description = "Structs and other generic types for building runtimes." authors = ["GalacticCouncil"] edition = "2021" diff --git a/runtime/adapters/src/lib.rs b/runtime/adapters/src/lib.rs index 7ac290d49..31e8a10df 100644 --- a/runtime/adapters/src/lib.rs +++ b/runtime/adapters/src/lib.rs @@ -324,12 +324,13 @@ where } /// Passes on trade and liquidity data from the omnipool to the oracle. -pub struct OmnipoolHookAdapter(PhantomData<(Origin, Lrna, Runtime)>); +pub struct OmnipoolHookAdapter(PhantomData<(Origin, NativeAsset, Lrna, Runtime)>); -impl OmnipoolHooks - for OmnipoolHookAdapter +impl OmnipoolHooks + for OmnipoolHookAdapter where Lrna: Get, + NativeAsset: Get, Runtime: pallet_ema_oracle::Config + pallet_circuit_breaker::Config + frame_system::Config @@ -470,12 +471,17 @@ where asset: AssetId, amount: Balance, ) -> Result { - let referrals_used = pallet_referrals::Pallet::::process_trade_fee( - fee_account.clone().into(), - trader.into(), - asset.into(), - amount, - )?; + let referrals_used = if asset == NativeAsset::get() { + Balance::zero() + } else { + pallet_referrals::Pallet::::process_trade_fee( + fee_account.clone().into(), + trader.into(), + asset.into(), + amount, + )? + }; + let staking_used = pallet_staking::Pallet::::process_trade_fee( fee_account.into(), asset.into(), diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index e0c5d1f32..9adf22497 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "195.0.0" +version = "196.0.0" authors = ["GalacticCouncil"] edition = "2021" license = "Apache 2.0" diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 336a0cd19..861fba964 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -248,7 +248,7 @@ impl pallet_omnipool::Config for Runtime { type NFTCollectionId = OmnipoolCollectionId; type NFTHandler = Uniques; type WeightInfo = weights::omnipool::HydraWeight; - type OmnipoolHooks = OmnipoolHookAdapter; + type OmnipoolHooks = OmnipoolHookAdapter; type PriceBarrier = ( EnsurePriceWithin< AccountId, @@ -573,18 +573,22 @@ impl AmmTradeWeights> for RouterWeightInfo { let amm_weight = match trade.pool { PoolType::Omnipool => weights::omnipool::HydraWeight::::router_execution_sell(c, e) - .saturating_add( as OmnipoolHooks< - RuntimeOrigin, - AccountId, - AssetId, - Balance, - >>::on_trade_weight()) - .saturating_add( as OmnipoolHooks< - RuntimeOrigin, - AccountId, - AssetId, - Balance, - >>::on_liquidity_changed_weight()), + .saturating_add( + as OmnipoolHooks< + RuntimeOrigin, + AccountId, + AssetId, + Balance, + >>::on_trade_weight(), + ) + .saturating_add( + as OmnipoolHooks< + RuntimeOrigin, + AccountId, + AssetId, + Balance, + >>::on_liquidity_changed_weight(), + ), PoolType::LBP => weights::lbp::HydraWeight::::router_execution_sell(c, e), PoolType::Stableswap(_) => weights::stableswap::HydraWeight::::router_execution_sell(c, e), PoolType::XYK => weights::xyk::HydraWeight::::router_execution_sell(c, e) @@ -607,18 +611,22 @@ impl AmmTradeWeights> for RouterWeightInfo { let amm_weight = match trade.pool { PoolType::Omnipool => weights::omnipool::HydraWeight::::router_execution_buy(c, e) - .saturating_add( as OmnipoolHooks< - RuntimeOrigin, - AccountId, - AssetId, - Balance, - >>::on_trade_weight()) - .saturating_add( as OmnipoolHooks< - RuntimeOrigin, - AccountId, - AssetId, - Balance, - >>::on_liquidity_changed_weight()), + .saturating_add( + as OmnipoolHooks< + RuntimeOrigin, + AccountId, + AssetId, + Balance, + >>::on_trade_weight(), + ) + .saturating_add( + as OmnipoolHooks< + RuntimeOrigin, + AccountId, + AssetId, + Balance, + >>::on_liquidity_changed_weight(), + ), PoolType::LBP => weights::lbp::HydraWeight::::router_execution_buy(c, e), PoolType::Stableswap(_) => weights::stableswap::HydraWeight::::router_execution_buy(c, e), PoolType::XYK => weights::xyk::HydraWeight::::router_execution_buy(c, e) @@ -1093,10 +1101,10 @@ impl GetByKey for ReferralsLevelVolumeAndRewa fn get(k: &Level) -> (Balance, FeeDistribution) { let volume = match k { Level::Tier0 | Level::None => 0, - Level::Tier1 => 1_222_222_000_000_000_000, - Level::Tier2 => 12_222_220_000_000_000_000, - Level::Tier3 => 122_222_200_000_000_000_000, - Level::Tier4 => 1_222_222_000_000_000_000_000, + Level::Tier1 => 305 * UNITS, + Level::Tier2 => 4_583 * UNITS, + Level::Tier3 => 61_111 * UNITS, + Level::Tier4 => 763_888 * UNITS, }; let rewards = match k { Level::None => FeeDistribution { diff --git a/runtime/hydradx/src/lib.rs b/runtime/hydradx/src/lib.rs index 24b27431a..798830fba 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -99,7 +99,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("hydradx"), impl_name: create_runtime_str!("hydradx"), authoring_version: 1, - spec_version: 195, + spec_version: 196, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1,