diff --git a/Cargo.lock b/Cargo.lock index 638e3049a..3a487abce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4482,7 +4482,7 @@ dependencies = [ [[package]] name = "hydradx" -version = "12.1.0" +version = "12.2.0" dependencies = [ "async-trait", "clap 4.4.11", @@ -4571,7 +4571,7 @@ dependencies = [ [[package]] name = "hydradx-adapters" -version = "1.2.5" +version = "1.3.0" dependencies = [ "cumulus-pallet-parachain-system", "cumulus-primitives-core", @@ -4621,7 +4621,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "225.0.0" +version = "226.0.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", @@ -4716,6 +4716,7 @@ dependencies = [ "pallet-xcm", "pallet-xcm-rate-limiter", "pallet-xyk", + "pallet-xyk-liquidity-mining", "parachain-info", "parity-scale-codec", "polkadot-parachain-primitives", @@ -4747,7 +4748,7 @@ dependencies = [ [[package]] name = "hydradx-traits" -version = "3.1.1" +version = "3.2.0" dependencies = [ "frame-support", "impl-trait-for-tuples", @@ -6985,7 +6986,7 @@ checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" [[package]] name = "pallet-asset-registry" -version = "3.1.1" +version = "3.2.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -7199,7 +7200,7 @@ dependencies = [ [[package]] name = "pallet-bonds" -version = "2.2.0" +version = "2.2.1" dependencies = [ "frame-benchmarking", "frame-support", @@ -7260,7 +7261,7 @@ dependencies = [ [[package]] name = "pallet-circuit-breaker" -version = "1.1.20" +version = "1.1.21" dependencies = [ "frame-benchmarking", "frame-support", @@ -7403,7 +7404,7 @@ dependencies = [ [[package]] name = "pallet-dca" -version = "1.4.1" +version = "1.4.2" dependencies = [ "cumulus-pallet-parachain-system", "cumulus-primitives-core", @@ -7903,7 +7904,7 @@ dependencies = [ [[package]] name = "pallet-liquidity-mining" -version = "4.3.0" +version = "4.3.1" dependencies = [ "fixed", "frame-support", @@ -8125,7 +8126,7 @@ dependencies = [ [[package]] name = "pallet-omnipool" -version = "4.1.4" +version = "4.1.5" dependencies = [ "bitflags 1.3.2", "frame-benchmarking", @@ -8152,7 +8153,7 @@ dependencies = [ [[package]] name = "pallet-omnipool-liquidity-mining" -version = "2.1.2" +version = "2.1.3" dependencies = [ "frame-benchmarking", "frame-support", @@ -8179,7 +8180,7 @@ dependencies = [ [[package]] name = "pallet-otc" -version = "1.1.0" +version = "1.1.1" dependencies = [ "frame-benchmarking", "frame-support", @@ -8321,7 +8322,7 @@ dependencies = [ [[package]] name = "pallet-route-executor" -version = "2.2.0" +version = "2.2.1" dependencies = [ "frame-benchmarking", "frame-support", @@ -8420,7 +8421,7 @@ dependencies = [ [[package]] name = "pallet-stableswap" -version = "3.5.0" +version = "3.5.1" dependencies = [ "bitflags 1.3.2", "frame-benchmarking", @@ -8800,7 +8801,7 @@ dependencies = [ [[package]] name = "pallet-xcm-rate-limiter" -version = "0.1.5" +version = "0.1.6" dependencies = [ "cumulus-pallet-xcmp-queue", "frame-benchmarking", @@ -8827,7 +8828,7 @@ dependencies = [ [[package]] name = "pallet-xyk" -version = "6.4.0" +version = "6.4.1" dependencies = [ "frame-benchmarking", "frame-support", @@ -8851,6 +8852,30 @@ dependencies = [ "substrate-wasm-builder", ] +[[package]] +name = "pallet-xyk-liquidity-mining" +version = "1.1.10" +dependencies = [ + "frame-support", + "frame-system", + "hydradx-traits", + "log", + "orml-tokens", + "orml-traits", + "pallet-balances", + "pallet-liquidity-mining", + "pallet-xyk", + "parity-scale-codec", + "pretty_assertions", + "primitives", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "parachain-info" version = "0.1.0" @@ -11366,7 +11391,7 @@ dependencies = [ [[package]] name = "runtime-integration-tests" -version = "1.19.12" +version = "1.20.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", @@ -11439,6 +11464,7 @@ dependencies = [ "pallet-utility", "pallet-xcm", "pallet-xyk", + "pallet-xyk-liquidity-mining", "parachain-info", "polkadot-parachain-primitives", "polkadot-primitives", diff --git a/Cargo.toml b/Cargo.toml index 75d1c7174..96f425794 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,8 @@ members = [ 'runtime/hydradx/src/evm/evm-utility/macro', 'pallets/referrals', 'pallets/evm-accounts', - 'pallets/dynamic-evm-fee' + 'pallets/dynamic-evm-fee', + 'pallets/xyk-liquidity-mining', ] [workspace.dependencies] @@ -86,6 +87,7 @@ pallet-xyk = { path = "pallets/xyk", default-features = false} pallet-referrals = { path = "pallets/referrals", default-features = false} pallet-evm-accounts = { path = "pallets/evm-accounts", default-features = false} pallet-evm-accounts-rpc-runtime-api = { path = "pallets/evm-accounts/rpc/runtime-api", default-features = false} +pallet-xyk-liquidity-mining = { path = "pallets/xyk-liquidity-mining", default-features = false } hydra-dx-build-script-utils = { path = "utils/build-script-utils", default-features = false } scraper = { path = "scraper", default-features = false } @@ -103,7 +105,7 @@ frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", rev = frame-benchmarking-cli = { git = "https://github.com/paritytech/polkadot-sdk", rev = "c8d2251cafadc108ba2f1f8a3208dc547ff38901", default-features = false } frame-executive = { git = "https://github.com/paritytech/polkadot-sdk", rev = "c8d2251cafadc108ba2f1f8a3208dc547ff38901", default-features = false } frame-remote-externalities = { git = "https://github.com/paritytech/polkadot-sdk", rev = "c8d2251cafadc108ba2f1f8a3208dc547ff38901", default-features = false } -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", rev = "c8d2251cafadc108ba2f1f8a3208dc547ff38901", default-features = false } +frame-support = { git = "https://github.com/paritytech/polkadot-sdk", rev = "c8d2251cafadc108ba2f1f8a3208dc547ff38901", default-features = false, features = ["tuples-96"] } frame-system = { git = "https://github.com/paritytech/polkadot-sdk", rev = "c8d2251cafadc108ba2f1f8a3208dc547ff38901", default-features = false } frame-system-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", rev = "c8d2251cafadc108ba2f1f8a3208dc547ff38901", default-features = false } frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk", rev = "c8d2251cafadc108ba2f1f8a3208dc547ff38901", default-features = false } @@ -578,4 +580,4 @@ westend-runtime = { git = "https://github.com/galacticcouncil/polkadot-sdk", rev cumulus-client-pov-recovery = { git = "https://github.com/galacticcouncil/polkadot-sdk", rev = "062d92eae0f3bb9908faf2d4e241eef17368b9d3" } cumulus-pallet-parachain-system-proc-macro = { git = "https://github.com/galacticcouncil/polkadot-sdk", rev = "062d92eae0f3bb9908faf2d4e241eef17368b9d3" } -cumulus-relay-chain-rpc-interface = { git = "https://github.com/galacticcouncil/polkadot-sdk", rev = "062d92eae0f3bb9908faf2d4e241eef17368b9d3" } \ No newline at end of file +cumulus-relay-chain-rpc-interface = { git = "https://github.com/galacticcouncil/polkadot-sdk", rev = "062d92eae0f3bb9908faf2d4e241eef17368b9d3" } diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 01e988202..30341369a 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runtime-integration-tests" -version = "1.19.12" +version = "1.20.0" description = "Integration tests" authors = ["GalacticCouncil"] edition = "2021" @@ -45,6 +45,7 @@ pallet-democracy = { workspace = true } pallet-scheduler = { workspace = true } pallet-elections-phragmen = { workspace = true } pallet-tips = { workspace = true } +pallet-xyk-liquidity-mining = { workspace = true } # collator support pallet-collator-selection = { workspace = true } diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs index c5fe26fc2..ce227c87b 100644 --- a/integration-tests/src/lib.rs +++ b/integration-tests/src/lib.rs @@ -28,6 +28,7 @@ mod vesting; mod xcm_defer; mod xcm_rate_limiter; mod xyk; +mod xyk_liquidity_mining; #[macro_export] macro_rules! assert_balance { diff --git a/integration-tests/src/xyk_liquidity_mining.rs b/integration-tests/src/xyk_liquidity_mining.rs new file mode 100644 index 000000000..44bab84f8 --- /dev/null +++ b/integration-tests/src/xyk_liquidity_mining.rs @@ -0,0 +1,1074 @@ +// This file is part of HydraDX-node. + +// 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(test)] + +use crate::{assert_nft_owner, omnipool_init::hydra_run_to_block, polkadot_test_net::*}; + +use frame_support::{assert_noop, assert_ok}; +use hydradx_traits::{liquidity_mining::PriceAdjustment, AMM}; +use pallet_asset_registry::AssetType; +use warehouse_liquidity_mining::{ + DefaultPriceAdjustment, DepositData, GlobalFarmData, GlobalFarmId, Instance2, LoyaltyCurve, YieldFarmData, + YieldFarmEntry, +}; + +use orml_traits::MultiCurrency; +use sp_runtime::{ + traits::{One, Zero}, + FixedU128, Perquintill, +}; +use xcm_emulator::TestExt; + +use hydradx_runtime::{ + AssetRegistry, Balance, Bonds, Runtime, RuntimeOrigin, RuntimeOrigin as hydra_origin, XYKLiquidityMining, + XYKWarehouseLM, XYK, +}; +use pallet_xyk::types::AssetPair; +use polkadot_xcm::v3::{ + Junction::{GeneralIndex, Parachain}, + Junctions::X2, + MultiLocation, +}; +use pretty_assertions::assert_eq; +use primitives::constants::time::unix_time::MONTH; + +#[test] +fn create_global_farm_should_work_when_origin_is_root() { + TestNet::reset(); + + Hydra::execute_with(|| { + let total_rewards: Balance = 1_000_000 * UNITS; + let planned_yielding_periods: BlockNumber = 1_000_000; + let blocks_per_period: BlockNumber = 10; + let reward_currency = HDX; + let incentivized_asset = PEPE; + let owner = Treasury::account_id(); + let yield_per_period = Perquintill::from_parts(570_776_255_707); + let min_deposit = 1_000; + + assert_ok!(hydradx_runtime::Balances::force_set_balance( + hydradx_runtime::RuntimeOrigin::root(), + owner.clone(), + total_rewards, + )); + + set_relaychain_block_number(100); + + assert_ok!(XYKLiquidityMining::create_global_farm( + hydradx_runtime::RuntimeOrigin::root(), + total_rewards, + planned_yielding_periods, + blocks_per_period, + incentivized_asset, + reward_currency, + owner.clone(), + yield_per_period, + min_deposit, + FixedU128::from(2) + )); + + let farm_id = 1; + let updated_at = 100 / blocks_per_period; + assert_eq!( + XYKWarehouseLM::global_farm(1).unwrap(), + GlobalFarmData::new( + farm_id, + updated_at, + reward_currency, + yield_per_period, + planned_yielding_periods, + blocks_per_period, + owner, + incentivized_asset, + total_rewards / planned_yielding_periods as u128, + min_deposit, + FixedU128::from(2), + ) + ); + + let g_farm_account = XYKWarehouseLM::farm_account_id(farm_id).unwrap(); + assert_eq!(hydradx_runtime::Balances::free_balance(g_farm_account), total_rewards); + }); +} + +#[test] +fn create_yield_farm_should_work_when_xyk_exists() { + TestNet::reset(); + + Hydra::execute_with(|| { + let global_farm_id = 1; + let created_yield_farm_id = 2; + let loyalty_curve = Some(LoyaltyCurve::default()); + let multiplier = FixedU128::one(); + let asset_pair = AssetPair { + asset_in: PEPE, + asset_out: ACA, + }; + let amm_pool_id = ::AMM::get_pair_id(asset_pair); + + set_relaychain_block_number(100); + create_global_farm(None, PEPE, None); + + create_xyk_pool( + asset_pair.asset_in, + 1_000_000 * UNITS, + asset_pair.asset_out, + 10_000_000 * UNITS, + ); + + set_relaychain_block_number(200); + assert_ok!(XYKLiquidityMining::create_yield_farm( + RuntimeOrigin::signed(Treasury::account_id()), + global_farm_id, + asset_pair, + multiplier, + loyalty_curve.clone() + )); + + let updated_at = 20; + let y_farm = warehouse_liquidity_mining::YieldFarm::::get(( + amm_pool_id, + global_farm_id, + created_yield_farm_id, + )) + .unwrap(); + assert_eq!( + y_farm, + YieldFarmData::new(created_yield_farm_id, updated_at, loyalty_curve, multiplier) + ); + }); +} + +#[test] +fn deposit_shares_should_work_when_yield_farm_exists() { + TestNet::reset(); + + Hydra::execute_with(|| { + let global_farm_id = 1; + let yield_farm_id = 2; + let deposit_amount = 5_000_000 * UNITS; + let asset_pair = AssetPair { + asset_in: PEPE, + asset_out: ACA, + }; + let amm_pool_id = ::AMM::get_pair_id(asset_pair); + + //Arrange + let xyk_share_id = create_xyk_pool( + asset_pair.asset_in, + 10_000_000 * UNITS, + asset_pair.asset_out, + 100_000_000 * UNITS, + ); + let dave_shares_balance = Currencies::free_balance(xyk_share_id, &DAVE.into()); + + hydradx_run_to_block(100); + set_relaychain_block_number(100); + create_global_farm(None, PEPE, None); + create_yield_farm(global_farm_id, asset_pair, None); + set_relaychain_block_number(300); + + //Act + set_relaychain_block_number(400); + assert_ok!(XYKLiquidityMining::deposit_shares( + RuntimeOrigin::signed(DAVE.into()), + global_farm_id, + yield_farm_id, + asset_pair, + deposit_amount + )); + + //Assert + let deposit = XYKWarehouseLM::deposit(1).unwrap(); + let mut expected_deposit = DepositData::new(deposit_amount, amm_pool_id); + expected_deposit + .add_yield_farm_entry(YieldFarmEntry::new( + global_farm_id, + yield_farm_id, + 500_000 * UNITS, + FixedU128::zero(), + 40, + 0, + )) + .unwrap(); + + assert_eq!(deposit, expected_deposit); + + //assert LM deposit + assert_nft_owner!(hydradx_runtime::XYKLmCollectionId::get(), 1, DAVE.into()); + assert_eq!( + Currencies::free_balance(xyk_share_id, &DAVE.into()), + dave_shares_balance - deposit_amount + ); + assert_eq!( + Currencies::free_balance(xyk_share_id, &XYKLiquidityMining::account_id()), + deposit_amount + ); + }); +} + +#[test] +fn redeposit_shares_multiple_times_should_work_when_shares_already_deposited() { + TestNet::reset(); + + Hydra::execute_with(|| { + let global_farm_1_id = 1; + let global_farm_2_id = 2; + let yield_farm_1_id = 3; + let yield_farm_2_id = 4; + + let asset_pair = AssetPair { + asset_in: PEPE, + asset_out: ACA, + }; + let amm_pool_id = ::AMM::get_pair_id(asset_pair); + + //Arrange + let xyk_share_id = create_xyk_pool( + asset_pair.asset_in, + 10_000_000 * UNITS, + asset_pair.asset_out, + 100_000_000 * UNITS, + ); + let dave_shares_balance = Currencies::free_balance(xyk_share_id, &DAVE.into()); + + //NOTE: necessary to get oracle price. + hydradx_run_to_block(100); + set_relaychain_block_number(100); + create_global_farm(None, PEPE, None); + create_global_farm(None, ACA, None); + + set_relaychain_block_number(200); + create_yield_farm(global_farm_1_id, asset_pair, None); + create_yield_farm(global_farm_2_id, asset_pair, None); + + set_relaychain_block_number(400); + let deposit_id = 1; + assert_ok!(XYKLiquidityMining::deposit_shares( + RuntimeOrigin::signed(DAVE.into()), + global_farm_1_id, + yield_farm_1_id, + asset_pair, + dave_shares_balance, + )); + + //Act + set_relaychain_block_number(500); + assert_ok!(XYKLiquidityMining::redeposit_shares( + RuntimeOrigin::signed(DAVE.into()), + global_farm_2_id, + yield_farm_2_id, + asset_pair, + deposit_id, + )); + + let deposit = XYKWarehouseLM::deposit(deposit_id).unwrap(); + let mut expected_deposit = DepositData::new(dave_shares_balance, amm_pool_id); + //1-th deposit entry + expected_deposit + .add_yield_farm_entry(YieldFarmEntry::new( + global_farm_1_id, + yield_farm_1_id, + 10_000_000 * UNITS, + FixedU128::zero(), + 40, + 0, + )) + .unwrap(); + + //2-nd redeposit entry + expected_deposit + .add_yield_farm_entry(YieldFarmEntry::new( + global_farm_2_id, + yield_farm_2_id, + 100_000_000 * UNITS, + FixedU128::zero(), + 50, + 0, + )) + .unwrap(); + + assert_eq!(deposit, expected_deposit); + + //assert LM deposit + assert_nft_owner!(hydradx_runtime::XYKLmCollectionId::get(), 1, DAVE.into()); + assert_eq!(Currencies::free_balance(xyk_share_id, &DAVE.into()), Balance::zero()); + assert_eq!( + Currencies::free_balance(xyk_share_id, &XYKLiquidityMining::account_id()), + dave_shares_balance + ); + }); +} + +#[test] +fn claim_rewards_should_work_when_rewards_are_accumulated_for_deposit() { + TestNet::reset(); + + Hydra::execute_with(|| { + let global_farm_1_id = 1; + let global_farm_2_id = 2; + let yield_farm_1_id = 3; + let yield_farm_2_id = 4; + + let asset_pair = AssetPair { + asset_in: PEPE, + asset_out: ACA, + }; + + //Arrange + let xyk_share_id = create_xyk_pool( + asset_pair.asset_in, + 10_000_000 * UNITS, + asset_pair.asset_out, + 100_000_000 * UNITS, + ); + let dave_shares_balance = Currencies::free_balance(xyk_share_id, &DAVE.into()); + + set_relaychain_block_number(100); + create_global_farm(None, PEPE, None); + create_global_farm(None, ACA, None); + + set_relaychain_block_number(200); + create_yield_farm(global_farm_1_id, asset_pair, None); + create_yield_farm(global_farm_2_id, asset_pair, None); + + set_relaychain_block_number(400); + let deposit_id = 1; + assert_ok!(XYKLiquidityMining::deposit_shares( + RuntimeOrigin::signed(DAVE.into()), + global_farm_1_id, + yield_farm_1_id, + asset_pair, + dave_shares_balance, + )); + + set_relaychain_block_number(500); + assert_ok!(XYKLiquidityMining::redeposit_shares( + RuntimeOrigin::signed(DAVE.into()), + global_farm_2_id, + yield_farm_2_id, + asset_pair, + deposit_id + )); + + //Act 1 - claim rewards for 2-nd yield-farm-entry + set_relaychain_block_number(600); + assert_ok!(XYKLiquidityMining::claim_rewards( + RuntimeOrigin::signed(DAVE.into()), + deposit_id, + yield_farm_2_id + )); + + //Assert + //NOTE: can't assert state in the deposit because fields are private + assert_eq!( + hydradx_runtime::Currencies::free_balance(HDX, &DAVE.into()), + 1_004_254_545_454_545_u128 + ); + + //Act & assert 2 - claim rewards in the same period for same yield-farm-entry should not work. + assert_noop!( + XYKLiquidityMining::claim_rewards(RuntimeOrigin::signed(DAVE.into()), deposit_id, yield_farm_2_id), + warehouse_liquidity_mining::Error::::DoubleClaimInPeriod + ); + + //Act 3 - claim rewards for differnt yield-farm-entry in the same period should work. + assert_ok!(XYKLiquidityMining::claim_rewards( + RuntimeOrigin::signed(DAVE.into()), + deposit_id, + yield_farm_1_id + )); + + //Assert + //NOTE: can't assert state in the deposit because fields are private + assert_eq!( + hydradx_runtime::Currencies::free_balance(HDX, &DAVE.into()), + 1_015_921_212_121_211_u128 + ); + }); +} + +#[test] +fn withdraw_shares_should_work_when_deposit_exists() { + TestNet::reset(); + + Hydra::execute_with(|| { + let global_farm_1_id = 1; + let global_farm_2_id = 2; + let yield_farm_1_id = 3; + let yield_farm_2_id = 4; + let asset_pair = AssetPair { + asset_in: PEPE, + asset_out: ACA, + }; + + //Arrange + let xyk_share_id = create_xyk_pool( + asset_pair.asset_in, + 10_000_000 * UNITS, + asset_pair.asset_out, + 100_000_000 * UNITS, + ); + let dave_shares_balance = Currencies::free_balance(xyk_share_id, &DAVE.into()); + + set_relaychain_block_number(100); + create_global_farm(None, PEPE, None); + create_global_farm(None, ACA, None); + + set_relaychain_block_number(200); + create_yield_farm(global_farm_1_id, asset_pair, None); + create_yield_farm(global_farm_2_id, asset_pair, None); + + set_relaychain_block_number(400); + let deposit_id = 1; + assert_ok!(XYKLiquidityMining::deposit_shares( + RuntimeOrigin::signed(DAVE.into()), + global_farm_1_id, + yield_farm_1_id, + asset_pair, + dave_shares_balance, + )); + + set_relaychain_block_number(500); + assert_ok!(XYKLiquidityMining::redeposit_shares( + RuntimeOrigin::signed(DAVE.into()), + global_farm_2_id, + yield_farm_2_id, + asset_pair, + deposit_id, + )); + + assert!( + warehouse_liquidity_mining::Deposit::::get(deposit_id) + .unwrap() + .get_yield_farm_entry(yield_farm_2_id) + .is_some() + ); + + set_relaychain_block_number(600); + assert_ok!(XYKLiquidityMining::withdraw_shares( + RuntimeOrigin::signed(DAVE.into()), + deposit_id, + yield_farm_2_id, + asset_pair, + )); + + //Assert + //NOTE: withdraw is claiming rewards automatically + assert_eq!( + hydradx_runtime::Currencies::free_balance(HDX, &DAVE.into()), + 1_004_254_545_454_545_u128 + ); + + //NOTE: shares should not be unlocked because deposit wasn't destroyed(it has 1 + //yield-farm-entry left) + //assert LM deposit + assert_eq!(Currencies::free_balance(xyk_share_id, &DAVE.into()), Balance::zero()); + assert_eq!( + Currencies::free_balance(xyk_share_id, &XYKLiquidityMining::account_id()), + dave_shares_balance + ); + + //Check if yield-farm-entry was removed from the deposit. + assert!( + warehouse_liquidity_mining::Deposit::::get(deposit_id) + .unwrap() + .get_yield_farm_entry(yield_farm_2_id) + .is_none() + ); + + set_relaychain_block_number(700); + //Arrange - claim before withdraw + assert_ok!(XYKLiquidityMining::claim_rewards( + RuntimeOrigin::signed(DAVE.into()), + deposit_id, + yield_farm_1_id, + )); + + //Act 2 - claim and withdraw should in the same period should work. + assert_ok!(XYKLiquidityMining::withdraw_shares( + RuntimeOrigin::signed(DAVE.into()), + deposit_id, + yield_farm_1_id, + asset_pair, + )); + + //Assert + //NOTE: claim happened before withdraw in this period so no rewards should be claimed. + assert_eq!( + hydradx_runtime::Currencies::free_balance(HDX, &DAVE.into()), + 1_021_616_083_916_083_u128 + ); + + //NOTE: last shares were unlockend and deposit's nft should be destroyed and omnipool's + //position should be unlocked. + assert!(warehouse_liquidity_mining::Deposit::::get(deposit_id).is_none()); + //LM nft should be destroyed + assert!(hydradx_runtime::Uniques::owner(hydradx_runtime::XYKLmCollectionId::get(), deposit_id).is_none()); + //omnpool's position should be unlocekd + assert_eq!( + Currencies::free_balance(xyk_share_id, &DAVE.into()), + dave_shares_balance + ); + assert_eq!( + Currencies::free_balance(xyk_share_id, &XYKLiquidityMining::account_id()), + Balance::zero() + ); + }); +} + +#[test] +fn liquidity_mining_should_work_when_distributes_insufficient_asset() { + TestNet::reset(); + Hydra::execute_with(|| { + let ext1 = register_external_asset(0_u128); + let ext2 = register_external_asset(1_u128); + + let global_farm_1_id = 1; + let global_farm_2_id = 2; + let yield_farm_1_id = 3; + let yield_farm_2_id = 4; + let asset_pair = AssetPair { + asset_in: ext1, + asset_out: ext2, + }; + + //Arrange + let xyk_share_id = create_xyk_pool( + asset_pair.asset_in, + 10_000_000 * UNITS, + asset_pair.asset_out, + 100_000_000 * UNITS, + ); + let dave_shares_balance = Currencies::free_balance(xyk_share_id, &DAVE.into()); + + set_relaychain_block_number(100); + let farm_owner = BOB; + create_global_farm(Some(ext1), ext1, Some(farm_owner.into())); + create_global_farm(Some(ext1), ext2, Some(farm_owner.into())); + + set_relaychain_block_number(200); + create_yield_farm(global_farm_1_id, asset_pair, Some(farm_owner.into())); + create_yield_farm(global_farm_2_id, asset_pair, Some(farm_owner.into())); + + set_relaychain_block_number(400); + let deposit_id = 1; + assert_ok!(XYKLiquidityMining::deposit_shares( + RuntimeOrigin::signed(DAVE.into()), + global_farm_1_id, + yield_farm_1_id, + asset_pair, + dave_shares_balance, + )); + + set_relaychain_block_number(500); + assert_ok!(XYKLiquidityMining::redeposit_shares( + RuntimeOrigin::signed(DAVE.into()), + global_farm_2_id, + yield_farm_2_id, + asset_pair, + deposit_id, + )); + + assert!( + warehouse_liquidity_mining::Deposit::::get(deposit_id) + .unwrap() + .get_yield_farm_entry(yield_farm_2_id) + .is_some() + ); + + set_relaychain_block_number(600); + assert_ok!(XYKLiquidityMining::withdraw_shares( + RuntimeOrigin::signed(DAVE.into()), + deposit_id, + yield_farm_2_id, + asset_pair, + )); + + //Assert + //NOTE: withdraw is claiming rewards automatically + assert_eq!( + hydradx_runtime::Currencies::free_balance(HDX, &DAVE.into()), + 995_300_000_000_000_u128 + ); + + //NOTE: shares should not be unlocked because deposit wasn't destroyed(it has 1 + //yield-farm-entry left) + //assert LM deposit + assert_eq!(Currencies::free_balance(xyk_share_id, &DAVE.into()), Balance::zero()); + assert_eq!( + Currencies::free_balance(xyk_share_id, &XYKLiquidityMining::account_id()), + dave_shares_balance + ); + + //Check if yield-farm-entry was removed from the deposit. + assert!( + warehouse_liquidity_mining::Deposit::::get(deposit_id) + .unwrap() + .get_yield_farm_entry(yield_farm_2_id) + .is_none() + ); + + set_relaychain_block_number(700); + //Arrange - claim before withdraw + assert_ok!(XYKLiquidityMining::claim_rewards( + RuntimeOrigin::signed(DAVE.into()), + deposit_id, + yield_farm_1_id, + )); + + //Act 2 - claim and withdraw should in the same period should work. + assert_ok!(XYKLiquidityMining::withdraw_shares( + RuntimeOrigin::signed(DAVE.into()), + deposit_id, + yield_farm_1_id, + asset_pair, + )); + + //Assert + //NOTE: claim happened before withdraw in this period so no rewards should be claimed. + //This is lower than before because dave received insufficient asset and he had to paid ED. + assert_eq!( + hydradx_runtime::Currencies::free_balance(HDX, &DAVE.into()), + 994_200_000_000_000_u128 + ); + + //NOTE: last shares were unlockend and deposit's nft should be destroyed and omnipool's + //position should be unlocked. + assert!(warehouse_liquidity_mining::Deposit::::get(deposit_id).is_none()); + //LM nft should be destroyed + assert!(hydradx_runtime::Uniques::owner(hydradx_runtime::XYKLmCollectionId::get(), deposit_id).is_none()); + //omnpool's position should be unlocekd + assert_eq!( + Currencies::free_balance(xyk_share_id, &DAVE.into()), + dave_shares_balance + ); + assert_eq!( + Currencies::free_balance(xyk_share_id, &XYKLiquidityMining::account_id()), + Balance::zero() + ); + }); +} + +#[test] +fn liquidity_mining_should_work_when_xyk_assets_are_insufficient() { + TestNet::reset(); + Hydra::execute_with(|| { + let ext1 = register_external_asset(0_u128); + let ext2 = register_external_asset(1_u128); + + let global_farm_1_id = 1; + let global_farm_2_id = 2; + let yield_farm_1_id = 3; + let yield_farm_2_id = 4; + let asset_pair = AssetPair { + asset_in: ext1, + asset_out: ext2, + }; + + //Arrange + let xyk_share_id = create_xyk_pool( + asset_pair.asset_in, + 10_000_000 * UNITS, + asset_pair.asset_out, + 100_000_000 * UNITS, + ); + let dave_shares_balance = Currencies::free_balance(xyk_share_id, &DAVE.into()); + + set_relaychain_block_number(100); + create_global_farm(None, ext1, None); + create_global_farm(None, ext2, None); + + set_relaychain_block_number(200); + create_yield_farm(global_farm_1_id, asset_pair, None); + create_yield_farm(global_farm_2_id, asset_pair, None); + + set_relaychain_block_number(400); + let deposit_id = 1; + assert_ok!(XYKLiquidityMining::deposit_shares( + RuntimeOrigin::signed(DAVE.into()), + global_farm_1_id, + yield_farm_1_id, + asset_pair, + dave_shares_balance, + )); + + set_relaychain_block_number(500); + assert_ok!(XYKLiquidityMining::redeposit_shares( + RuntimeOrigin::signed(DAVE.into()), + global_farm_2_id, + yield_farm_2_id, + asset_pair, + deposit_id, + )); + + assert!( + warehouse_liquidity_mining::Deposit::::get(deposit_id) + .unwrap() + .get_yield_farm_entry(yield_farm_2_id) + .is_some() + ); + + set_relaychain_block_number(600); + assert_ok!(XYKLiquidityMining::withdraw_shares( + RuntimeOrigin::signed(DAVE.into()), + deposit_id, + yield_farm_2_id, + asset_pair, + )); + + //Assert + //NOTE: withdraw is claiming rewards automatically + assert_eq!( + hydradx_runtime::Currencies::free_balance(HDX, &DAVE.into()), + 1_001_854_545_454_545_u128 + ); + + //NOTE: shares should not be unlocked because deposit wasn't destroyed(it has 1 + //yield-farm-entry left) + //assert LM deposit + assert_eq!(Currencies::free_balance(xyk_share_id, &DAVE.into()), Balance::zero()); + assert_eq!( + Currencies::free_balance(xyk_share_id, &XYKLiquidityMining::account_id()), + dave_shares_balance + ); + + //Check if yield-farm-entry was removed from the deposit. + assert!( + warehouse_liquidity_mining::Deposit::::get(deposit_id) + .unwrap() + .get_yield_farm_entry(yield_farm_2_id) + .is_none() + ); + + set_relaychain_block_number(700); + //Arrange - claim before withdraw + assert_ok!(XYKLiquidityMining::claim_rewards( + RuntimeOrigin::signed(DAVE.into()), + deposit_id, + yield_farm_1_id, + )); + + //Act 2 - claim and withdraw should in the same period should work. + assert_ok!(XYKLiquidityMining::withdraw_shares( + RuntimeOrigin::signed(DAVE.into()), + deposit_id, + yield_farm_1_id, + asset_pair, + )); + + //Assert + //NOTE: claim happened before withdraw in this period so no rewards should be claimed. + assert_eq!( + hydradx_runtime::Currencies::free_balance(HDX, &DAVE.into()), + 1_019_216_083_916_083_u128 + ); + + //NOTE: last shares were unlockend and deposit's nft should be destroyed and omnipool's + //position should be unlocked. + assert!(warehouse_liquidity_mining::Deposit::::get(deposit_id).is_none()); + //LM nft should be destroyed + assert!(hydradx_runtime::Uniques::owner(hydradx_runtime::XYKLmCollectionId::get(), deposit_id).is_none()); + //omnpool's position should be unlocekd + assert_eq!( + Currencies::free_balance(xyk_share_id, &DAVE.into()), + dave_shares_balance + ); + assert_eq!( + Currencies::free_balance(xyk_share_id, &XYKLiquidityMining::account_id()), + Balance::zero() + ); + }); +} + +#[test] +fn price_adjustment_from_oracle_should_be_saved_in_global_farm_when_oracle_is_available() { + TestNet::reset(); + + Hydra::execute_with(|| { + let global_farm_1_id = 1; + let global_farm_2_id = 2; + let yield_farm_1_id = 3; + let asset_pair = AssetPair { + asset_in: PEPE, + asset_out: ACA, + }; + + //Arrange + let xyk_share_id = create_xyk_pool( + asset_pair.asset_in, + 10_000_000 * UNITS, + asset_pair.asset_out, + 100_000_000 * UNITS, + ); + let dave_shares_balance = Currencies::free_balance(xyk_share_id, &DAVE.into()); + + set_relaychain_block_number(100); + create_global_farm(Some(ACA), PEPE, None); + create_global_farm(Some(PEPE), ACA, None); + + set_relaychain_block_number(200); + create_yield_farm(global_farm_1_id, asset_pair, None); + create_yield_farm(global_farm_2_id, asset_pair, None); + + assert_ok!(Currencies::update_balance( + hydradx_runtime::RuntimeOrigin::root(), + ALICE.into(), + PEPE, + 1000 * UNITS as i128, + )); + + assert_ok!(Currencies::update_balance( + hydradx_runtime::RuntimeOrigin::root(), + ALICE.into(), + ACA, + 1000 * UNITS as i128, + )); + + assert_ok!(hydradx_runtime::XYK::buy( + RuntimeOrigin::signed(ALICE.into()), + PEPE, + ACA, + 2 * UNITS, + 200 * UNITS, + false, + )); + + hydra_run_to_block(500); + set_relaychain_block_number(500); + + //Act + let deposit_id = 1; + assert_ok!(XYKLiquidityMining::deposit_shares( + RuntimeOrigin::signed(DAVE.into()), + global_farm_1_id, + yield_farm_1_id, + asset_pair, + dave_shares_balance, + )); + + set_relaychain_block_number(600); + + assert_ok!(XYKLiquidityMining::claim_rewards( + RuntimeOrigin::signed(DAVE.into()), + deposit_id, + yield_farm_1_id + )); + + //Assert + let global_farm = XYKWarehouseLM::global_farm(global_farm_1_id).unwrap(); + let price_adjustment = DefaultPriceAdjustment::get(&global_farm).unwrap(); + assert_eq!(price_adjustment, FixedU128::from_inner(10_000_004_006_001_202_400_u128)); + }); +} + +#[test] +fn liquidity_mining_should_work_when_farm_distribute_bonds() { + TestNet::reset(); + + Hydra::execute_with(|| { + //Create bodns + assert_ok!(hydradx_runtime::Balances::force_set_balance( + hydradx_runtime::RuntimeOrigin::root(), + Treasury::account_id(), + 2_000_000 * UNITS, + )); + + let maturity = NOW + MONTH; + let bond_id = AssetRegistry::next_asset_id().unwrap(); + assert_ok!(Bonds::issue( + RuntimeOrigin::signed(Treasury::account_id()), + HDX, + 2_000_000 * UNITS, + maturity + )); + assert_eq!(AssetRegistry::assets(bond_id).unwrap().asset_type, AssetType::Bond); + //NOTE: make bond sufficient because treasury account is whitelisted. In this case farm + //would have to pay ED for receiving insufficicient bods and farm's account has no balance. + assert_ok!(AssetRegistry::update( + hydradx_runtime::RuntimeOrigin::root(), + bond_id, + None, + None, + None, + None, + Some(true), + None, + None, + None, + )); + + // farm's rewards in test are less than ED. + assert_ok!(hydradx_runtime::Currencies::transfer( + hydradx_runtime::RuntimeOrigin::signed(Treasury::account_id()), + CHARLIE.into(), + bond_id, + 2 * UNITS, + )); + + let global_farm_1_id = 1; + let yield_farm_1_id = 2; + let asset_pair = AssetPair { + asset_in: PEPE, + asset_out: ACA, + }; + + //Arrange + let xyk_share_id = create_xyk_pool( + asset_pair.asset_in, + 10_000_000 * UNITS, + asset_pair.asset_out, + 100_000_000 * UNITS, + ); + + create_xyk_pool(HDX, 10_000_000 * UNITS, PEPE, 100_000_000 * UNITS); + let dave_shares_balance = Currencies::free_balance(xyk_share_id, &DAVE.into()); + + set_relaychain_block_number(100); + create_global_farm(Some(bond_id), PEPE, None); + + set_relaychain_block_number(200); + create_yield_farm(global_farm_1_id, asset_pair, None); + + assert_ok!(Currencies::update_balance( + hydradx_runtime::RuntimeOrigin::root(), + ALICE.into(), + PEPE, + 1000 * UNITS as i128, + )); + + assert_ok!(Currencies::update_balance( + hydradx_runtime::RuntimeOrigin::root(), + ALICE.into(), + bond_id, + 1000 * UNITS as i128, + )); + + assert_ok!(hydradx_runtime::XYK::buy( + RuntimeOrigin::signed(ALICE.into()), + PEPE, + HDX, + 2 * UNITS, + 200 * UNITS, + false, + )); + + hydra_run_to_block(500); + set_relaychain_block_number(500); + + //Act + let deposit_id = 1; + assert_ok!(XYKLiquidityMining::deposit_shares( + RuntimeOrigin::signed(DAVE.into()), + global_farm_1_id, + yield_farm_1_id, + asset_pair, + dave_shares_balance, + )); + + set_relaychain_block_number(600); + + let dave_bonds_balance = Currencies::free_balance(bond_id, &DAVE.into()); + + assert_ok!(XYKLiquidityMining::claim_rewards( + RuntimeOrigin::signed(DAVE.into()), + deposit_id, + yield_farm_1_id + )); + + //Assert + assert!(Currencies::free_balance(bond_id, &DAVE.into()) > dave_bonds_balance); + let global_farm = XYKWarehouseLM::global_farm(global_farm_1_id).unwrap(); + let price_adjustment = DefaultPriceAdjustment::get(&global_farm).unwrap(); + assert_eq!(price_adjustment, FixedU128::from_inner(100_000_004_006_000_120_u128)); + }); +} + +fn create_xyk_pool(asset_a: u32, amount_a: u128, asset_b: u32, amount_b: u128) -> AssetId { + let share_id = AssetRegistry::next_asset_id().unwrap(); + + assert_ok!(Currencies::update_balance( + hydradx_runtime::RuntimeOrigin::root(), + DAVE.into(), + asset_a, + amount_a as i128, + )); + assert_ok!(Currencies::update_balance( + hydradx_runtime::RuntimeOrigin::root(), + DAVE.into(), + asset_b, + amount_b as i128, + )); + + assert_ok!(XYK::create_pool( + RuntimeOrigin::signed(DAVE.into()), + asset_a, + amount_a, + asset_b, + amount_b, + )); + + share_id +} + +fn create_global_farm(rewards_currency: Option, incentivized_asset: AssetId, owner: Option) { + let total_rewards = 1_000_000 * UNITS; + + let owner = owner.unwrap_or(Treasury::account_id()); + assert_ok!(Currencies::update_balance( + hydradx_runtime::RuntimeOrigin::root(), + owner.clone(), + rewards_currency.unwrap_or(HDX), + total_rewards as i128 + 1_000, //for seeding pot's account + )); + + assert_ok!(XYKLiquidityMining::create_global_farm( + hydradx_runtime::RuntimeOrigin::root(), + total_rewards, + 1_000_000, + 10, + incentivized_asset, + rewards_currency.unwrap_or(HDX), + owner, + Perquintill::from_parts(570_776_255_707), + 1_000, + FixedU128::one() + )); +} + +fn create_yield_farm(id: GlobalFarmId, pair: AssetPair, owner: Option) { + assert_ok!(XYKLiquidityMining::create_yield_farm( + RuntimeOrigin::signed(owner.unwrap_or(Treasury::account_id())), + id, + pair, + FixedU128::one(), + Some(LoyaltyCurve::default()) + )); +} + +fn register_external_asset(general_index: u128) -> AssetId { + let location = hydradx_runtime::AssetLocation(MultiLocation::new( + 1, + X2(Parachain(MOONBEAM_PARA_ID), GeneralIndex(general_index)), + )); + + let next_asset_id = AssetRegistry::next_asset_id().unwrap(); + AssetRegistry::register_external(hydra_origin::signed(BOB.into()), location).unwrap(); + + next_asset_id +} diff --git a/node/Cargo.toml b/node/Cargo.toml index d246a8cd5..1c306e109 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx" -version = "12.1.0" +version = "12.2.0" description = "HydraDX node" authors = ["GalacticCouncil"] edition = "2021" diff --git a/node/src/chain_spec/mod.rs b/node/src/chain_spec/mod.rs index a9e1ff8cb..713c4160c 100644 --- a/node/src/chain_spec/mod.rs +++ b/node/src/chain_spec/mod.rs @@ -216,6 +216,8 @@ pub fn parachain_genesis( }, ethereum: Default::default(), evm: Default::default(), + xyk_warehouse_lm: Default::default(), + xyk_liquidity_mining: Default::default(), } } diff --git a/pallets/asset-registry/Cargo.toml b/pallets/asset-registry/Cargo.toml index a4cdc9cf9..fe331df95 100644 --- a/pallets/asset-registry/Cargo.toml +++ b/pallets/asset-registry/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-asset-registry" -version = "3.1.1" +version = "3.2.0" description = "Pallet for asset registry management" authors = ["GalacticCouncil"] edition = "2021" diff --git a/pallets/asset-registry/src/lib.rs b/pallets/asset-registry/src/lib.rs index c91188006..3110d41db 100644 --- a/pallets/asset-registry/src/lib.rs +++ b/pallets/asset-registry/src/lib.rs @@ -682,6 +682,10 @@ impl Inspect for Pallet { fn asset_symbol(id: Self::AssetId) -> Option> { Self::assets(id).and_then(|a| a.symbol.map(|v| v.into())) } + + fn existential_deposit(id: Self::AssetId) -> Option { + Self::assets(id).map(|a| a.existential_deposit) + } } impl Mutate for Pallet { diff --git a/pallets/asset-registry/src/tests/inspect_trait.rs b/pallets/asset-registry/src/tests/inspect_trait.rs index ee57dde96..e51475577 100644 --- a/pallets/asset-registry/src/tests/inspect_trait.rs +++ b/pallets/asset-registry/src/tests/inspect_trait.rs @@ -267,3 +267,39 @@ fn asset_symbol_should_work() { assert_eq!(::asset_name(non_existing_id), None); }); } + +#[test] +fn existential_deposit_should_work() { + let non_existing_id = 543_u32; + let asset_one_symbol = b"TKN".to_vec(); + + ExtBuilder::default() + .with_assets(vec![ + ( + Some(1), + Some(b"Tkn1".to_vec().try_into().unwrap()), + UNIT, + Some(asset_one_symbol.try_into().unwrap()), + None, + None, + true, + ), + (Some(2), None, UNIT, None, None, None, false), + ( + Some(3), + Some(b"Tkn3".to_vec().try_into().unwrap()), + 2 * UNIT, + None, + None, + None, + true, + ), + ]) + .build() + .execute_with(|| { + //Act & assert + assert_eq!(::existential_deposit(3), Some(2 * UNIT)); + + assert_eq!(::existential_deposit(non_existing_id), None); + }); +} diff --git a/pallets/bonds/Cargo.toml b/pallets/bonds/Cargo.toml index abec15d8f..13f11eb0a 100644 --- a/pallets/bonds/Cargo.toml +++ b/pallets/bonds/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-bonds" -version = "2.2.0" +version = "2.2.1" authors = ['GalacticCouncil'] edition = "2021" license = "Apache-2.0" diff --git a/pallets/bonds/src/tests/mock.rs b/pallets/bonds/src/tests/mock.rs index 4ac045b33..0bf2f8181 100644 --- a/pallets/bonds/src/tests/mock.rs +++ b/pallets/bonds/src/tests/mock.rs @@ -252,6 +252,10 @@ impl Inspect for DummyRegistry { fn asset_symbol(_id: Self::AssetId) -> Option> { unimplemented!() } + + fn existential_deposit(_id: Self::AssetId) -> Option { + unimplemented!() + } } pub struct ExtBuilder { diff --git a/pallets/circuit-breaker/Cargo.toml b/pallets/circuit-breaker/Cargo.toml index 926a8872d..7ad396c84 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.20" +version = "1.1.21" 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 fe6a0e6db..cfe4131f1 100644 --- a/pallets/circuit-breaker/src/tests/mock.rs +++ b/pallets/circuit-breaker/src/tests/mock.rs @@ -403,6 +403,10 @@ where fn asset_name(_id: Self::AssetId) -> Option> { unimplemented!() } + + fn existential_deposit(_id: Self::AssetId) -> Option { + unimplemented!() + } } pub struct ExtBuilder { diff --git a/pallets/dca/Cargo.toml b/pallets/dca/Cargo.toml index c921e6f6e..4b50dba37 100644 --- a/pallets/dca/Cargo.toml +++ b/pallets/dca/Cargo.toml @@ -1,6 +1,6 @@ [package] name = 'pallet-dca' -version = "1.4.1" +version = "1.4.2" description = 'A pallet to manage DCA scheduling' authors = ['GalacticCouncil'] edition = '2021' diff --git a/pallets/dca/src/tests/mock.rs b/pallets/dca/src/tests/mock.rs index d24f1e671..454596d77 100644 --- a/pallets/dca/src/tests/mock.rs +++ b/pallets/dca/src/tests/mock.rs @@ -385,6 +385,10 @@ impl hydradx_traits::registry::Inspect for MockedAssetRegistry { fn asset_symbol(_id: Self::AssetId) -> Option> { unimplemented!() } + + fn existential_deposit(_id: Self::AssetId) -> Option { + unimplemented!() + } } impl pallet_route_executor::Config for Test { @@ -810,6 +814,10 @@ where fn asset_symbol(_id: Self::AssetId) -> Option> { unimplemented!() } + + fn existential_deposit(_id: Self::AssetId) -> Option { + unimplemented!() + } } pub type AccountId = u64; diff --git a/pallets/liquidity-mining/Cargo.toml b/pallets/liquidity-mining/Cargo.toml index ee0110220..4f86bd892 100644 --- a/pallets/liquidity-mining/Cargo.toml +++ b/pallets/liquidity-mining/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-liquidity-mining" -version = "4.3.0" +version = "4.3.1" description = "Liquidity mining" authors = ["GalacticCouncil"] edition = "2021" diff --git a/pallets/liquidity-mining/src/lib.rs b/pallets/liquidity-mining/src/lib.rs index 79aff9e83..ca77bb4de 100644 --- a/pallets/liquidity-mining/src/lib.rs +++ b/pallets/liquidity-mining/src/lib.rs @@ -1911,3 +1911,9 @@ impl, I: 'static> hydradx_traits::liquidity_mining::Mutate, I: 'static> hydradx_traits::liquidity_mining::Inspect for Pallet { + fn pot_account() -> Option { + Self::pot_account_id() + } +} diff --git a/pallets/liquidity-mining/src/tests/mock.rs b/pallets/liquidity-mining/src/tests/mock.rs index 53b64b631..fbac1cd5e 100644 --- a/pallets/liquidity-mining/src/tests/mock.rs +++ b/pallets/liquidity-mining/src/tests/mock.rs @@ -456,6 +456,10 @@ impl Inspect for DummyRegistry { fn asset_name(_id: Self::AssetId) -> Option> { unimplemented!() } + + fn existential_deposit(_id: Self::AssetId) -> Option { + unimplemented!() + } } impl GetByKey for DummyRegistry { diff --git a/pallets/omnipool-liquidity-mining/Cargo.toml b/pallets/omnipool-liquidity-mining/Cargo.toml index 692a5e726..c1315670b 100644 --- a/pallets/omnipool-liquidity-mining/Cargo.toml +++ b/pallets/omnipool-liquidity-mining/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-omnipool-liquidity-mining" -version = "2.1.2" +version = "2.1.3" authors = ['GalacticCouncil'] edition = "2021" license = "Apache-2.0" diff --git a/pallets/omnipool-liquidity-mining/src/tests/mock.rs b/pallets/omnipool-liquidity-mining/src/tests/mock.rs index 267c39cd4..357b10548 100644 --- a/pallets/omnipool-liquidity-mining/src/tests/mock.rs +++ b/pallets/omnipool-liquidity-mining/src/tests/mock.rs @@ -675,6 +675,10 @@ where fn asset_symbol(_id: Self::AssetId) -> Option> { unimplemented!() } + + fn existential_deposit(_id: Self::AssetId) -> Option { + unimplemented!() + } } #[cfg(feature = "runtime-benchmarks")] diff --git a/pallets/omnipool/Cargo.toml b/pallets/omnipool/Cargo.toml index 7dee25670..4a5e7943c 100644 --- a/pallets/omnipool/Cargo.toml +++ b/pallets/omnipool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-omnipool" -version = "4.1.4" +version = "4.1.5" authors = ['GalacticCouncil'] edition = "2021" license = "Apache-2.0" diff --git a/pallets/omnipool/src/tests/mock.rs b/pallets/omnipool/src/tests/mock.rs index aac0fb912..f2564901c 100644 --- a/pallets/omnipool/src/tests/mock.rs +++ b/pallets/omnipool/src/tests/mock.rs @@ -551,6 +551,10 @@ where fn asset_symbol(_id: Self::AssetId) -> Option> { unimplemented!() } + + fn existential_deposit(_id: Self::AssetId) -> Option { + unimplemented!() + } } pub(crate) fn get_mock_minted_position(position_id: u32) -> Option { diff --git a/pallets/otc/Cargo.toml b/pallets/otc/Cargo.toml index e27a5b5b3..3eba0e9f0 100644 --- a/pallets/otc/Cargo.toml +++ b/pallets/otc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = 'pallet-otc' -version = '1.1.0' +version = '1.1.1' description = 'A pallet for trustless over-the-counter trading' authors = ['GalacticCouncil'] edition = '2021' diff --git a/pallets/otc/src/tests/mock.rs b/pallets/otc/src/tests/mock.rs index 67b9cb65e..8612a3381 100644 --- a/pallets/otc/src/tests/mock.rs +++ b/pallets/otc/src/tests/mock.rs @@ -164,6 +164,10 @@ impl Inspect for DummyRegistry { fn asset_symbol(_id: Self::AssetId) -> Option> { unimplemented!() } + + fn existential_deposit(_id: Self::AssetId) -> Option { + unimplemented!() + } } #[cfg(feature = "runtime-benchmarks")] diff --git a/pallets/route-executor/Cargo.toml b/pallets/route-executor/Cargo.toml index db6487946..16ba22e8d 100644 --- a/pallets/route-executor/Cargo.toml +++ b/pallets/route-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = 'pallet-route-executor' -version = '2.2.0' +version = '2.2.1' description = 'A pallet to execute a route containing a sequence of trades' authors = ['GalacticCouncil'] edition = '2021' diff --git a/pallets/route-executor/src/tests/mock.rs b/pallets/route-executor/src/tests/mock.rs index b6374b48f..cebcaabab 100644 --- a/pallets/route-executor/src/tests/mock.rs +++ b/pallets/route-executor/src/tests/mock.rs @@ -187,6 +187,10 @@ impl hydradx_traits::registry::Inspect for MockedAssetRegistry { fn asset_symbol(_id: Self::AssetId) -> Option> { unimplemented!() } + + fn existential_deposit(_id: Self::AssetId) -> Option { + unimplemented!() + } } pub type AccountId = u64; diff --git a/pallets/stableswap/Cargo.toml b/pallets/stableswap/Cargo.toml index a49599419..1ec8da512 100644 --- a/pallets/stableswap/Cargo.toml +++ b/pallets/stableswap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = 'pallet-stableswap' -version = '3.5.0' +version = '3.5.1' description = 'AMM for correlated assets' authors = ['GalacticCouncil'] edition = '2021' diff --git a/pallets/stableswap/src/tests/mock.rs b/pallets/stableswap/src/tests/mock.rs index 439edbdde..c6ae86e8a 100644 --- a/pallets/stableswap/src/tests/mock.rs +++ b/pallets/stableswap/src/tests/mock.rs @@ -350,6 +350,10 @@ impl Inspect for DummyRegistry { fn asset_symbol(_id: Self::AssetId) -> Option> { unimplemented!() } + + fn existential_deposit(_id: Self::AssetId) -> Option { + unimplemented!() + } } #[cfg(feature = "runtime-benchmarks")] diff --git a/pallets/xcm-rate-limiter/Cargo.toml b/pallets/xcm-rate-limiter/Cargo.toml index a657534ed..18a46a6eb 100644 --- a/pallets/xcm-rate-limiter/Cargo.toml +++ b/pallets/xcm-rate-limiter/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-xcm-rate-limiter" -version = "0.1.5" +version = "0.1.6" authors = ["GalacticCouncil "] edition = "2021" license = "Apache-2.0" diff --git a/pallets/xcm-rate-limiter/src/tests/mock.rs b/pallets/xcm-rate-limiter/src/tests/mock.rs index 41ff345a4..fc3bcf239 100644 --- a/pallets/xcm-rate-limiter/src/tests/mock.rs +++ b/pallets/xcm-rate-limiter/src/tests/mock.rs @@ -345,6 +345,10 @@ where fn asset_name(_id: Self::AssetId) -> Option> { unimplemented!() } + + fn existential_deposit(_id: Self::AssetId) -> Option { + unimplemented!() + } } pub struct ExtBuilder { diff --git a/pallets/xyk-liquidity-mining/Cargo.toml b/pallets/xyk-liquidity-mining/Cargo.toml new file mode 100644 index 000000000..418de04a5 --- /dev/null +++ b/pallets/xyk-liquidity-mining/Cargo.toml @@ -0,0 +1,64 @@ +[package] +name = "pallet-xyk-liquidity-mining" +version = "1.1.10" +description = "Liquidity mining" +authors = ["GalacticCouncil"] +edition = "2021" +homepage = 'https://github.com/galacticcouncil/hydradx-node' +repository = 'https://github.com/galacticcouncil/hydradx-node' +license = "Apache 2.0" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.4.0", features = ["derive", "max-encoded-len"], default-features = false } +scale-info = { version = "2.3.1", default-features = false, features = ["derive"] } + +log = { workspace = true } + +# ORML dependencies +orml-traits = { workspace = true } + +# Local dependencies +primitives = { workspace = true } + +# Warehouse dependencies +pallet-liquidity-mining = { workspace = true } +hydradx-traits = { workspace = true } +pallet-xyk = { workspace = true } + +# Substrate dependencies +sp-std = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-arithmetic = { workspace = true } +sp-runtime = { workspace = true } + +[dev-dependencies] +sp-io = { workspace = true } +sp-core = { workspace = true } +orml-tokens = { workspace = true } +pallet-balances = { workspace = true } +pretty_assertions = "1.2.1" + +[features] +default = ["std"] +std = [ + "codec/std", + "scale-info/std", + "sp-runtime/std", + "sp-std/std", + "frame-support/std", + "frame-system/std", + "sp-core/std", + "sp-io/std", + "pallet-balances/std", + "orml-tokens/std", + "pallet-xyk/std", + "pallet-liquidity-mining/std", + "primitives/std", + "hydradx-traits/std", + "log/std", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/xyk-liquidity-mining/benchmarking/Cargo.toml b/pallets/xyk-liquidity-mining/benchmarking/Cargo.toml new file mode 100644 index 000000000..829873557 --- /dev/null +++ b/pallets/xyk-liquidity-mining/benchmarking/Cargo.toml @@ -0,0 +1,70 @@ +[package] +name = "pallet-xyk-liquidity-mining-benchmarking" +version = "1.0.16" +description = "Liquidity Mining Benchmarking Module" +authors = ["GalacticCouncil"] +edition = "2021" +homepage = "https://github.com/galacticcouncil/basilisk-node" +license = "Apache 2.0" +repository = "https://github.com/galacticcouncil/basilisk-node" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.4.0", features = ["derive", "max-encoded-len"], default-features = false } +scale-info = { version = "2.3.1", features = ["derive"], default-features = false } + +# Local dependencies +pallet-xyk-liquidity-mining = { path = "../../xyk-liquidity-mining", default-features = false } +primitives = { path = "../../../primitives", default-features = false } + +# ORML dependencies +orml-traits = { workspace = true } +orml-tokens = { workspace = true } + +# HydraDX dependencies +pallet-nft = { workspace = true } +pallet-asset-registry = { workspace = true } +pallet-liquidity-mining = { workspace = true } +hydradx-traits = { workspace = true } +pallet-duster = { workspace = true } +pallet-xyk = { workspace = true } + +# Substrate dependencies +sp-std = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-arithmetic = { workspace = true } +pallet-uniques = { workspace = true } +pallet-balances = { workspace = true } + +frame-benchmarking = { workspace = true } + +[dev-dependencies] +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-core = { workspace = true } + +[features] +default = ["std"] + +runtime-benchmarks=["pallet-uniques/runtime-benchmarks"] + +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "sp-std/std", + "orml-traits/std", + "orml-tokens/std", + "primitives/std", + "hydradx-traits/std", + "pallet-balances/std", + "frame-benchmarking/std", + "pallet-xyk-liquidity-mining/std", + "pallet-asset-registry/std", + "pallet-nft/std", + "pallet-uniques/std", + "pallet-duster/std", +] diff --git a/pallets/xyk-liquidity-mining/benchmarking/src/lib.rs b/pallets/xyk-liquidity-mining/benchmarking/src/lib.rs new file mode 100644 index 000000000..f3e4b918c --- /dev/null +++ b/pallets/xyk-liquidity-mining/benchmarking/src/lib.rs @@ -0,0 +1,458 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2022 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")] +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::unnecessary_wraps)] + +mod mock; + +use pallet_liquidity_mining::{GlobalFarmId, LoyaltyCurve, YieldFarmId}; +use pallet_xyk::types::{AssetId, AssetPair, Balance}; +use pallet_xyk_liquidity_mining::Pallet as XYKLiquidityMining; + +use frame_benchmarking::{account, benchmarks}; +use frame_system::{pallet_prelude::BlockNumberFor, Pallet as System, RawOrigin}; + +use frame_support::dispatch; +use orml_traits::arithmetic::One; +use orml_traits::MultiCurrency; +use sp_arithmetic::FixedU128; +use sp_arithmetic::Perquintill; +use sp_std::convert::From; + +use pallet_xyk as xykpool; + +pub const GLOBAL_FARM_ID: GlobalFarmId = 1; +pub const GLOBAL_FARM_ID_2: GlobalFarmId = 3; +pub const YIELD_FARM_ID: YieldFarmId = 2; +pub const YIELD_FARM_ID_2: YieldFarmId = 4; +pub const YIELD_FARM_ID_3: YieldFarmId = 4; +pub const DEPOSIT_ID: u128 = 1; + +const SEED: u32 = 0; + +const BSX: AssetId = 0; +const KSM: AssetId = 1; +const DOT: AssetId = 2; +const ASSET_PAIR: AssetPair = AssetPair { + asset_in: BSX, + asset_out: KSM, +}; + +const INITIAL_BALANCE: Balance = 100_000_000; +const ONE: Balance = 1_000_000_000_000; + +pub trait Config: pallet_xyk_liquidity_mining::Config + pallet_xyk::Config + pallet_asset_registry::Config {} + +pub struct Pallet(XYKLiquidityMining); + +type MultiCurrencyOf = ::MultiCurrency; + +fn create_funded_account(name: &'static str, index: u32) -> T::AccountId { + let caller: T::AccountId = account(name, index, SEED); + + ::MultiCurrency::deposit(BSX, &caller, INITIAL_BALANCE * ONE).unwrap(); + + ::MultiCurrency::deposit(KSM, &caller, INITIAL_BALANCE * ONE).unwrap(); + + ::MultiCurrency::deposit(DOT, &caller, INITIAL_BALANCE * ONE).unwrap(); + + caller +} + +fn initialize_pool( + caller: T::AccountId, + asset_a: AssetId, + asset_b: AssetId, + amount_a: Balance, + amount_b: Balance, +) -> dispatch::DispatchResult { + xykpool::Pallet::::create_pool(RawOrigin::Signed(caller).into(), asset_a, amount_a, asset_b, amount_b) +} + +fn xyk_add_liquidity( + caller: T::AccountId, + assets: AssetPair, + amount_a: Balance, + amount_b_max: Balance, +) -> dispatch::DispatchResult { + xykpool::Pallet::::add_liquidity( + RawOrigin::Signed(caller).into(), + assets.asset_in, + assets.asset_out, + amount_a, + amount_b_max, + ) +} + +fn lm_create_global_farm( + total_rewards: Balance, + owner: T::AccountId, + yield_per_period: Perquintill, +) -> dispatch::DispatchResult { + XYKLiquidityMining::::create_global_farm( + RawOrigin::Root.into(), + total_rewards, + BlockNumberFor::::from(1_000_000_u32), + BlockNumberFor::::from(1_u32), + BSX, + BSX, + owner, + yield_per_period, + 1_000, + One::one(), + ) +} + +fn lm_deposit_shares(caller: T::AccountId, assets: AssetPair, amount: Balance) -> dispatch::DispatchResult { + XYKLiquidityMining::::deposit_shares( + RawOrigin::Signed(caller).into(), + GLOBAL_FARM_ID, + YIELD_FARM_ID, + assets, + amount, + ) +} + +fn lm_create_yield_farm( + caller: T::AccountId, + farm_id: GlobalFarmId, + assets: AssetPair, + multiplier: FixedU128, +) -> dispatch::DispatchResult { + XYKLiquidityMining::::create_yield_farm( + RawOrigin::Signed(caller).into(), + farm_id, + assets, + multiplier, + Some(LoyaltyCurve::default()), + ) +} + +fn set_period(block: u32) { + //NOTE: predefined global farm has period size = 1 block. + System::::set_block_number(block.into()); +} + +benchmarks! { + create_global_farm { + let total_rewards = 1_000_000 * ONE; + let caller = create_funded_account::("caller", 0); + let planned_yielding_periods = BlockNumberFor::::from(1_000_000_u32); + let yield_per_period = Perquintill::from_percent(20); + let blocks_per_period = BlockNumberFor::::from(1_u32); + }: { + XYKLiquidityMining::::create_global_farm(RawOrigin::Root.into(), total_rewards, planned_yielding_periods, blocks_per_period, BSX, BSX, caller.clone(), yield_per_period, 1_000, One::one())? + } + + update_global_farm { + let caller = create_funded_account::("caller", 0); + let xyk_caller = create_funded_account::("xyk_caller", 1); + let liq_provider = create_funded_account::("liq_provider", 2); + + initialize_pool::(xyk_caller, ASSET_PAIR.asset_in, ASSET_PAIR.asset_out, 1_000_000 * ONE, 10_000_000 * ONE)?; + let xyk_id = xykpool::Pallet::::pair_account_from_assets(ASSET_PAIR.asset_in, ASSET_PAIR.asset_out); + xyk_add_liquidity::(liq_provider.clone(), ASSET_PAIR, 10 * ONE, 1_000 * ONE)?; + + lm_create_global_farm::(1_000_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller.clone(), GLOBAL_FARM_ID, ASSET_PAIR, FixedU128::one())?; + + lm_deposit_shares::(liq_provider, ASSET_PAIR, 10 * ONE)?; + set_period::(200_000); + }: { + XYKLiquidityMining::::update_global_farm(RawOrigin::Signed(caller.clone()).into(), GLOBAL_FARM_ID, FixedU128::from_inner(234_456_677_000_000_000_u128))? + } + + terminate_global_farm { + let total_rewards = 1_000_000 * ONE; + let caller = create_funded_account::("caller", 0); + let xyk_caller = create_funded_account::("xyk_caller", 1); + let liq_provider = create_funded_account::("liq_provider", 2); + + initialize_pool::(xyk_caller, ASSET_PAIR.asset_in, ASSET_PAIR.asset_out, 1_000_000 * ONE, 10_000_000 * ONE)?; + let xyk_id = xykpool::Pallet::::pair_account_from_assets(ASSET_PAIR.asset_in, ASSET_PAIR.asset_out); + xyk_add_liquidity::(liq_provider.clone(), ASSET_PAIR, 10 * ONE, 1_000 * ONE)?; + + lm_create_global_farm::(1_000_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller.clone(), GLOBAL_FARM_ID, ASSET_PAIR, FixedU128::one())?; + + lm_deposit_shares::(liq_provider, ASSET_PAIR, 10 * ONE)?; + set_period::(100_000); + + XYKLiquidityMining::::stop_yield_farm(RawOrigin::Signed(caller.clone()).into(), GLOBAL_FARM_ID, ASSET_PAIR)?; + XYKLiquidityMining::::terminate_yield_farm(RawOrigin::Signed(caller.clone()).into(), GLOBAL_FARM_ID, YIELD_FARM_ID, ASSET_PAIR)?; + set_period::(200_000); + }: { + XYKLiquidityMining::::terminate_global_farm(RawOrigin::Signed(caller.clone()).into(), GLOBAL_FARM_ID)? + } + + create_yield_farm { + let caller = create_funded_account::("caller", 0); + let xyk_caller = create_funded_account::("xyk_caller", 1); + let liq_provider = create_funded_account::("liq_provider", 2); + let bsx_dot = AssetPair { + asset_in: BSX, + asset_out: DOT + }; + + initialize_pool::(xyk_caller.clone(), ASSET_PAIR.asset_in, ASSET_PAIR.asset_out, 1_000_000 * ONE, 10_000_000 * ONE)?; + let xyk_id = xykpool::Pallet::::pair_account_from_assets(ASSET_PAIR.asset_in, ASSET_PAIR.asset_out); + xyk_add_liquidity::(liq_provider.clone(), ASSET_PAIR, 10 * ONE, 1_000 * ONE)?; + + lm_create_global_farm::(1_000_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller.clone(), GLOBAL_FARM_ID, ASSET_PAIR, FixedU128::one())?; + + lm_deposit_shares::(liq_provider, ASSET_PAIR, 10 * ONE)?; + set_period::(100_000); + + initialize_pool::(xyk_caller, bsx_dot.asset_in, bsx_dot.asset_out, 1_000_000 * ONE, 10_000_000 * ONE)?; + }: { + XYKLiquidityMining::::create_yield_farm(RawOrigin::Signed(caller.clone()).into(), GLOBAL_FARM_ID, bsx_dot, FixedU128::from(50_000_000_u128), Some(LoyaltyCurve::default()))? + } + + update_yield_farm { + let new_multiplier = FixedU128::one(); + let caller = create_funded_account::("caller", 0); + let xyk_caller = create_funded_account::("xyk_caller", 1); + let liq_provider = create_funded_account::("liq_provider", 2); + + initialize_pool::(xyk_caller, ASSET_PAIR.asset_in, ASSET_PAIR.asset_out, 1_000_000 * ONE, 10_000_000 * ONE)?; + let xyk_id = xykpool::Pallet::::pair_account_from_assets(ASSET_PAIR.asset_in, ASSET_PAIR.asset_out); + xyk_add_liquidity::(liq_provider.clone(), ASSET_PAIR, 10 * ONE, 1_000 * ONE)?; + + lm_create_global_farm::(1_000_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller.clone(), GLOBAL_FARM_ID, ASSET_PAIR, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + lm_deposit_shares::(liq_provider, ASSET_PAIR, 10 * ONE)?; + set_period::(100_000); + }: { + XYKLiquidityMining::::update_yield_farm(RawOrigin::Signed(caller.clone()).into(), 1, ASSET_PAIR, new_multiplier)? + } + + stop_yield_farm { + let caller = create_funded_account::("caller", 0); + let xyk_caller = create_funded_account::("xyk_caller", 1); + let liq_provider = create_funded_account::("liq_provider", 2); + + initialize_pool::(xyk_caller, ASSET_PAIR.asset_in, ASSET_PAIR.asset_out, 1_000_000 * ONE, 10_000_000 * ONE)?; + let xyk_id = xykpool::Pallet::::pair_account_from_assets(ASSET_PAIR.asset_in, ASSET_PAIR.asset_out); + xyk_add_liquidity::(liq_provider.clone(), ASSET_PAIR, 10 * ONE, 1_000 * ONE)?; + + lm_create_global_farm::(1_000_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller.clone(), GLOBAL_FARM_ID, ASSET_PAIR, FixedU128::one())?; + + lm_deposit_shares::(liq_provider, ASSET_PAIR, 10 * ONE)?; + set_period::(100_000); + }: { + XYKLiquidityMining::::stop_yield_farm(RawOrigin::Signed(caller.clone()).into(), GLOBAL_FARM_ID, ASSET_PAIR)? + } + + terminate_yield_farm { + let caller = create_funded_account::("caller", 0); + let xyk_caller = create_funded_account::("xyk_caller", 1); + let liq_provider = create_funded_account::("liq_provider", 2); + + initialize_pool::(xyk_caller, ASSET_PAIR.asset_in, ASSET_PAIR.asset_out, 1_000_000 * ONE, 10_000_000 * ONE)?; + let xyk_id = xykpool::Pallet::::pair_account_from_assets(ASSET_PAIR.asset_in, ASSET_PAIR.asset_out); + xyk_add_liquidity::(liq_provider.clone(), ASSET_PAIR, 10 * ONE, 1_000 * ONE)?; + + lm_create_global_farm::(1_000_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller.clone(), GLOBAL_FARM_ID, ASSET_PAIR, FixedU128::one())?; + + lm_deposit_shares::(liq_provider, ASSET_PAIR, 10 * ONE)?; + set_period::(100_000); + + XYKLiquidityMining::::stop_yield_farm(RawOrigin::Signed(caller.clone()).into(), GLOBAL_FARM_ID, ASSET_PAIR)?; + }: { + XYKLiquidityMining::::terminate_yield_farm(RawOrigin::Signed(caller.clone()).into(), GLOBAL_FARM_ID,YIELD_FARM_ID, ASSET_PAIR)? + } + + deposit_shares { + let caller = create_funded_account::("caller", 0); + let xyk_caller = create_funded_account::("xyk_caller", 1); + let liq_provider = create_funded_account::("liq_provider", 2); + + initialize_pool::(xyk_caller, ASSET_PAIR.asset_in, ASSET_PAIR.asset_out, 1_000_000 * ONE, 10_000_000 * ONE)?; + let xyk_id = xykpool::Pallet::::pair_account_from_assets(ASSET_PAIR.asset_in, ASSET_PAIR.asset_out); + xyk_add_liquidity::(liq_provider.clone(), ASSET_PAIR, 10 * ONE, 1_000 * ONE)?; + + lm_create_global_farm::(1_000_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller, GLOBAL_FARM_ID, ASSET_PAIR, FixedU128::one())?; + + lm_deposit_shares::(liq_provider.clone(), ASSET_PAIR, 5 * ONE)?; + set_period::(100_000); + }: { + XYKLiquidityMining::::deposit_shares(RawOrigin::Signed(liq_provider.clone()).into(), GLOBAL_FARM_ID, YIELD_FARM_ID, ASSET_PAIR, 5 * ONE)? + } + + redeposit_shares { + let caller = create_funded_account::("caller", 0); + let xyk_caller = create_funded_account::("xyk_caller", 1); + let liq_provider = create_funded_account::("liq_provider", 2); + let shares_amount = 10 * ONE; + + initialize_pool::(xyk_caller, ASSET_PAIR.asset_in, ASSET_PAIR.asset_out, 1_000_000 * ONE, 10_000_000 * ONE)?; + xyk_add_liquidity::(liq_provider.clone(), ASSET_PAIR, 10 * ONE, 1_000 * ONE)?; + + //global id: 1, yield id: 2 + lm_create_global_farm::(100_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller.clone(), GLOBAL_FARM_ID, ASSET_PAIR, FixedU128::one())?; + + //global id: 3, yield id: 4 + lm_create_global_farm::(100_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller.clone(), GLOBAL_FARM_ID_2, ASSET_PAIR, FixedU128::one())?; + + //global id: 5, yield id:6 + lm_create_global_farm::(100_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller.clone(), 5, ASSET_PAIR, FixedU128::one())?; + + //global id: 7, yield id:8 + lm_create_global_farm::(100_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller.clone(), 7, ASSET_PAIR, FixedU128::one())?; + + //global id: 9, yield id:10 + lm_create_global_farm::(100_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller, 9, ASSET_PAIR, FixedU128::one())?; + + set_period::(200_000); + + XYKLiquidityMining::::deposit_shares(RawOrigin::Signed(liq_provider.clone()).into(), GLOBAL_FARM_ID, YIELD_FARM_ID, ASSET_PAIR, shares_amount)?; + //NOTE: with this redeposits it's like 0.5 µs slower(on my machine) because adding yield + //farm entry into the deposit is doing search on BoundedVec + XYKLiquidityMining::::redeposit_shares(RawOrigin::Signed(liq_provider.clone()).into(), GLOBAL_FARM_ID_2, YIELD_FARM_ID_2, ASSET_PAIR, DEPOSIT_ID)?; + XYKLiquidityMining::::redeposit_shares(RawOrigin::Signed(liq_provider.clone()).into(), 5, 6, ASSET_PAIR, DEPOSIT_ID)?; + XYKLiquidityMining::::redeposit_shares(RawOrigin::Signed(liq_provider.clone()).into(), 7, 8, ASSET_PAIR, DEPOSIT_ID)?; + }: { + XYKLiquidityMining::::redeposit_shares(RawOrigin::Signed(liq_provider.clone()).into(), 9, 10, ASSET_PAIR, DEPOSIT_ID)? + } + + claim_rewards { + let caller = create_funded_account::("caller", 0); + let xyk_caller = create_funded_account::("xyk_caller", 1); + let liq_provider = create_funded_account::("liq_provider", 2); + let shares_amount = 10 * ONE; + + initialize_pool::(xyk_caller, ASSET_PAIR.asset_in, ASSET_PAIR.asset_out, 1_000_000 * ONE, 10_000_000 * ONE)?; + xyk_add_liquidity::(liq_provider.clone(), ASSET_PAIR, 10 * ONE, 1_000 * ONE)?; + + //global id: 1, yield id: 2 + lm_create_global_farm::(100_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller.clone(),GLOBAL_FARM_ID, ASSET_PAIR, FixedU128::one())?; + + //global id: 3, yield id: 4 + lm_create_global_farm::(100_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller.clone(), GLOBAL_FARM_ID_2, ASSET_PAIR, FixedU128::one())?; + + //global id: 5, yield id:6 + lm_create_global_farm::(100_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller.clone(), 5, ASSET_PAIR, FixedU128::one())?; + + //global id: 7, yield id:8 + lm_create_global_farm::(100_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller.clone(), 7, ASSET_PAIR, FixedU128::one())?; + + //global id: 9, yield id:10 + lm_create_global_farm::(100_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller, 9, ASSET_PAIR, FixedU128::one())?; + + set_period::(200_000); + + XYKLiquidityMining::::deposit_shares(RawOrigin::Signed(liq_provider.clone()).into(), GLOBAL_FARM_ID, YIELD_FARM_ID, ASSET_PAIR, shares_amount)?; + XYKLiquidityMining::::redeposit_shares(RawOrigin::Signed(liq_provider.clone()).into(), GLOBAL_FARM_ID_2, YIELD_FARM_ID_2, ASSET_PAIR, DEPOSIT_ID)?; + XYKLiquidityMining::::redeposit_shares(RawOrigin::Signed(liq_provider.clone()).into(), 5, 6, ASSET_PAIR, DEPOSIT_ID)?; + XYKLiquidityMining::::redeposit_shares(RawOrigin::Signed(liq_provider.clone()).into(), 7, 8, ASSET_PAIR, DEPOSIT_ID)?; + XYKLiquidityMining::::redeposit_shares(RawOrigin::Signed(liq_provider.clone()).into(), 9, 10, ASSET_PAIR, DEPOSIT_ID)?; + + set_period::(400_000); + let liq_provider_bsx_balance = MultiCurrencyOf::::free_balance(BSX, &liq_provider); + }: { + XYKLiquidityMining::::claim_rewards(RawOrigin::Signed(liq_provider.clone()).into(), DEPOSIT_ID, 10)? + } verify { + assert!(MultiCurrencyOf::::free_balance(BSX, &liq_provider).gt(&liq_provider_bsx_balance)); + } + + //This benchmark has higher weights than: + // * withdraw_shares with farms removal from storage + // * withdraw_shares without deposit removal from storage + withdraw_shares { + let caller = create_funded_account::("caller", 0); + let xyk_caller = create_funded_account::("xyk_caller", 1); + let liq_provider = create_funded_account::("liq_provider", 2); + let shares_amount = 10 * ONE; + + initialize_pool::(xyk_caller, ASSET_PAIR.asset_in, ASSET_PAIR.asset_out, 1_000_000 * ONE, 10_000_000 * ONE)?; + xyk_add_liquidity::(liq_provider.clone(), ASSET_PAIR, 10 * ONE, 1_000 * ONE)?; + + //global id: 1, yield id: 2 + lm_create_global_farm::(100_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + lm_create_yield_farm::(caller, GLOBAL_FARM_ID, ASSET_PAIR, FixedU128::one())?; + + set_period::(200_000); + + XYKLiquidityMining::::deposit_shares(RawOrigin::Signed(liq_provider.clone()).into(), GLOBAL_FARM_ID, YIELD_FARM_ID, ASSET_PAIR, shares_amount)?; + + set_period::(400_000); + + let liq_provider_bsx_balance = MultiCurrencyOf::::free_balance(BSX, &liq_provider); + }: { + XYKLiquidityMining::::withdraw_shares(RawOrigin::Signed(liq_provider.clone()).into(), DEPOSIT_ID, YIELD_FARM_ID, ASSET_PAIR)? + } verify { + assert!(MultiCurrencyOf::::free_balance(BSX, &liq_provider).gt(&liq_provider_bsx_balance)); + } + + resume_yield_farm { + let caller = create_funded_account::("caller", 0); + let xyk_caller = create_funded_account::("xyk_caller", 1); + let liq_provider = create_funded_account::("liq_provider", 2); + let shares_amount = 10 * ONE; + let bsx_dot = AssetPair { + asset_in: BSX, + asset_out: DOT + }; + + initialize_pool::(xyk_caller.clone(), ASSET_PAIR.asset_in, ASSET_PAIR.asset_out, 1_000_000 * ONE, 10_000_000 * ONE)?; + initialize_pool::(xyk_caller, bsx_dot.asset_in, bsx_dot.asset_out, 1_000_000 * ONE, 10_000_000 * ONE)?; + xyk_add_liquidity::(liq_provider.clone(), bsx_dot, 10 * ONE, 1_000 * ONE)?; + + //global id: 1 + lm_create_global_farm::(100_000 * ONE, caller.clone(), Perquintill::from_percent(20))?; + //yield id: 2 + lm_create_yield_farm::(caller.clone(), GLOBAL_FARM_ID, ASSET_PAIR, FixedU128::one())?; + //yield id: 3 + lm_create_yield_farm::(caller.clone(), GLOBAL_FARM_ID, bsx_dot, FixedU128::one())?; + + XYKLiquidityMining::::stop_yield_farm(RawOrigin::Signed(caller.clone()).into(), GLOBAL_FARM_ID, ASSET_PAIR)?; + + set_period::(200_000); + + XYKLiquidityMining::::deposit_shares(RawOrigin::Signed(liq_provider.clone()).into(), GLOBAL_FARM_ID, 3, bsx_dot, shares_amount)?; + + set_period::(400_000); + let liq_provider_bsx_balance = MultiCurrencyOf::::free_balance(BSX, &liq_provider); + }: { + XYKLiquidityMining::::resume_yield_farm(RawOrigin::Signed(caller.clone()).into(), GLOBAL_FARM_ID, YIELD_FARM_ID, ASSET_PAIR, FixedU128::from(12_452))? + } +} + +#[cfg(test)] +mod tests { + use super::Pallet; + use frame_benchmarking::impl_benchmark_test_suite; + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/pallets/xyk-liquidity-mining/benchmarking/src/mock.rs b/pallets/xyk-liquidity-mining/benchmarking/src/mock.rs new file mode 100644 index 000000000..8bedff222 --- /dev/null +++ b/pallets/xyk-liquidity-mining/benchmarking/src/mock.rs @@ -0,0 +1,355 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2021 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(test)] + +use frame_support::{ + instances::Instance1, + parameter_types, + traits::{AsEnsureOriginWithArg, Everything, Nothing}, + PalletId, +}; + +use frame_system as system; +use frame_system::{EnsureRoot, EnsureSigned}; +use hydradx_traits::{AssetPairAccountIdFor, Source}; +use orml_traits::parameter_type_with_key; +use primitives::{ + constants::{ + chain::{DISCOUNTED_FEE, MAX_IN_RATIO, MAX_OUT_RATIO, MIN_POOL_LIQUIDITY, MIN_TRADING_LIMIT}, + currency::NATIVE_EXISTENTIAL_DEPOSIT, + }, + Amount, AssetId, Balance, +}; + +use pallet_nft::{CollectionType, NftPermissions}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, BlockNumberProvider, IdentityLookup}, + BuildStorage, +}; + +pub const UNITS: Balance = 1_000_000_000_000; + +pub type AccountId = u128; +pub type BlockNumber = u64; + +pub const BSX: AssetId = 0; +pub const KSM: AssetId = 1; +pub const DOT: AssetId = 2; + +pub const LIQ_MINING_NFT_COLLECTION: primitives::CollectionId = 1; + +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + Duster: pallet_duster, + XYK: pallet_xyk, + LiquidityMining: pallet_xyk_liquidity_mining, + NFT: pallet_nft, + Balances: pallet_balances, + Uniques: pallet_uniques, + Currency: orml_tokens, + AssetRegistry: pallet_asset_registry, + WarehouseLM: pallet_liquidity_mining::, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 63; + pub static MockBlockNumberProvider: u64 = 0; + pub const BSXAssetId: AssetId = BSX; + pub ExchangeFeeRate: (u32, u32) = (2, 1_000); + pub RegistryStringLimit: u32 = 100; + pub const SequentialIdOffset: u32 = 1_000_000; +} + +impl BlockNumberProvider for MockBlockNumberProvider { + type BlockNumber = u64; + + fn current_block_number() -> Self::BlockNumber { + System::block_number() + } +} +impl system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl crate::Config for Test {} + +parameter_types! { + pub const WarehouseLMPalletId: PalletId = PalletId(*b"WhouseLm"); + pub const MaxEntriesPerDeposit: u8 = 5; + pub const MaxYieldFarmsPerGlobalFarm: u8 = 5; + pub const MinPlannedYieldingPeriods: BlockNumber = 100; + pub const MinTotalFarmRewards: Balance = 1_000_000; +} + +impl pallet_liquidity_mining::Config for Test { + type AssetId = AssetId; + type MultiCurrency = Currency; + type PalletId = WarehouseLMPalletId; + type MinTotalFarmRewards = MinTotalFarmRewards; + type MinPlannedYieldingPeriods = MinPlannedYieldingPeriods; + type BlockNumberProvider = MockBlockNumberProvider; + type AmmPoolId = AccountId; + type MaxFarmEntriesPerDeposit = MaxEntriesPerDeposit; + type MaxYieldFarmsPerGlobalFarm = MaxYieldFarmsPerGlobalFarm; + type AssetRegistry = AssetRegistry; + type NonDustableWhitelistHandler = Duster; + type RuntimeEvent = RuntimeEvent; + type PriceAdjustment = pallet_liquidity_mining::DefaultPriceAdjustment; +} + +parameter_types! { + pub const LMPalletId: PalletId = PalletId(*b"LiqMinId"); + pub const NftCollection: primitives::CollectionId = LIQ_MINING_NFT_COLLECTION; +} + +impl pallet_xyk_liquidity_mining::Config for Test { + type RuntimeEvent = RuntimeEvent; + type MultiCurrency = Currency; + type CreateOrigin = EnsureRoot; + type PalletId = LMPalletId; + type NftCollectionId = NftCollection; + type AMM = XYK; + type WeightInfo = (); + type NFTHandler = NFT; + type LiquidityMiningHandler = WarehouseLM; + type NonDustableWhitelistHandler = Duster; +} + +impl pallet_duster::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type Amount = Amount; + type CurrencyId = AssetId; + type MultiCurrency = Currency; + type MinCurrencyDeposits = AssetRegistry; + type Reward = (); + type NativeCurrencyId = BSXAssetId; + type BlacklistUpdateOrigin = EnsureRoot; + type WeightInfo = (); +} + +parameter_types! { + pub const ReserveCollectionIdUpTo: u128 = 9999; +} + +impl pallet_nft::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_nft::weights::BasiliskWeight; + type NftCollectionId = primitives::CollectionId; + type NftItemId = primitives::ItemId; + type CollectionType = CollectionType; + type Permissions = NftPermissions; + type ReserveCollectionIdUpTo = ReserveCollectionIdUpTo; +} + +parameter_types! { + pub const ExistentialDeposit: u128 = NATIVE_EXISTENTIAL_DEPOSIT; + pub const MaxReserves: u32 = 50; + pub const MaxLocks: u32 = 1; +} + +impl pallet_balances::Config for Test { + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = frame_system::Pallet; + type MaxLocks = (); + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type MaxHolds = (); + type RuntimeHoldReason = (); +} + +parameter_types! { + pub const CollectionDeposit: Balance = 100 * UNITS; // 100 UNITS deposit to create asset class + pub const ItemDeposit: Balance = 100 * UNITS; // 100 UNITS deposit to create asset instance + pub const KeyLimit: u32 = 256; // Max 256 bytes per key + pub const ValueLimit: u32 = 1024; // Max 1024 bytes per value + pub const UniquesMetadataDepositBase: Balance = 100 * UNITS; + pub const AttributeDepositBase: Balance = 10 * UNITS; + pub const DepositPerByte: Balance = UNITS; +} + +impl pallet_uniques::Config for Test { + type RuntimeEvent = RuntimeEvent; + type CollectionId = primitives::CollectionId; + type ItemId = primitives::ItemId; + type Currency = Balances; + type ForceOrigin = frame_system::EnsureRoot; + type CollectionDeposit = CollectionDeposit; + type ItemDeposit = ItemDeposit; + type MetadataDepositBase = UniquesMetadataDepositBase; + type AttributeDepositBase = AttributeDepositBase; + type DepositPerByte = DepositPerByte; + type StringLimit = primitives::UniquesStringLimit; + type KeyLimit = KeyLimit; + type ValueLimit = ValueLimit; + type CreateOrigin = AsEnsureOriginWithArg>; + type Locker = (); + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type Helper = (); +} + +parameter_type_with_key! { + pub ExistentialDeposits: |_currency_id: AssetId| -> Balance { + 1u128 + }; +} + +impl orml_tokens::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type Amount = Amount; + type CurrencyId = AssetId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type MaxLocks = MaxLocks; + type DustRemovalWhitelist = Nothing; + type MaxReserves = MaxReserves; + type ReserveIdentifier = (); + type CurrencyHooks = (); +} + +#[derive(Default)] +pub struct ExtBuilder { + endowed_accounts: Vec<(AccountId, AssetId, Balance)>, +} + +pub struct AssetPairAccountIdTest(); + +impl AssetPairAccountIdFor for AssetPairAccountIdTest { + fn from_assets(asset_a: AssetId, asset_b: AssetId, _: &str) -> AccountId { + let mut a = asset_a as u128; + let mut b = asset_b as u128; + if a > b { + std::mem::swap(&mut a, &mut b) + } + (a * 1000 + b) as AccountId + } +} + +impl pallet_asset_registry::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RegistryOrigin = EnsureSigned; + type AssetId = AssetId; + type Balance = Balance; + type AssetNativeLocation = u8; + type StringLimit = RegistryStringLimit; + type SequentialIdStartAt = SequentialIdOffset; + type NativeAssetId = BSXAssetId; + type WeightInfo = (); +} + +parameter_types! { + pub const MinTradingLimit: Balance = MIN_TRADING_LIMIT; + pub const MinPoolLiquidity: Balance = MIN_POOL_LIQUIDITY; + pub const MaxInRatio: u128 = MAX_IN_RATIO; + pub const MaxOutRatio: u128 = MAX_OUT_RATIO; + pub const DiscountedFee: (u32, u32) = DISCOUNTED_FEE; + pub const XYKOracleSourceIdentifier: Source = *b"snek/xyk"; +} + +impl pallet_xyk::Config for Test { + type RuntimeEvent = RuntimeEvent; + type AssetRegistry = AssetRegistry; + type AssetPairAccountId = AssetPairAccountIdTest; + type Currency = Currency; + type NativeAssetId = BSXAssetId; + type WeightInfo = (); + type GetExchangeFee = ExchangeFeeRate; + type MinTradingLimit = MinTradingLimit; + type MinPoolLiquidity = MinPoolLiquidity; + type MaxInRatio = MaxInRatio; + type MaxOutRatio = MaxOutRatio; + type OracleSource = XYKOracleSourceIdentifier; + type CanCreatePool = pallet_xyk::AllowAllPools; + type AMMHandler = (); + type DiscountedFee = DiscountedFee; + type NonDustableWhitelistHandler = Duster; +} + +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, + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_asset_registry::GenesisConfig:: { + registered_assets: vec![(b"KSM".to_vec(), 1_000, Some(KSM)), (b"DOT".to_vec(), 1_000, Some(DOT))], + native_asset_name: b"BSX".to_vec(), + native_existential_deposit: 1_000_000_000_000, + } + .assimilate_storage(&mut t) + .unwrap(); + + as BuildStorage>::assimilate_storage( + &pallet_xyk_liquidity_mining::GenesisConfig::::default(), + &mut t, + ) + .unwrap(); + + t.into() + } +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut ext = ExtBuilder::default().build(); + ext.execute_with(|| { + System::set_block_number(1); + }); + + ext +} diff --git a/pallets/xyk-liquidity-mining/src/lib.rs b/pallets/xyk-liquidity-mining/src/lib.rs new file mode 100644 index 000000000..52ff4e289 --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/lib.rs @@ -0,0 +1,962 @@ +// This file is part of HydraDX + +// Copyright (C) 2020-2022 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. + +//! ## Overview +//! +//! This pallet provides functionality for liquidity mining programs with time incentive(loyalty +//! factor) and multiple incentives scheme for XYK pools. +//! Users are rewarded for each period they stay in liq. mining program. +//! +//! Reward per one period is derived from the user's loyalty factor which grows with time(periods) +//! the user is in the liq. mining and amount of LP shares user locked into deposit. +//! User's loyalty factor is reset if the user exits and reenters liquidity mining. +//! User can claim rewards without resetting loyalty factor, only withdrawing shares +//! is penalized by loyalty factor reset. +//! User is rewarded from the next period after he enters. +//! +//! Multiple Incentives +//! +//! This feature allow users to redeposit already deposited LP shares to multiple yield farms and +//! receive incentives from this farms. +//! LP shares can be redeposited only to different yield farms running liquidity mining for same +//! pair of assets. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(test)] +mod tests; + +pub mod migration; +pub mod weights; + +pub use pallet::*; + +use frame_support::traits::tokens::nonfungibles::{Create, Inspect, Mutate, Transfer}; +use frame_support::{ensure, sp_runtime::traits::Zero, PalletId}; +use frame_system::pallet_prelude::BlockNumberFor; +use hydradx_traits::liquidity_mining::{ + GlobalFarmId, Inspect as LiquidityMiningInspect, Mutate as LiquidityMiningMutate, YieldFarmId, +}; +use pallet_liquidity_mining::{FarmMultiplier, LoyaltyCurve}; +use pallet_xyk::types::{AssetId, AssetPair, Balance}; + +use frame_support::{pallet_prelude::*, sp_runtime::traits::AccountIdConversion}; +use frame_system::{ensure_signed, pallet_prelude::OriginFor}; +use hydradx_traits::{registry::Inspect as RegistryInspect, AMMPosition, AMM}; +use orml_traits::MultiCurrency; +use primitives::{CollectionId, ItemId as DepositId}; +use sp_arithmetic::{FixedU128, Perquintill}; +use sp_std::{ + convert::{From, Into, TryInto}, + vec, +}; + +type PeriodOf = BlockNumberFor; + +#[frame_support::pallet] +#[allow(clippy::too_many_arguments)] +pub mod pallet { + use super::*; + use crate::weights::WeightInfo; + use frame_system::pallet_prelude::BlockNumberFor; + use hydradx_traits::pools::DustRemovalAccountWhitelist; + + const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] + pub struct GenesisConfig { + #[serde(skip)] + pub _marker: PhantomData, + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) { + let pallet_account = >::account_id(); + + T::NonDustableWhitelistHandler::add_account(&pallet_account).unwrap(); + + ::NFTHandler::create_collection( + &::NFTCollectionId::get(), + &pallet_account, + &pallet_account, + ) + .unwrap() + } + } + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// Currency for transfers. + type Currencies: MultiCurrency; + + /// AMM helper functions. + type AMM: AMM + + AMMPosition; + + /// The origin account that can create new liquidity mining program. + type CreateOrigin: EnsureOrigin; + + /// Pallet id. + type PalletId: Get; + + /// NFT collection id for liquidity mining's deposit nfts. + #[pallet::constant] + type NFTCollectionId: Get; + + /// Non fungible handling + type NFTHandler: Mutate + + Create + + Inspect + + Transfer; + + /// Liquidity mining handler for managing liquidity mining functionalities + type LiquidityMiningHandler: LiquidityMiningMutate< + Self::AccountId, + AssetId, + BlockNumberFor, + Error = DispatchError, + AmmPoolId = Self::AccountId, + Balance = Balance, + LoyaltyCurve = LoyaltyCurve, + Period = PeriodOf, + > + LiquidityMiningInspect; + + /// Account whitelist manager to exclude pool accounts from dusting mechanism. + type NonDustableWhitelistHandler: DustRemovalAccountWhitelist; + + /// AssetRegistry used to retrieve information about asset. + type AssetRegistry: RegistryInspect; + + /// Weight information for extrinsic in this module. + type WeightInfo: WeightInfo; + } + + #[pallet::error] + #[cfg_attr(test, derive(PartialEq, Eq))] + pub enum Error { + /// Nft pallet didn't return an owner. + CantFindDepositOwner, + + /// Account balance of XYK pool shares is not sufficient. + InsufficientXykSharesBalance, + + /// XYK pool does not exist + XykPoolDoesntExist, + + /// Account is not deposit owner. + NotDepositOwner, + + /// XYK did not return assets for given pool id + // Not tested because previous checks in the code prevents this error + CantGetXykAssets, + + ///Deposit data not found + DepositDataNotFound, + + /// Calculated reward to claim is 0. + ZeroClaimedRewards, + + /// Asset is not in the `AssetPair`. + AssetNotInAssetPair, + + /// Provided `AssetPair` is not used by the deposit. + InvalidAssetPair, + + /// Asset is not registered in asset registry. + AssetNotRegistered, + + /// Failed to calculate `pot`'s account. + FailToGetPotId, + } + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event { + /// New global farm was created. + GlobalFarmCreated { + id: GlobalFarmId, + owner: T::AccountId, + total_rewards: Balance, + reward_currency: AssetId, + yield_per_period: Perquintill, + planned_yielding_periods: PeriodOf, + blocks_per_period: BlockNumberFor, + incentivized_asset: AssetId, + max_reward_per_period: Balance, + min_deposit: Balance, + price_adjustment: FixedU128, + }, + + /// Global farm's `price_adjustment` was updated. + GlobalFarmUpdated { + id: GlobalFarmId, + price_adjustment: FixedU128, + }, + + /// New yield farm was added into the farm. + YieldFarmCreated { + global_farm_id: GlobalFarmId, + yield_farm_id: YieldFarmId, + multiplier: FarmMultiplier, + asset_pair: AssetPair, + loyalty_curve: Option, + }, + + /// Global farm was terminated. + GlobalFarmTerminated { + global_farm_id: GlobalFarmId, + who: T::AccountId, + reward_currency: AssetId, + undistributed_rewards: Balance, + }, + + /// New LP tokens was deposited. + SharesDeposited { + global_farm_id: GlobalFarmId, + yield_farm_id: YieldFarmId, + who: T::AccountId, + amount: Balance, + lp_token: AssetId, + deposit_id: DepositId, + }, + + /// LP token was redeposited for a new yield farm entry + SharesRedeposited { + global_farm_id: GlobalFarmId, + yield_farm_id: YieldFarmId, + who: T::AccountId, + amount: Balance, + lp_token: AssetId, + deposit_id: DepositId, + }, + + /// Rewards was claimed. + RewardClaimed { + global_farm_id: GlobalFarmId, + yield_farm_id: YieldFarmId, + who: T::AccountId, + claimed: Balance, + reward_currency: AssetId, + deposit_id: DepositId, + }, + + /// LP tokens was withdrawn. + SharesWithdrawn { + global_farm_id: GlobalFarmId, + yield_farm_id: YieldFarmId, + who: T::AccountId, + lp_token: AssetId, + amount: Balance, + deposit_id: DepositId, + }, + + /// Yield farm for asset pair was stopped. + YieldFarmStopped { + global_farm_id: GlobalFarmId, + yield_farm_id: YieldFarmId, + who: T::AccountId, + asset_pair: AssetPair, + }, + + /// Yield farm for asset pair was resumed. + YieldFarmResumed { + global_farm_id: GlobalFarmId, + yield_farm_id: YieldFarmId, + who: T::AccountId, + asset_pair: AssetPair, + multiplier: FarmMultiplier, + }, + + /// Yield farm was terminated from global farm. + YieldFarmTerminated { + global_farm_id: GlobalFarmId, + yield_farm_id: YieldFarmId, + who: T::AccountId, + asset_pair: AssetPair, + }, + + /// Yield farm multiplier was updated. + YieldFarmUpdated { + global_farm_id: GlobalFarmId, + yield_farm_id: YieldFarmId, + who: T::AccountId, + asset_pair: AssetPair, + multiplier: FarmMultiplier, + }, + + /// NFT representing deposit has been destroyed + DepositDestroyed { who: T::AccountId, deposit_id: DepositId }, + } + + #[pallet::call] + impl Pallet { + /// Create new liquidity mining program with provided parameters. + /// + /// `owner` account has to have at least `total_rewards` balance. This fund will be + /// transferred from `owner` to farm account. + /// In case of `reward_currency` is insufficient asset, farm's `owner` has to pay existential + /// deposit for global farm account and for liquidity mining `pot` account. + /// + /// The dispatch origin for this call must be `T::CreateOrigin`. + /// !!!WARN: `T::CreateOrigin` has power over funds of `owner`'s account and it should be + /// configured to trusted origin e.g Sudo or Governance. + /// + /// Parameters: + /// - `origin`: global farm's owner. + /// - `total_rewards`: total rewards planned to distribute. This rewards will be + /// distributed between all yield farms in the global farm. + /// - `planned_yielding_periods`: planned number of periods to distribute `total_rewards`. + /// WARN: THIS IS NOT HARD DEADLINE. Not all rewards have to be distributed in + /// `planned_yielding_periods`. Rewards are distributed based on the situation in the yield + /// farms and can be distributed in a longer time frame but never in the shorter time frame. + /// - `blocks_per_period`: number of blocks in a single period. Min. number of blocks per + /// period is 1. + /// - `incentivized_asset`: asset to be incentivized in XYK pools. All yield farms added into + /// liq. mining program have to have `incentivized_asset` in their pair. + /// - `reward_currency`: payoff currency of rewards. + /// - `owner`: liq. mining program owner. + /// - `yield_per_period`: percentage return on `reward_currency` of all farms p.a. + /// - `min_deposit`: minimum amount which can be deposited to the farm + /// - `price_adjustment`: + /// Emits `GlobalFarmCreated` event when successful. + #[pallet::call_index(0)] + #[pallet::weight(::WeightInfo::create_global_farm())] + pub fn create_global_farm( + origin: OriginFor, + total_rewards: Balance, + planned_yielding_periods: PeriodOf, + blocks_per_period: BlockNumberFor, + incentivized_asset: AssetId, + reward_currency: AssetId, + owner: T::AccountId, + yield_per_period: Perquintill, + min_deposit: Balance, + price_adjustment: FixedU128, + ) -> DispatchResult { + ::CreateOrigin::ensure_origin(origin)?; + + if !T::AssetRegistry::is_sufficient(reward_currency) { + let ed = + T::AssetRegistry::existential_deposit(reward_currency).ok_or(Error::::AssetNotRegistered)?; + + let pot = T::LiquidityMiningHandler::pot_account().ok_or(Error::::FailToGetPotId)?; + T::Currencies::transfer(reward_currency, &owner, &pot, ed)?; + } + + let (id, max_reward_per_period) = T::LiquidityMiningHandler::create_global_farm( + total_rewards, + planned_yielding_periods, + blocks_per_period, + incentivized_asset, + reward_currency, + owner.clone(), + yield_per_period, + min_deposit, + price_adjustment, + )?; + + Self::deposit_event(Event::GlobalFarmCreated { + id, + owner, + total_rewards, + reward_currency, + yield_per_period, + planned_yielding_periods, + blocks_per_period, + incentivized_asset, + max_reward_per_period, + min_deposit, + price_adjustment, + }); + + Ok(()) + } + + /// Update global farm's prices adjustment. + /// + /// Only farm's owner can perform this action. + /// + /// Parameters: + /// - `origin`: global farm's owner. + /// - `global_farm_id`: id of the global farm to update + /// - `price_adjustment`: new value for price adjustment + /// + /// Emits `GlobalFarmUpdated` event when successful. + #[pallet::call_index(1)] + #[pallet::weight(::WeightInfo::update_global_farm())] + pub fn update_global_farm( + origin: OriginFor, + global_farm_id: GlobalFarmId, + price_adjustment: FixedU128, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + T::LiquidityMiningHandler::update_global_farm_price_adjustment(who, global_farm_id, price_adjustment)?; + + Self::deposit_event(Event::GlobalFarmUpdated { + id: global_farm_id, + price_adjustment, + }); + + Ok(()) + } + + /// Terminate existing liq. mining program. + /// + /// Only farm owner can perform this action. + /// + /// WARN: To successfully terminate a farm, farm have to be empty(all yield farms in he global farm must be terminated). + /// + /// Parameters: + /// - `origin`: global farm's owner. + /// - `global_farm_id`: id of global farm to be terminated. + /// + /// Emits `GlobalFarmTerminated` event when successful. + #[pallet::call_index(2)] + #[pallet::weight(::WeightInfo::terminate_global_farm())] + pub fn terminate_global_farm(origin: OriginFor, global_farm_id: GlobalFarmId) -> DispatchResult { + let who = ensure_signed(origin)?; + + let (reward_currency, undistributed_rewards, who) = + T::LiquidityMiningHandler::terminate_global_farm(who, global_farm_id)?; + + Self::deposit_event(Event::GlobalFarmTerminated { + global_farm_id, + who, + reward_currency, + undistributed_rewards, + }); + Ok(()) + } + + /// Add yield farm for given `asset_pair` XYK pool. + /// + /// Only farm owner can perform this action. + /// + /// Only XYKs with `asset_pair` with `incentivized_asset` can be added into the farm. XYK + /// pool for `asset_pair` has to exist to successfully create yield farm. + /// Yield farm for same `asset_pair` can exist only once in the global farm. + /// + /// Parameters: + /// - `origin`: global farm's owner. + /// - `farm_id`: global farm id to which a yield farm will be added. + /// - `asset_pair`: asset pair identifying yield farm. Liq. mining will be allowed for this + /// `asset_pair` and one of the assets in the pair must be `incentivized_asset`. + /// - `multiplier`: yield farm multiplier. + /// - `loyalty_curve`: curve to calculate loyalty multiplier to distribute rewards to users + /// with time incentive. `None` means no loyalty multiplier. + /// + /// Emits `YieldFarmCreated` event when successful. + #[pallet::call_index(3)] + #[pallet::weight(::WeightInfo::create_yield_farm())] + pub fn create_yield_farm( + origin: OriginFor, + global_farm_id: GlobalFarmId, + asset_pair: AssetPair, + multiplier: FarmMultiplier, + loyalty_curve: Option, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let amm_pool_id = Self::ensure_xyk(asset_pair)?; + + let yield_farm_id = T::LiquidityMiningHandler::create_yield_farm( + who, + global_farm_id, + multiplier, + loyalty_curve.clone(), + amm_pool_id, + vec![asset_pair.asset_in, asset_pair.asset_out], + )?; + + Self::deposit_event(Event::YieldFarmCreated { + global_farm_id, + yield_farm_id, + multiplier, + loyalty_curve, + asset_pair, + }); + + Ok(()) + } + + /// Update yield farm multiplier. + /// + /// Only farm owner can perform this action. + /// + /// Parameters: + /// - `origin`: global farm's owner. + /// - `global_farm_id`: global farm id in which yield farm will be updated. + /// - `asset_pair`: asset pair identifying yield farm in global farm. + /// - `multiplier`: new yield farm multiplier. + /// + /// Emits `YieldFarmUpdated` event when successful. + #[pallet::call_index(4)] + #[pallet::weight(::WeightInfo::update_yield_farm())] + pub fn update_yield_farm( + origin: OriginFor, + global_farm_id: GlobalFarmId, + asset_pair: AssetPair, + multiplier: FarmMultiplier, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let amm_pool_id = Self::ensure_xyk(asset_pair)?; + + let yield_farm_id = T::LiquidityMiningHandler::update_yield_farm_multiplier( + who.clone(), + global_farm_id, + amm_pool_id, + multiplier, + )?; + + Self::deposit_event(Event::YieldFarmUpdated { + global_farm_id, + yield_farm_id, + multiplier, + who, + asset_pair, + }); + + Ok(()) + } + + /// Stop liq. miming for specific yield farm. + /// + /// This function claims rewards from `GlobalFarm` last time and stops yield farm + /// incentivization from a `GlobalFarm`. Users will be able to only withdraw + /// shares(with claiming) after calling this function. + /// `deposit_shares()` and `claim_rewards()` are not allowed on canceled yield farm. + /// + /// Only farm owner can perform this action. + /// + /// Parameters: + /// - `origin`: global farm's owner. + /// - `global_farm_id`: farm id in which yield farm will be canceled. + /// - `asset_pair`: asset pair identifying yield farm in the farm. + /// + /// Emits `YieldFarmStopped` event when successful. + #[pallet::call_index(5)] + #[pallet::weight(::WeightInfo::stop_yield_farm())] + pub fn stop_yield_farm( + origin: OriginFor, + global_farm_id: GlobalFarmId, + asset_pair: AssetPair, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + //NOTE: don't check XYK pool existance, owner must be able to stop yield farm. + let amm_pool_id = T::AMM::get_pair_id(asset_pair); + let yield_farm_id = T::LiquidityMiningHandler::stop_yield_farm(who.clone(), global_farm_id, amm_pool_id)?; + + Self::deposit_event(Event::YieldFarmStopped { + global_farm_id, + yield_farm_id, + who, + asset_pair, + }); + + Ok(()) + } + + /// Resume yield farm for stopped yield farm. + /// + /// This function resume incentivization from `GlobalFarm` and restore full functionality + /// for yield farm. Users will be able to deposit, claim and withdraw again. + /// + /// WARN: Yield farm is NOT rewarded for time it was stopped. + /// + /// Only farm owner can perform this action. + /// + /// Parameters: + /// - `origin`: global farm's owner. + /// - `global_farm_id`: global farm id in which yield farm will be resumed. + /// - `yield_farm_id`: id of yield farm to be resumed. + /// - `asset_pair`: asset pair identifying yield farm in global farm. + /// - `multiplier`: yield farm multiplier in the farm. + /// + /// Emits `YieldFarmResumed` event when successful. + #[pallet::call_index(6)] + #[pallet::weight(::WeightInfo::resume_yield_farm())] + pub fn resume_yield_farm( + origin: OriginFor, + global_farm_id: GlobalFarmId, + yield_farm_id: YieldFarmId, + asset_pair: AssetPair, + multiplier: FarmMultiplier, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let amm_pool_id = Self::ensure_xyk(asset_pair)?; + + T::LiquidityMiningHandler::resume_yield_farm( + who.clone(), + global_farm_id, + yield_farm_id, + amm_pool_id, + multiplier, + )?; + + Self::deposit_event(Event::::YieldFarmResumed { + global_farm_id, + yield_farm_id, + who, + asset_pair, + multiplier, + }); + + Ok(()) + } + + /// Remove yield farm + /// + /// This function marks a yield farm as ready to be removed from storage when it's empty. Users will + /// be able to only withdraw shares(without claiming rewards from yield farm). Unpaid rewards + /// will be transferred back to global farm and will be used to distribute to other yield farms. + /// + /// Yield farm must be stopped before calling this function. + /// + /// Only global farm's owner can perform this action. Yield farm stays in the storage until it's + /// empty(all farm entries are withdrawn). Last withdrawn from yield farm trigger removing from + /// the storage. + /// + /// Parameters: + /// - `origin`: global farm's owner. + /// - `global_farm_id`: farm id from which yield farm should be terminated. + /// - `yield_farm_id`: id of yield farm to be terminated. + /// - `asset_pair`: asset pair identifying yield farm in the global farm. + /// + /// Emits `YieldFarmTerminated` event when successful. + #[pallet::call_index(7)] + #[pallet::weight(::WeightInfo::terminate_yield_farm())] + pub fn terminate_yield_farm( + origin: OriginFor, + global_farm_id: GlobalFarmId, + yield_farm_id: YieldFarmId, + asset_pair: AssetPair, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + //NOTE: don't check XYK pool existance, owner must be able to stop yield farm. + let amm_pool_id = T::AMM::get_pair_id(asset_pair); + + T::LiquidityMiningHandler::terminate_yield_farm(who.clone(), global_farm_id, yield_farm_id, amm_pool_id)?; + + Self::deposit_event(Event::YieldFarmTerminated { + global_farm_id, + yield_farm_id, + who, + asset_pair, + }); + + Ok(()) + } + + /// Deposit LP shares to a liq. mining. + /// + /// This function transfers LP shares from `origin` to pallet's account and mint nft for + /// `origin` account. Minted nft represents deposit in the liq. mining. + /// + /// Parameters: + /// - `origin`: account depositing LP shares. This account has to have at least + /// `shares_amount` of LP shares. + /// - `global_farm_id`: id of global farm to which user wants to deposit LP shares. + /// - `yield_farm_id`: id of yield farm to deposit to. + /// - `asset_pair`: asset pair identifying LP shares user wants to deposit. + /// - `shares_amount`: amount of LP shares user wants to deposit. + /// + /// Emits `SharesDeposited` event when successful. + #[pallet::call_index(8)] + #[pallet::weight(::WeightInfo::deposit_shares())] + pub fn deposit_shares( + origin: OriginFor, + global_farm_id: GlobalFarmId, + yield_farm_id: YieldFarmId, + asset_pair: AssetPair, + shares_amount: Balance, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let amm_pool_id = Self::ensure_xyk(asset_pair)?; + + let amm_share_token = T::AMM::get_share_token(asset_pair); + + ensure!( + T::Currencies::ensure_can_withdraw(amm_share_token, &who, shares_amount).is_ok(), + Error::::InsufficientXykSharesBalance + ); + + let deposit_id = T::LiquidityMiningHandler::deposit_lp_shares( + global_farm_id, + yield_farm_id, + amm_pool_id, + shares_amount, + Self::get_token_value_of_lp_shares, + )?; + + Self::lock_lp_tokens(amm_share_token, &who, shares_amount)?; + T::NFTHandler::mint_into(&T::NFTCollectionId::get(), &deposit_id, &who)?; + + Self::deposit_event(Event::SharesDeposited { + global_farm_id, + yield_farm_id, + who, + amount: shares_amount, + lp_token: amm_share_token, + deposit_id, + }); + + Ok(()) + } + + /// Redeposit already locked LP shares to another yield farm. + /// + /// This function create yield farm entry for existing deposit. LP shares are not transferred + /// and amount of LP shares is based on existing deposit. + /// + /// This function DOESN'T create new deposit. + /// + /// Parameters: + /// - `origin`: account depositing LP shares. This account have to have at least + /// - `global_farm_id`: global farm identifier. + /// - `yield_farm_id`: yield farm identifier redepositing to. + /// - `asset_pair`: asset pair identifying LP shares user want to deposit. + /// - `deposit_id`: identifier of the deposit. + /// + /// Emits `SharesRedeposited` event when successful. + #[pallet::call_index(9)] + #[pallet::weight(::WeightInfo::redeposit_shares())] + pub fn redeposit_shares( + origin: OriginFor, + global_farm_id: GlobalFarmId, + yield_farm_id: YieldFarmId, + asset_pair: AssetPair, + deposit_id: DepositId, + ) -> DispatchResult { + let owner = Self::ensure_nft_owner(origin, deposit_id)?; + let amm_pool_id = Self::ensure_xyk(asset_pair)?; + + let amm_share_token = T::AMM::get_share_token(asset_pair); + + let (shares_amount, deposit_amm_pool_id) = T::LiquidityMiningHandler::redeposit_lp_shares( + global_farm_id, + yield_farm_id, + deposit_id, + Self::get_token_value_of_lp_shares, + )?; + + ensure!(amm_pool_id == deposit_amm_pool_id, Error::::InvalidAssetPair); + + Self::deposit_event(Event::SharesRedeposited { + global_farm_id, + yield_farm_id, + who: owner, + amount: shares_amount, + lp_token: amm_share_token, + deposit_id, + }); + + Ok(()) + } + + /// Claim rewards from liq. mining for deposit represented by `nft_id`. + /// + /// This function calculate user rewards from liq. mining and transfer rewards to `origin` + /// account. Claiming in the same period is allowed only once. + /// + /// Parameters: + /// - `origin`: account owner of deposit(nft). + /// - `deposit_id`: nft id representing deposit in the yield farm. + /// - `yield_farm_id`: yield farm identifier to claim rewards from. + /// + /// Emits `RewardClaimed` event when successful. + #[pallet::call_index(10)] + #[pallet::weight(::WeightInfo::claim_rewards())] + pub fn claim_rewards( + origin: OriginFor, + deposit_id: DepositId, + yield_farm_id: YieldFarmId, + ) -> DispatchResult { + let owner = Self::ensure_nft_owner(origin, deposit_id)?; + + let (global_farm_id, reward_currency, claimed, _) = + T::LiquidityMiningHandler::claim_rewards(owner.clone(), deposit_id, yield_farm_id)?; + + ensure!(!claimed.is_zero(), Error::::ZeroClaimedRewards); + + Self::deposit_event(Event::RewardClaimed { + global_farm_id, + yield_farm_id, + who: owner, + claimed, + reward_currency, + deposit_id, + }); + + Ok(()) + } + + /// Withdraw LP shares from liq. mining with reward claiming if possible. + /// + /// List of possible cases of transfers of LP shares and claimed rewards: + /// + /// * yield farm is active(yield farm is not stopped) - claim and transfer rewards(if it + /// wasn't claimed in this period) and transfer LP shares. + /// * liq. mining is stopped - claim and transfer rewards(if it + /// wasn't claimed in this period) and transfer LP shares. + /// * yield farm was terminated - only LP shares will be transferred. + /// * farm was terminated - only LP shares will be transferred. + /// + /// User's unclaimable rewards will be transferred back to global farm's account. + /// + /// Parameters: + /// - `origin`: account owner of deposit(nft). + /// - `deposit_id`: nft id representing deposit in the yield farm. + /// - `yield_farm_id`: yield farm identifier to dithdraw shares from. + /// - `asset_pair`: asset pair identifying yield farm in global farm. + /// + /// Emits: + /// * `RewardClaimed` if claim happen + /// * `SharesWithdrawn` event when successful + #[pallet::call_index(11)] + #[pallet::weight(::WeightInfo::withdraw_shares())] + pub fn withdraw_shares( + origin: OriginFor, + deposit_id: DepositId, + yield_farm_id: YieldFarmId, + asset_pair: AssetPair, + ) -> DispatchResult { + let owner = Self::ensure_nft_owner(origin, deposit_id)?; + let amm_pool_id = Self::ensure_xyk(asset_pair)?; + + let global_farm_id = T::LiquidityMiningHandler::get_global_farm_id(deposit_id, yield_farm_id) + .ok_or(Error::::DepositDataNotFound)?; + + let (withdrawn_amount, claim_data, is_destroyed) = T::LiquidityMiningHandler::withdraw_lp_shares( + owner.clone(), + deposit_id, + global_farm_id, + yield_farm_id, + amm_pool_id.clone(), + )?; + + if let Some((reward_currency, claimed, _)) = claim_data { + if !claimed.is_zero() { + Self::deposit_event(Event::RewardClaimed { + global_farm_id, + yield_farm_id, + who: owner.clone(), + claimed, + reward_currency, + deposit_id, + }); + } + } + + let lp_token = Self::get_lp_token(&amm_pool_id)?; + if !withdrawn_amount.is_zero() { + Self::deposit_event(Event::SharesWithdrawn { + global_farm_id, + yield_farm_id, + who: owner.clone(), + lp_token, + amount: withdrawn_amount, + deposit_id, + }); + } + + if is_destroyed { + Self::unlock_lp_tokens(lp_token, &owner, withdrawn_amount)?; + T::NFTHandler::burn(&T::NFTCollectionId::get(), &deposit_id, Some(&owner))?; + + Self::deposit_event(Event::DepositDestroyed { who: owner, deposit_id }); + } + + Ok(()) + } + } +} + +impl Pallet { + /// Account ID of the pot holding locked LP shares. This account is also owner of NFT class + /// for all the NFTs minted by this pallet. + pub fn account_id() -> T::AccountId { + ::PalletId::get().into_account_truncating() + } + + fn get_lp_token(amm_pool_id: &T::AccountId) -> Result> { + let assets = T::AMM::get_pool_assets(amm_pool_id).ok_or(Error::::CantGetXykAssets)?; + let asset_pair = AssetPair::new(assets[0], assets[1]); + + //NOTE: this check is important AMM:get_share_token() return `0` if amm doesn't exist + ensure!(T::AMM::exists(asset_pair), Error::::XykPoolDoesntExist); + + Ok(T::AMM::get_share_token(asset_pair)) + } + + fn lock_lp_tokens(lp_token: AssetId, who: &T::AccountId, amount: Balance) -> Result<(), DispatchError> { + let service_account_for_lp_shares = Self::account_id(); + + T::Currencies::transfer(lp_token, who, &service_account_for_lp_shares, amount) + } + + fn unlock_lp_tokens(lp_token: AssetId, who: &T::AccountId, amount: Balance) -> Result<(), DispatchError> { + let service_account_for_lp_shares = Self::account_id(); + + T::Currencies::transfer(lp_token, &service_account_for_lp_shares, who, amount) + } + + /// This function retuns value of lp tokens in the `asset` currency. + fn get_token_value_of_lp_shares( + asset: AssetId, + amm_pool_id: T::AccountId, + lp_shares_amount: Balance, + ) -> Result { + let assets = T::AMM::get_pool_assets(&amm_pool_id).ok_or(Error::::CantGetXykAssets)?; + + ensure!(assets.contains(&asset), Error::::AssetNotInAssetPair); + + let (liquidity_a, liquidity_b) = T::AMM::get_liquidity_behind_shares(assets[0], assets[1], lp_shares_amount)?; + + if assets[0] == asset { + return Ok(liquidity_a); + } + + Ok(liquidity_b) + } + + fn ensure_xyk(asset_pair: AssetPair) -> Result> { + ensure!(T::AMM::exists(asset_pair), Error::::XykPoolDoesntExist); + + Ok(T::AMM::get_pair_id(asset_pair)) + } + + fn ensure_nft_owner(origin: OriginFor, deposit_id: DepositId) -> Result { + let who = ensure_signed(origin)?; + + let nft_owner = + T::NFTHandler::owner(&T::NFTCollectionId::get(), &deposit_id).ok_or(Error::::CantFindDepositOwner)?; + + ensure!(nft_owner == who, Error::::NotDepositOwner); + + Ok(who) + } +} diff --git a/pallets/xyk-liquidity-mining/src/migration.rs b/pallets/xyk-liquidity-mining/src/migration.rs new file mode 100644 index 000000000..793218376 --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/migration.rs @@ -0,0 +1,77 @@ +// 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. + +use super::*; +use frame_support::{ + traits::{Get, StorageVersion}, + weights::Weight, +}; + +/// Migrate the pallet storage to v1. This migration creates NFT collection for xyk's +/// liquidity mining. +pub fn migrate_to_v1() -> frame_support::weights::Weight { + let on_chain_storage_version = StorageVersion::get::>(); + //offset for storage version read + let mut weight: Weight = T::DbWeight::get().reads(1); + + log::info!( + target: "runtime::xyk-liquidity-mining", + "Running migration storage v1 for xyk-liquidity-mining with storage version {:?}", + on_chain_storage_version, + ); + + if on_chain_storage_version < 1 { + let pallet_account = >::account_id(); + match ::NFTHandler::create_collection( + &::NFTCollectionId::get(), + &pallet_account, + &pallet_account, + ) { + Ok(_) => { + weight = weight + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(2)); + + StorageVersion::new(1).put::>(); + //add storage version update weight + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + target: "runtime::xyk-liquidity-mining", + "Running migration storage v1 for xyk-liquidity-mining with storage version {:?} was complete", + on_chain_storage_version, + ); + } + Err(e) => { + log::error!( + target: "runtime: xyk-liquidity-mining", + "Error to create NFT collection: {:?}", + e + ); + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + } + }; + + // return migration weights + weight + } else { + log::warn!( + target: "runtime::xyk-liquidity-mining", + "Attempted to apply migration to v1 but failed because storage version is {:?}", + on_chain_storage_version, + ); + weight + } +} diff --git a/pallets/xyk-liquidity-mining/src/tests/claim_rewards.rs b/pallets/xyk-liquidity-mining/src/tests/claim_rewards.rs new file mode 100644 index 000000000..9bcc494a0 --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/tests/claim_rewards.rs @@ -0,0 +1,186 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2022 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. + +use super::*; + +#[test] +fn claim_rewards_should_work_when_deposit_exist() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 2, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + //Arrange + set_block_number(10_000); + + //Act + assert_ok!(LiquidityMining::claim_rewards(Origin::signed(CHARLIE), 1, 2)); + + //Assert + assert_last_event!(crate::Event::RewardClaimed { + global_farm_id: 1, + yield_farm_id: 2, + who: CHARLIE, + claimed: 20_000_000 * ONE, + reward_currency: BSX, + deposit_id: 1, + } + .into()); + }); +} + +#[test] +fn claim_rewards_should_propagate_error_when_claims_rewards_fails_due_to_double_claim() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 2, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + set_block_number(1_000); + //Arrange + assert_ok!(LiquidityMining::claim_rewards(Origin::signed(CHARLIE), 1, 2)); + + //Act and assert + assert_noop!( + LiquidityMining::claim_rewards(Origin::signed(CHARLIE), 1, 2), + "Dummy Double Claim" + ); + }); +} + +#[test] +fn claim_rewards_should_fail_when_origin_is_not_signed() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 2, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + assert_noop!(LiquidityMining::claim_rewards(Origin::none(), 1, 2), BadOrigin); + }); +} + +#[test] +fn claim_rewards_should_fail_when_claimed_by_non_deposit_owner() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 2, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + const NOT_OWNER: u128 = BOB; + + assert_noop!( + LiquidityMining::claim_rewards(Origin::signed(NOT_OWNER), 1, 2), + Error::::NotDepositOwner + ); + }); +} + +#[test] +fn claim_rewards_should_fail_when_claimed_reward_is_zero() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (ZERO_REWARDS_USER, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(ZERO_REWARDS_USER, 1, 2, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + set_block_number(1_000); + + assert_noop!( + LiquidityMining::claim_rewards(Origin::signed(ZERO_REWARDS_USER), 1, 2), + Error::::ZeroClaimedRewards + ); + }); +} diff --git a/pallets/xyk-liquidity-mining/src/tests/create_global_farm.rs b/pallets/xyk-liquidity-mining/src/tests/create_global_farm.rs new file mode 100644 index 000000000..f706a791b --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/tests/create_global_farm.rs @@ -0,0 +1,159 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2022 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. + +use super::*; + +#[test] +fn create_global_farm_should_work() { + ExtBuilder::default() + .with_endowed_accounts(vec![(ALICE, BSX, 500_000 * ONE)]) + .build() + .execute_with(|| { + //Arrange + let id = 1; + let total_rewards: Balance = 400_000 * ONE; + let reward_currency = BSX; + let planned_yielding_periods: BlockNumber = 1_000_000_000_u64; + let blocks_per_period = 20_000; + let incentivized_asset = BSX; + let owner = ALICE; + let yield_per_period = Perquintill::from_percent(20); + let max_reward_per_period: Balance = total_rewards.checked_div(planned_yielding_periods.into()).unwrap(); + let min_deposit = 3; + let price_adjustment = One::one(); + + let created_at_block = 15_896; + + set_block_number(created_at_block); + + //Act + assert_ok!(LiquidityMining::create_global_farm( + Origin::root(), + total_rewards, + planned_yielding_periods, + blocks_per_period, + incentivized_asset, + reward_currency, + owner, + yield_per_period, + min_deposit, + price_adjustment + )); + + assert_last_event!(crate::Event::GlobalFarmCreated { + id, + owner, + total_rewards, + reward_currency, + yield_per_period, + planned_yielding_periods, + blocks_per_period, + incentivized_asset, + max_reward_per_period, + min_deposit, + price_adjustment, + } + .into()); + }); +} + +#[test] +fn create_global_farm_should_fail_when_not_allowed_origin() { + ExtBuilder::default() + .with_endowed_accounts(vec![(ALICE, BSX, 500_000 * ONE)]) + .build() + .execute_with(|| { + let created_at_block = 15_896; + + set_block_number(created_at_block); + + assert_noop!( + LiquidityMining::create_global_farm( + Origin::signed(ALICE), + 1_000_000, + 1_000, + 300, + BSX, + BSX, + ALICE, + Perquintill::from_percent(20), + 3, + One::one() + ), + BadOrigin + ); + }); +} + +#[test] +fn owner_should_seed_pot_account_when_reward_currency_is_insufficient_asset() { + ExtBuilder::default() + .with_endowed_accounts(vec![(ALICE, INSUFF, 500_002 * ONE)]) + .build() + .execute_with(|| { + //Arrange + let id = 1; + let total_rewards: Balance = 400_000 * ONE; + let reward_currency = INSUFF; + let planned_yielding_periods: BlockNumber = 1_000_000_000_u64; + let blocks_per_period = 20_000; + let incentivized_asset = BSX; + let owner = ALICE; + let yield_per_period = Perquintill::from_percent(20); + let max_reward_per_period: Balance = total_rewards.checked_div(planned_yielding_periods.into()).unwrap(); + let min_deposit = 3; + let price_adjustment = One::one(); + + let created_at_block = 15_896; + + set_block_number(created_at_block); + + let pot = DummyLiquidityMining::pot_account().unwrap(); + assert_eq!(Tokens::free_balance(reward_currency, &pot), Balance::default()); + //Act + assert_ok!(LiquidityMining::create_global_farm( + Origin::root(), + total_rewards, + planned_yielding_periods, + blocks_per_period, + incentivized_asset, + reward_currency, + owner, + yield_per_period, + min_deposit, + price_adjustment + )); + + assert_last_event!(crate::Event::GlobalFarmCreated { + id, + owner, + total_rewards, + reward_currency, + yield_per_period, + planned_yielding_periods, + blocks_per_period, + incentivized_asset, + max_reward_per_period, + min_deposit, + price_adjustment, + } + .into()); + + let expected_ed = DummyRegistry::::existential_deposit(reward_currency).unwrap(); + assert_eq!(Tokens::free_balance(reward_currency, &pot), expected_ed); + }); +} diff --git a/pallets/xyk-liquidity-mining/src/tests/create_yield_farm.rs b/pallets/xyk-liquidity-mining/src/tests/create_yield_farm.rs new file mode 100644 index 000000000..11daade86 --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/tests/create_yield_farm.rs @@ -0,0 +1,107 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2022 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. + +use super::*; +use pretty_assertions::assert_eq; + +const BSX_ACA_ASSET_PAIR: AssetPair = AssetPair { + asset_in: BSX, + asset_out: ACA, +}; + +#[test] +fn create_yield_farm_should_work_when_global_farm_exist() { + ExtBuilder::default() + .with_endowed_accounts(vec![(ALICE, BSX, 1_000_000 * ONE)]) + .with_amm_pool(BSX_ACA_AMM, BSX_ACA_SHARE_ID, BSX_ACA_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 10_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(20), + ONE, + One::one(), + ) + .build() + .execute_with(|| { + let multiplier = One::one(); + let loyalty_curve = Some(LoyaltyCurve { + initial_reward_percentage: FixedU128::from_float(0.558), + scale_coef: 20, + }); + + set_block_number(17_850); + + //Act + assert_ok!(LiquidityMining::create_yield_farm( + Origin::signed(ALICE), + ALICE_FARM, + BSX_ACA_ASSET_PAIR, + multiplier, + loyalty_curve.clone() + )); + + //Assert + assert_last_event!(crate::Event::YieldFarmCreated { + global_farm_id: ALICE_FARM, + yield_farm_id: 2, + multiplier, + loyalty_curve, + asset_pair: BSX_ACA_ASSET_PAIR, + } + .into()); + }) +} + +#[test] +fn create_yield_farm_should_fail_when_amm_pool_doesnt_exists() { + let assets_without_pool = AssetPair { + asset_in: BSX, + asset_out: KSM, + }; + + ExtBuilder::default() + .with_endowed_accounts(vec![(ALICE, BSX, 1_000_000 * ONE)]) + .with_amm_pool(BSX_ACA_AMM, BSX_ACA_SHARE_ID, BSX_ACA_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 10_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(20), + ONE, + One::one(), + ) + .build() + .execute_with(|| { + assert_noop!( + LiquidityMining::create_yield_farm( + Origin::signed(ALICE), + ALICE_FARM, + assets_without_pool, + One::one(), + Some(LoyaltyCurve::default()) + ), + Error::::XykPoolDoesntExist + ); + }); +} diff --git a/pallets/xyk-liquidity-mining/src/tests/deposit_shares.rs b/pallets/xyk-liquidity-mining/src/tests/deposit_shares.rs new file mode 100644 index 000000000..c714f6256 --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/tests/deposit_shares.rs @@ -0,0 +1,158 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2022 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. + +use super::*; + +#[test] +fn deposit_shares_should_work() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE), (ALICE, BSX_KSM_SHARE_ID, 100 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(BOB, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + pretty_assertions::assert_eq!( + Tokens::total_balance(BSX_KSM_SHARE_ID, &LiquidityMining::account_id()), + 0 + ); + + set_block_number(1_800); + let deposited_amount = 50 * ONE; + + //Act + assert_ok!(LiquidityMining::deposit_shares( + Origin::signed(ALICE), + 1, + 2, + BSX_KSM_ASSET_PAIR, + deposited_amount, + )); + + //Assert + assert_last_event!(crate::Event::SharesDeposited { + global_farm_id: 1, + yield_farm_id: 2, + who: ALICE, + lp_token: BSX_KSM_SHARE_ID, + amount: deposited_amount, + deposit_id: 1 + } + .into()); + + pretty_assertions::assert_eq!( + Tokens::total_balance(BSX_KSM_SHARE_ID, &LiquidityMining::account_id()), + deposited_amount + ); + + let nft_owner: AccountId = DummyNFT::owner(&LM_NFT_COLLECTION, &1).unwrap(); + pretty_assertions::assert_eq!(nft_owner, ALICE); + }); +} + +#[test] +fn deposit_shares_should_fail_when_account_balance_is_insufficient() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE), (ALICE, BSX_KSM_SHARE_ID, 40 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(BOB, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + assert_noop!( + LiquidityMining::deposit_shares(Origin::signed(ALICE), 1, 2, BSX_KSM_ASSET_PAIR, 50 * ONE), + Error::::InsufficientXykSharesBalance + ); + }); +} + +#[test] +fn deposit_shares_should_fail_when_origin_is_not_signed() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE), (ALICE, BSX_KSM_SHARE_ID, 50 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(BOB, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + assert_noop!( + LiquidityMining::deposit_shares(Origin::none(), 1, 2, BSX_KSM_ASSET_PAIR, 50 * ONE), + BadOrigin + ); + }); +} + +#[test] +fn deposit_shares_should_fail_when_amm_pool_does_not_exist() { + let assets_without_amm: AssetPair = AssetPair { + asset_in: BSX, + asset_out: DOT, + }; + + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE), (ALICE, BSX_KSM_SHARE_ID, 50 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(BOB, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + assert_noop!( + LiquidityMining::deposit_shares(Origin::signed(ALICE), 1, 2, assets_without_amm, 50 * ONE), + Error::::XykPoolDoesntExist + ); + }); +} diff --git a/pallets/xyk-liquidity-mining/src/tests/get_token_value_of_lp_shares.rs b/pallets/xyk-liquidity-mining/src/tests/get_token_value_of_lp_shares.rs new file mode 100644 index 000000000..e62adad70 --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/tests/get_token_value_of_lp_shares.rs @@ -0,0 +1,92 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2022 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. + +//NOTE: This function is provided as callback for other pallets. + +use super::*; + +#[test] +fn get_token_value_of_lp_shares_should_return_valued_of_correct_token_when_amm_exists() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE), (ALICE, BSX_KSM_SHARE_ID, 100 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + let amm_pool_id = DummyAMM::get_pair_id(AssetPair { + asset_in: BSX, + asset_out: KSM, + }); + + //Arrange + Tokens::set_balance(Origin::root(), amm_pool_id, BSX, 50, 0).unwrap(); + Tokens::set_balance(Origin::root(), amm_pool_id, KSM, 100, 0).unwrap(); + + //Act & Assert + pretty_assertions::assert_eq!( + LiquidityMining::get_token_value_of_lp_shares(BSX, amm_pool_id, 1_000).unwrap(), + 50 + ); + pretty_assertions::assert_eq!( + LiquidityMining::get_token_value_of_lp_shares(KSM, amm_pool_id, 1_000).unwrap(), + 100 + ); + }); +} + +#[test] +fn get_token_value_of_lp_shares_should_whould_fail_when_request_asset_is_not_in_the_amm() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE), (ALICE, BSX_KSM_SHARE_ID, 100 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + let amm_pool_id = DummyAMM::get_pair_id(AssetPair { + asset_in: BSX, + asset_out: KSM, + }); + + //Arrange + Tokens::set_balance(Origin::root(), amm_pool_id, BSX, 50, 0).unwrap(); + Tokens::set_balance(Origin::root(), amm_pool_id, KSM, 100, 0).unwrap(); + + //Act & Assert + assert_noop!( + LiquidityMining::get_token_value_of_lp_shares(DOT, amm_pool_id, 1_000), + Error::::AssetNotInAssetPair + ); + }); +} + +#[test] +fn get_token_value_of_lp_shares_should_whould_fail_when_cannot_get_assets_for_amm_pool_id() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE), (ALICE, BSX_KSM_SHARE_ID, 100 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + let amm_pool_id = DummyAMM::get_pair_id(AssetPair { + asset_in: BSX, + asset_out: DOT, + }); + + //Act & Assert + assert_noop!( + LiquidityMining::get_token_value_of_lp_shares(BSX, amm_pool_id, 1_000), + Error::::CantGetXykAssets + ); + }); +} diff --git a/pallets/xyk-liquidity-mining/src/tests/mock.rs b/pallets/xyk-liquidity-mining/src/tests/mock.rs new file mode 100644 index 000000000..e5637ffa4 --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/tests/mock.rs @@ -0,0 +1,1140 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2022 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(test)] +use super::*; + +use crate as liq_mining; +use frame_support::weights::RuntimeDbWeight; +use frame_support::{ + parameter_types, + traits::{Everything, Nothing}, + PalletId, +}; + +use frame_system as system; +use hydradx_traits::{pools::DustRemovalAccountWhitelist, AMMPosition, AMM}; +use orml_traits::parameter_type_with_key; +use pallet_liquidity_mining::{FarmMultiplier, YieldFarmId}; +use pallet_xyk::types::{AssetId, AssetPair, Balance}; +use primitives::{Amount, ItemId}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, BlockNumberProvider, IdentityLookup}, + BuildStorage, +}; +use sp_std::convert::TryFrom; +use std::{cell::RefCell, collections::HashMap}; + +pub type AccountId = u128; +pub type BlockNumber = u64; +pub const ALICE: AccountId = 1; +pub const BOB: AccountId = 2; +pub const CHARLIE: AccountId = 3; +pub const ZERO_REWARDS_USER: AccountId = 4; +pub const LM_POT: AccountId = 5; + +pub const ONE: Balance = 1_000_000_000_000; + +pub const BSX_ACA_SHARE_ID: AssetId = 100; +pub const BSX_KSM_SHARE_ID: AssetId = 101; + +pub const BSX: AssetId = 1000; +pub const ACA: AssetId = 3000; +pub const KSM: AssetId = 4000; +pub const DOT: AssetId = 5000; +pub const INSUFF: AssetId = 5_555; + +pub const BSX_ACA_AMM: AccountId = 11_000; +pub const BSX_KSM_AMM: AccountId = 11_001; +pub const DEFAULT_AMM: AccountId = 11_007; + +pub const BSX_FARM: YieldFarmId = 1; +pub const KSM_FARM: YieldFarmId = 2; + +pub const INITIAL_READ_WEIGHT: u64 = 1; +pub const INITIAL_WRITE_WEIGHT: u64 = 1; + +pub const LM_NFT_COLLECTION: primitives::CollectionId = 1; + +pub const BSX_KSM_ASSET_PAIR: AssetPair = AssetPair { + asset_in: BSX, + asset_out: KSM, +}; + +pub const BSX_ACA_ASSET_PAIR: AssetPair = AssetPair { + asset_in: BSX, + asset_out: ACA, +}; + +pub const BSX_DOT_ASSET_PAIR: AssetPair = AssetPair { + asset_in: BSX, + asset_out: DOT, +}; + +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + LiquidityMining: liq_mining, + Tokens: orml_tokens, + Balances: pallet_balances, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 63; + pub static MockBlockNumberProvider: u64 = 0; + pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight{ + read: INITIAL_READ_WEIGHT, write: INITIAL_WRITE_WEIGHT + }; +} + +impl BlockNumberProvider for MockBlockNumberProvider { + type BlockNumber = u64; + + fn current_block_number() -> Self::BlockNumber { + Self::get() + } +} +impl system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = DbWeight; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +thread_local! { + pub static NFT_COLLECTION: RefCell<(u128, u128, u128)>= RefCell::new((0,0,0)); + + pub static AMM_POOLS: RefCell> = RefCell::new(HashMap::new()); + pub static NFTS: RefCell> = RefCell::new(HashMap::default()); + pub static DEPOSIT_IDS: RefCell> = RefCell::new(Vec::new()); + + pub static GLOBAL_FARMS: RefCell> = RefCell::new(HashMap::default()); + pub static YIELD_FARMS: RefCell> = RefCell::new(HashMap::default()); + pub static DEPOSITS: RefCell> = RefCell::new(HashMap::default()); + pub static DEPOSIT_ENTRIES: RefCell> = RefCell::new(HashMap::default()); + + pub static FARM_ID: RefCell = RefCell::new(0); + pub static DEPOSIT_ID: RefCell = RefCell::new(0); + + pub static DUSTER_WHITELIST: RefCell>= RefCell::new(Vec::new()); +} +#[derive(Copy, Clone)] +pub struct DymmyGlobalFarm { + total_rewards: Balance, + _planned_yielding_periods: PeriodOf, + _blocks_per_period: BlockNumber, + incentivized_asset: AssetId, + reward_currency: AssetId, + _owner: AccountId, + _yield_per_period: Perquintill, + _min_deposit: Balance, + price_adjustment: FixedU128, + _max_reward_per_period: Balance, +} + +#[derive(Clone, Debug)] +pub struct DummyYieldFarm { + _global_farm_id: u32, + multiplier: FarmMultiplier, + amm_pool_id: AccountId, + _assets: Vec, + stopped: bool, +} + +#[derive(Copy, Clone)] +pub struct DummyDeposit { + amm_pool_id: AccountId, + shares_amount: Balance, + entries: u32, +} + +#[derive(Copy, Clone)] +pub struct DummyFarmEntry { + _yield_farm_id: u32, + global_farm_id: u32, + _valued_shares: Balance, + last_claimed: BlockNumber, +} + +pub struct DummyAMM; + +impl AMM for DummyAMM { + fn get_max_out_ratio() -> u128 { + 0_u32.into() + } + + fn get_fee(_pool_account_id: &AccountId) -> (u32, u32) { + (0, 0) + } + + fn get_max_in_ratio() -> u128 { + 0_u32.into() + } + + fn get_pool_assets(pool_id: &AccountId) -> Option> { + AMM_POOLS.with(|v| match v.borrow().get(pool_id) { + Some((_, pair)) => Some(vec![pair.asset_in, pair.asset_out]), + _ => None, + }) + } + + fn get_spot_price_unchecked(_asset_a: AssetId, _asset_b: AssetId, _amount: Balance) -> Balance { + Balance::from(0_u32) + } + + fn validate_sell( + _origin: &AccountId, + _assets: AssetPair, + _amount: Balance, + _min_bought: Balance, + _discount: bool, + ) -> Result< + hydradx_traits::AMMTransfer, + frame_support::sp_runtime::DispatchError, + > { + Err(sp_runtime::DispatchError::Other("NotImplemented")) + } + + fn execute_buy( + _transfer: &hydradx_traits::AMMTransfer, + ) -> frame_support::dispatch::DispatchResult { + Err(sp_runtime::DispatchError::Other("NotImplemented")) + } + + fn execute_sell( + _transfer: &hydradx_traits::AMMTransfer, + ) -> frame_support::dispatch::DispatchResult { + Err(sp_runtime::DispatchError::Other("NotImplemented")) + } + + fn validate_buy( + _origin: &AccountId, + _assets: AssetPair, + _amount: Balance, + _max_limit: Balance, + _discount: bool, + ) -> Result< + hydradx_traits::AMMTransfer, + frame_support::sp_runtime::DispatchError, + > { + Err(sp_runtime::DispatchError::Other("NotImplemented")) + } + + fn get_min_pool_liquidity() -> Balance { + Balance::from(0_u32) + } + + fn get_min_trading_limit() -> Balance { + Balance::from(0_u32) + } + + // Fn bellow are used by liq. mining pallet + fn exists(assets: AssetPair) -> bool { + AMM_POOLS.with(|v| { + let p = v.borrow(); + + p.iter().any(|(_, v)| v.1 == assets) + }) + } + + fn get_pair_id(assets: AssetPair) -> AccountId { + AMM_POOLS.with(|v| { + let p = v.borrow(); + + match p.iter().find(|(_, v)| v.1 == assets) { + Some((pair_id, _)) => *pair_id, + None => DEFAULT_AMM, + } + }) + } + + fn get_share_token(assets: AssetPair) -> AssetId { + AMM_POOLS.with(|v| { + let p = v.borrow(); + + match p.iter().find(|(_, v)| v.1 == assets) { + Some((_, v)) => v.0, + None => BSX, + } + }) + } +} + +impl AMMPosition for DummyAMM { + type Error = DispatchError; + + fn get_liquidity_behind_shares( + asset_a: AssetId, + asset_b: AssetId, + _shares_amount: Balance, + ) -> Result<(Balance, Balance), Self::Error> { + let asset_pair = AssetPair { + asset_in: asset_a, + asset_out: asset_b, + }; + let amm_pool_id = DummyAMM::get_pair_id(asset_pair); + + Ok(( + Tokens::free_balance(asset_a, &amm_pool_id), + Tokens::free_balance(asset_b, &amm_pool_id), + )) + } +} + +parameter_types! { + pub const WarehouseLMPalletId: PalletId = PalletId(*b"WhouseLm"); + pub const MinDeposit: Balance = 1; + pub const MaxLocks: u32 = 1; + pub const LMPalletId: PalletId = PalletId(*b"TEST_lm_"); + #[derive(PartialEq, Eq)] + pub const MaxEntriesPerDeposit: u8 = 10; + pub const MaxYieldFarmsPerGlobalFarm: u8 = 5; + pub const NftCollectionId: primitives::CollectionId = LM_NFT_COLLECTION; + pub const ReserveClassIdUpTo: u128 = 2; +} + +impl liq_mining::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Currencies = Tokens; + type CreateOrigin = frame_system::EnsureRoot; + type WeightInfo = (); + type PalletId = LMPalletId; + type AMM = DummyAMM; + type NFTCollectionId = NftCollectionId; + type NFTHandler = DummyNFT; + type LiquidityMiningHandler = DummyLiquidityMining; + type NonDustableWhitelistHandler = Whitelist; + type AssetRegistry = DummyRegistry; +} + +use hydradx_traits::registry::{AssetKind, Inspect as InspectRegistry}; + +pub struct DummyRegistry(sp_std::marker::PhantomData); + +impl InspectRegistry for DummyRegistry +where + AssetId: Into + From, +{ + type AssetId = AssetId; + type Location = u8; + + fn is_sufficient(id: Self::AssetId) -> bool { + id != INSUFF + } + + fn asset_type(_id: Self::AssetId) -> Option { + unimplemented!() + } + + fn decimals(_id: Self::AssetId) -> Option { + unimplemented!() + } + + fn exists(_id: AssetId) -> bool { + unimplemented!() + } + + fn is_banned(_id: Self::AssetId) -> bool { + unimplemented!() + } + + fn asset_name(_id: Self::AssetId) -> Option> { + unimplemented!() + } + + fn asset_symbol(_id: Self::AssetId) -> Option> { + unimplemented!() + } + + fn existential_deposit(_id: Self::AssetId) -> Option { + Some(1_000_u128) + } +} + +use frame_support::traits::tokens::nonfungibles::{Create, Inspect, Mutate, Transfer}; +pub struct DummyNFT; + +impl> Inspect for DummyNFT { + type ItemId = ItemId; + type CollectionId = CollectionId; + + fn owner(collection: &Self::CollectionId, item: &Self::ItemId) -> Option { + let mut owner: Option = None; + + NFTS.with(|v| { + if let Some(o) = v.borrow().get(&(*collection, *item)) { + owner = Some((*o).into()); + } + }); + owner + } +} + +impl> Create for DummyNFT { + fn create_collection(_collection: &Self::CollectionId, _who: &AccountId, _admin: &AccountId) -> DispatchResult { + Ok(()) + } +} + +impl + Into + Copy> Mutate for DummyNFT { + fn mint_into(collection: &Self::CollectionId, item: &Self::ItemId, who: &AccountId) -> DispatchResult { + NFTS.with(|v| { + let mut m = v.borrow_mut(); + m.insert((*collection, *item), (*who).into()); + }); + Ok(()) + } + + fn burn( + collection: &Self::CollectionId, + item: &Self::ItemId, + _maybe_check_owner: Option<&AccountId>, + ) -> DispatchResult { + NFTS.with(|v| { + let mut m = v.borrow_mut(); + m.remove(&(*collection, *item)); + }); + Ok(()) + } +} + +impl Transfer for DummyNFT { + fn transfer(collection: &Self::CollectionId, item: &Self::ItemId, destination: &AccountId) -> DispatchResult { + NFTS.with(|v| { + let mut m = v.borrow_mut(); + let key = (*collection, *item); + + if !m.contains_key(&key) { + return Err(sp_runtime::DispatchError::Other("NFT not found")); + } + + m.insert(key, *destination); + + Ok(()) + }) + } +} + +pub struct DummyLiquidityMining {} + +impl DummyLiquidityMining { + fn claim_rewards( + who: AccountId, + deposit_id: u128, + yield_farm_id: u32, + fail_on_double_claim: bool, + ) -> Result<(u32, AssetId, Balance, Balance), DispatchError> { + DEPOSIT_ENTRIES.with(|v| { + let mut p = v.borrow_mut(); + let yield_farm_entry = p.get_mut(&(deposit_id, yield_farm_id)).unwrap(); + + if yield_farm_entry.last_claimed == MockBlockNumberProvider::get() && fail_on_double_claim { + return Err("Dummy Double Claim".into()); + } + + let reward_currency = GLOBAL_FARMS.with(|v| { + v.borrow() + .get(&yield_farm_entry.global_farm_id) + .unwrap() + .reward_currency + }); + + let mut claimed = 20_000_000 * ONE; + let mut unclaimable = 10_000 * ONE; + if yield_farm_entry.last_claimed == MockBlockNumberProvider::get() { + claimed = 0; + unclaimable = 200_000 * ONE; + } + + if yield_farm_entry.last_claimed == MockBlockNumberProvider::get() { + claimed = 0; + } + + yield_farm_entry.last_claimed = MockBlockNumberProvider::get(); + + if who == ZERO_REWARDS_USER { + claimed = 0; + unclaimable = 0; + } + + Ok((yield_farm_entry.global_farm_id, reward_currency, claimed, unclaimable)) + }) + } +} + +impl hydradx_traits::liquidity_mining::Mutate for DummyLiquidityMining { + type Error = DispatchError; + + type AmmPoolId = AccountId; + type Balance = Balance; + type Period = PeriodOf; + type LoyaltyCurve = LoyaltyCurve; + + fn create_global_farm( + total_rewards: Self::Balance, + planned_yielding_periods: Self::Period, + blocks_per_period: BlockNumber, + incentivized_asset: AssetId, + reward_currency: AssetId, + owner: AccountId, + yield_per_period: Perquintill, + min_deposit: Self::Balance, + price_adjustment: FixedU128, + ) -> Result<(u32, Self::Balance), Self::Error> { + let max_reward_per_period = total_rewards.checked_div(planned_yielding_periods.into()).unwrap(); + let farm_id = get_next_farm_id(); + + GLOBAL_FARMS.with(|v| { + v.borrow_mut().insert( + farm_id, + DymmyGlobalFarm { + total_rewards, + _planned_yielding_periods: planned_yielding_periods, + _blocks_per_period: blocks_per_period, + incentivized_asset, + reward_currency, + _owner: owner, + _yield_per_period: yield_per_period, + _min_deposit: min_deposit, + price_adjustment, + _max_reward_per_period: max_reward_per_period, + }, + ); + }); + + Ok((farm_id, max_reward_per_period)) + } + + fn update_global_farm_price_adjustment( + _who: AccountId, + global_farm_id: u32, + price_adjustment: FixedU128, + ) -> Result<(), Self::Error> { + GLOBAL_FARMS.with(|v| { + let mut p = v.borrow_mut(); + + let global_farm = p.get_mut(&global_farm_id).unwrap(); + + global_farm.price_adjustment = price_adjustment; + + Ok(()) + }) + } + + fn terminate_global_farm( + who: AccountId, + global_farm_id: u32, + ) -> Result<(AssetId, Self::Balance, AccountId), Self::Error> { + GLOBAL_FARMS.with(|v| { + let g_f = v.borrow_mut().remove_entry(&global_farm_id).unwrap().1; + + Ok((g_f.reward_currency, g_f.total_rewards, who)) + }) + } + + fn create_yield_farm( + _who: AccountId, + global_farm_id: u32, + multiplier: FixedU128, + _loyalty_curve: Option, + amm_pool_id: Self::AmmPoolId, + assets: Vec, + ) -> Result { + let farm_id = get_next_farm_id(); + + YIELD_FARMS.with(|v| { + v.borrow_mut().insert( + farm_id, + DummyYieldFarm { + _global_farm_id: global_farm_id, + multiplier, + amm_pool_id, + _assets: assets, + stopped: false, + }, + ); + }); + + Ok(farm_id) + } + + fn update_yield_farm_multiplier( + _who: AccountId, + _global_farm_id: u32, + amm_pool_id: Self::AmmPoolId, + multiplier: FixedU128, + ) -> Result { + YIELD_FARMS.with(|v| { + let mut p = v.borrow_mut(); + + let (id, yield_farm) = p.iter_mut().find(|(_, farm)| farm.amm_pool_id == amm_pool_id).unwrap(); + + yield_farm.multiplier = multiplier; + + Ok(*id) + }) + } + + fn stop_yield_farm( + _who: AccountId, + _global_farm_id: u32, + amm_pool_id: Self::AmmPoolId, + ) -> Result { + YIELD_FARMS.with(|v| { + let mut p = v.borrow_mut(); + + let (id, yield_farm) = p.iter_mut().find(|(_, farm)| farm.amm_pool_id == amm_pool_id).unwrap(); + + yield_farm.stopped = true; + + Ok(*id) + }) + } + + fn resume_yield_farm( + _who: AccountId, + _global_farm_id: u32, + yield_farm_id: u32, + _amm_pool_id: Self::AmmPoolId, + multiplier: FixedU128, + ) -> Result<(), Self::Error> { + YIELD_FARMS.with(|v| { + let mut p = v.borrow_mut(); + + let yield_farm = p.get_mut(&yield_farm_id).unwrap(); + + yield_farm.stopped = true; + yield_farm.multiplier = multiplier; + + Ok(()) + }) + } + + fn terminate_yield_farm( + _who: AccountId, + _global_farm_id: u32, + yield_farm_id: u32, + _amm_pool_id: Self::AmmPoolId, + ) -> Result<(), Self::Error> { + YIELD_FARMS.with(|v| { + let _ = v.borrow_mut().remove_entry(&yield_farm_id).unwrap().1; + }); + + Ok(()) + } + + fn deposit_lp_shares( + global_farm_id: u32, + yield_farm_id: u32, + amm_pool_id: Self::AmmPoolId, + shares_amount: Self::Balance, + get_token_value_of_lp_shares: F, + ) -> Result + where + F: Fn(AssetId, Self::AmmPoolId, Self::Balance) -> Result, + { + let deposit_id = get_next_deposit_id(); + + let incentivized_asset = GLOBAL_FARMS.with(|v| v.borrow().get(&global_farm_id).unwrap().incentivized_asset); + + let valued_shares = get_token_value_of_lp_shares(incentivized_asset, amm_pool_id, shares_amount).unwrap(); + + DEPOSITS.with(|v| { + v.borrow_mut().insert( + deposit_id, + DummyDeposit { + amm_pool_id, + shares_amount, + entries: 1, + }, + ); + }); + + DEPOSIT_ENTRIES.with(|v| { + v.borrow_mut().insert( + (deposit_id, yield_farm_id), + DummyFarmEntry { + global_farm_id, + _yield_farm_id: yield_farm_id, + _valued_shares: valued_shares, + last_claimed: MockBlockNumberProvider::get(), + }, + ); + }); + + Ok(deposit_id) + } + + fn redeposit_lp_shares( + global_farm_id: u32, + yield_farm_id: u32, + deposit_id: u128, + get_token_value_of_lp_shares: F, + ) -> Result<(Self::Balance, Self::AmmPoolId), Self::Error> + where + F: Fn(AssetId, Self::AmmPoolId, Self::Balance) -> Result, + { + let deposit = DEPOSITS.with(|v| { + let mut p = v.borrow_mut(); + let deposit = p.get_mut(&deposit_id).unwrap(); + + deposit.entries += 1; + + *deposit + }); + + let incentivized_asset = GLOBAL_FARMS.with(|v| v.borrow().get(&global_farm_id).unwrap().incentivized_asset); + let amm_pool_id = deposit.amm_pool_id; + + let valued_shares = + get_token_value_of_lp_shares(incentivized_asset, amm_pool_id, deposit.shares_amount).unwrap(); + + DEPOSIT_ENTRIES.with(|v| { + v.borrow_mut().insert( + (deposit_id, yield_farm_id), + DummyFarmEntry { + _yield_farm_id: yield_farm_id, + global_farm_id, + _valued_shares: valued_shares, + last_claimed: MockBlockNumberProvider::get(), + }, + ) + }); + + Ok((deposit.shares_amount, deposit.amm_pool_id)) + } + + fn claim_rewards( + who: AccountId, + deposit_id: u128, + yield_farm_id: u32, + ) -> Result<(u32, AssetId, Self::Balance, Self::Balance), Self::Error> { + let fail_on_double_claim = true; + + Self::claim_rewards(who, deposit_id, yield_farm_id, fail_on_double_claim) + } + + fn withdraw_lp_shares( + who: AccountId, + deposit_id: u128, + global_farm_id: u32, + yield_farm_id: u32, + amm_pool_id: Self::AmmPoolId, + ) -> Result<(Self::Balance, Option<(AssetId, Self::Balance, Self::Balance)>, bool), Self::Error> { + let claim_data = if Self::is_yield_farm_claimable(global_farm_id, yield_farm_id, amm_pool_id) { + let fail_on_double_claim = false; + let (_, reward_currency, claimed_amount, unclaimable_amount) = + Self::claim_rewards(who, deposit_id, yield_farm_id, fail_on_double_claim)?; + + Some((reward_currency, claimed_amount, unclaimable_amount)) + } else { + None + }; + + let deposit = DEPOSITS.with(|v| { + let mut p = v.borrow_mut(); + let deposit = p.get_mut(&deposit_id).unwrap(); + + deposit.entries -= 1; + + *deposit + }); + + let withdrawn_amount = deposit.shares_amount; + + let mut destroyed = false; + if deposit.entries.is_zero() { + DEPOSITS.with(|v| v.borrow_mut().remove(&deposit_id)); + destroyed = true; + } + + Ok((withdrawn_amount, claim_data, destroyed)) + } + + fn is_yield_farm_claimable(_global_farm_id: u32, yield_farm_id: u32, _amm_pool_id: Self::AmmPoolId) -> bool { + !YIELD_FARMS.with(|v| v.borrow().get(&yield_farm_id).unwrap().stopped) + } + + fn get_global_farm_id(deposit_id: u128, yield_farm_id: u32) -> Option { + DEPOSIT_ENTRIES.with(|v| v.borrow().get(&(deposit_id, yield_farm_id)).map(|d| d.global_farm_id)) + } + + fn create_global_farm_without_price_adjustment( + _total_rewards: Self::Balance, + _planned_yielding_periods: Self::Period, + _blocks_per_period: BlockNumber, + _incentivized_asset: AssetId, + _reward_currency: AssetId, + _owner: AccountId, + _yield_per_period: Perquintill, + _min_deposit: Self::Balance, + ) -> Result<(YieldFarmId, Self::Balance), Self::Error> { + //NOTE: Basilisk is not using this fn. + Err(sp_runtime::DispatchError::Other("Not implemented")) + } +} + +impl hydradx_traits::liquidity_mining::Inspect for DummyLiquidityMining { + fn pot_account() -> Option { + Some(LM_POT) + } +} +//NOTE: this is and should not be used anywhere. This exists only to make trait bellow happy. Trait +//bellow is not really used. Basilisk is using `DefaultPriceAdjustment` implementation. +struct FakeGlobalFarm; + +impl hydradx_traits::liquidity_mining::PriceAdjustment for DummyLiquidityMining { + type Error = DispatchError; + type PriceAdjustment = FixedU128; + + //NOTE: basilisk is using `DefaultPriceAdjustment` for now. + fn get(_global_farm: &FakeGlobalFarm) -> Result { + Err(sp_runtime::DispatchError::Other("Not implemented")) + } +} + +parameter_types! { + pub const ExistentialDeposit: u128 = 500; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Test { + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = frame_system::Pallet; + type MaxLocks = (); + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type MaxHolds = (); + type RuntimeHoldReason = (); +} + +parameter_type_with_key! { + pub ExistentialDeposits: |_currency_id: AssetId| -> Balance { + 1u128 + }; +} + +impl orml_tokens::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type Amount = Amount; + type CurrencyId = AssetId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type MaxLocks = MaxLocks; + type DustRemovalWhitelist = Nothing; + type MaxReserves = ConstU32<100_000>; + type ReserveIdentifier = (); + type CurrencyHooks = (); +} + +pub struct ExtBuilder { + endowed_accounts: Vec<(AccountId, AssetId, Balance)>, + + amm_pools: Vec<(AccountId, AssetId, AssetPair)>, + + #[allow(clippy::too_many_arguments, clippy::type_complexity)] + global_farms: Vec<( + Balance, + PeriodOf, + BlockNumber, + AssetId, + AssetId, + AccountId, + Perquintill, + Balance, + FixedU128, + )>, + #[allow(clippy::too_many_arguments, clippy::type_complexity)] + yield_farms: Vec<(AccountId, GlobalFarmId, FarmMultiplier, Option, AssetPair)>, + deposits: Vec<(AccountId, GlobalFarmId, YieldFarmId, AssetPair, Balance)>, + starting_block: u64, +} + +impl Default for ExtBuilder { + fn default() -> Self { + // If eg. tests running on one thread only, this thread local is shared. + // let's make sure that it is empty for each test case + // or set to original default value + GLOBAL_FARMS.with(|v| { + v.borrow_mut().clear(); + }); + YIELD_FARMS.with(|v| { + v.borrow_mut().clear(); + }); + DEPOSITS.with(|v| { + v.borrow_mut().clear(); + }); + DEPOSIT_ENTRIES.with(|v| { + v.borrow_mut().clear(); + }); + NFTS.with(|v| { + v.borrow_mut().clear(); + }); + AMM_POOLS.with(|v| { + v.borrow_mut().clear(); + }); + + FARM_ID.with(|v| { + *v.borrow_mut() = 0; + }); + DEPOSIT_ID.with(|v| { + *v.borrow_mut() = 0; + }); + + Self { + endowed_accounts: vec![], + global_farms: vec![], + yield_farms: vec![], + deposits: vec![], + amm_pools: vec![], + starting_block: 1, + } + } +} + +impl ExtBuilder { + pub fn with_endowed_accounts(mut self, accounts: Vec<(AccountId, AssetId, Balance)>) -> Self { + self.endowed_accounts = accounts; + self + } + + pub fn _start_from_block(mut self, block_number: u64) -> Self { + self.starting_block = block_number; + + self + } + + pub fn with_amm_pool(mut self, amm_id: AccountId, lp_token: AssetId, asset_pair: AssetPair) -> Self { + self.amm_pools.push((amm_id, lp_token, asset_pair)); + + self + } + + #[allow(clippy::too_many_arguments)] + pub fn with_global_farm( + mut self, + total_rewards: Balance, + planned_yielding_periods: PeriodOf, + blocks_per_period: BlockNumber, + incentivized_asset: AssetId, + reward_currency: AssetId, + owner: AccountId, + yield_per_period: Perquintill, + min_deposit: Balance, + price_adjustment: FixedU128, + ) -> Self { + self.global_farms.push(( + total_rewards, + planned_yielding_periods, + blocks_per_period, + incentivized_asset, + reward_currency, + owner, + yield_per_period, + min_deposit, + price_adjustment, + )); + + self + } + + pub fn with_yield_farm( + mut self, + who: AccountId, + global_farm_id: GlobalFarmId, + multiplier: FarmMultiplier, + loyalty_curve: Option, + assets: AssetPair, + ) -> Self { + self.yield_farms + .push((who, global_farm_id, multiplier, loyalty_curve, assets)); + + self + } + + pub fn with_deposit( + mut self, + owner: AccountId, + global_farm_id: GlobalFarmId, + yield_farm_id: YieldFarmId, + assets: AssetPair, + amount: Balance, + ) -> Self { + self.deposits + .push((owner, global_farm_id, yield_farm_id, assets, amount)); + + 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(|| { + set_block_number(self.starting_block); + + //Initialize amm pools + for (amm_id, lp_token, asset_pair) in self.amm_pools { + AMM_POOLS.with(|v| { + v.borrow_mut().insert(amm_id, (lp_token, asset_pair)); + }); + } + + //Create global farms + for ( + total_rewards, + planned_yielding_periods, + blocks_per_period, + incentivized_asset, + reward_currency, + owner, + yield_per_period, + min_deposit, + price_adjustment, + ) in self.global_farms + { + let _ = DummyLiquidityMining::create_global_farm( + total_rewards, + planned_yielding_periods, + blocks_per_period, + incentivized_asset, + reward_currency, + owner, + yield_per_period, + min_deposit, + price_adjustment, + ); + } + + //Create yield farms + for (who, global_farm_id, multiplier, loyalty_curve, asset_pair) in self.yield_farms { + let amm_pool_id = DummyAMM::get_pair_id(asset_pair); + + assert!(amm_pool_id != DEFAULT_AMM, "get_pair_id() returned DEFAULT_AMM"); + + let _ = DummyLiquidityMining::create_yield_farm( + who, + global_farm_id, + multiplier, + loyalty_curve, + amm_pool_id, + vec![asset_pair.asset_in, asset_pair.asset_out], + ); + } + + //Create deposits + let mut i: DepositId = 1; + for (owner, global_farm_id, yield_farm_id, asset_pair, amount) in self.deposits { + assert_ok!(LiquidityMining::deposit_shares( + RuntimeOrigin::signed(owner), + global_farm_id, + yield_farm_id, + asset_pair, + amount + )); + + DEPOSIT_IDS.with(|v| { + v.borrow_mut().push(i); + }); + i += 1; + } + }); + + r + } +} + +pub struct Whitelist; + +impl DustRemovalAccountWhitelist for Whitelist { + type Error = DispatchError; + + fn add_account(account: &AccountId) -> Result<(), Self::Error> { + DEPOSIT_IDS.with(|v| { + v.borrow_mut().push(*account); + }); + + Ok(()) + } + + fn remove_account(_account: &AccountId) -> Result<(), Self::Error> { + Err(sp_runtime::DispatchError::Other("Not implemented")) + } +} + +fn get_next_farm_id() -> u32 { + FARM_ID.with(|v| { + *v.borrow_mut() += 1; + + *v.borrow() + }) +} + +fn get_next_deposit_id() -> DepositId { + DEPOSIT_ID.with(|v| { + *v.borrow_mut() += 1; + + *v.borrow() + }) +} + +pub fn set_block_number(n: u64) { + MockBlockNumberProvider::set(n); + System::set_block_number(n); +} diff --git a/pallets/xyk-liquidity-mining/src/tests/mod.rs b/pallets/xyk-liquidity-mining/src/tests/mod.rs new file mode 100644 index 000000000..78e22405e --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/tests/mod.rs @@ -0,0 +1,56 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2022 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. + +use super::*; +use mock::*; +use sp_runtime::traits::One; + +use frame_support::{assert_noop, assert_ok}; +use primitives::Balance; +use sp_runtime::traits::BadOrigin; + +const ALICE_FARM: u32 = BSX_FARM; +const BOB_FARM: u32 = KSM_FARM; + +use pallet_liquidity_mining::LoyaltyCurve; + +pub type Origin = RuntimeOrigin; + +macro_rules! assert_last_event { + ( $x:expr ) => {{ + pretty_assertions::assert_eq!(System::events().last().expect("events expected").event, $x); + }}; +} + +pub fn has_event(event: mock::RuntimeEvent) -> bool { + System::events().iter().any(|record| record.event == event) +} + +pub mod claim_rewards; +pub mod create_global_farm; +pub mod create_yield_farm; +pub mod deposit_shares; +pub mod get_token_value_of_lp_shares; +pub mod mock; +pub mod redeposit_shares; +pub mod resume_yield_farm; +pub mod stop_yield_farm; +pub mod terminate_global_farm; +pub mod terminate_yield_farm; +pub mod update_global_farm; +pub mod update_yield_farm; +pub mod withdraw_shares; diff --git a/pallets/xyk-liquidity-mining/src/tests/redeposit_shares.rs b/pallets/xyk-liquidity-mining/src/tests/redeposit_shares.rs new file mode 100644 index 000000000..507524582 --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/tests/redeposit_shares.rs @@ -0,0 +1,304 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2022 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. + +use super::*; + +#[test] +fn redeposit_shares_should_work_when_deposit_already_exists() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_yield_farm(BOB, 2, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 3, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + set_block_number(50_000); + + //Act + assert_ok!(LiquidityMining::redeposit_shares( + Origin::signed(CHARLIE), + 2, + 4, + BSX_KSM_ASSET_PAIR, + 1, + )); + + assert_last_event!(crate::Event::SharesRedeposited { + global_farm_id: 2, + yield_farm_id: 4, + who: CHARLIE, + lp_token: BSX_KSM_SHARE_ID, + amount: 100 * ONE, + deposit_id: 1, + } + .into()); + }) +} + +#[test] +fn redeposit_shares_should_fail_when_called_by_not_the_deposit_owner() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_yield_farm(BOB, 2, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 3, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + assert_noop!( + LiquidityMining::redeposit_shares(Origin::signed(BOB), 2, 4, BSX_KSM_ASSET_PAIR, 1,), + Error::::NotDepositOwner + ); + }); +} + +#[test] +fn redeposit_shares_deposit_should_fail_when_asset_pair_has_invalid_asset() { + let pair_without_amm = AssetPair { + asset_in: BSX, + asset_out: DOT, + }; + + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_yield_farm(BOB, 2, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 3, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + assert_noop!( + LiquidityMining::redeposit_shares(Origin::signed(CHARLIE), 2, 4, pair_without_amm, 1,), + Error::::XykPoolDoesntExist + ); + }); +} + +#[test] +fn redeposit_shares_should_fail_when_origin_is_not_signed() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_yield_farm(BOB, 2, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 3, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + assert_noop!( + LiquidityMining::redeposit_shares(Origin::none(), 2, 4, BSX_KSM_ASSET_PAIR, 1,), + BadOrigin + ); + }); +} + +#[test] +fn redeposit_shares_should_fail_when_nft_owner_is_not_found() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_yield_farm(BOB, 2, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 3, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + set_block_number(50_000); + let deposit_id = 1; + + //Arrange + //Destory NFT without destruction of depoist. + mock::DummyNFT::burn(&LM_NFT_COLLECTION, &deposit_id, None::<&AccountId>).unwrap(); + + //Act + assert_noop!( + LiquidityMining::redeposit_shares(Origin::signed(CHARLIE), 2, 4, BSX_KSM_ASSET_PAIR, 1,), + Error::::CantFindDepositOwner + ); + }) +} + +#[test] +fn redeposit_shares_should_fail_when_asset_pair_is_not_in_the_deposit() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (BOB, ACA, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_amm_pool(BSX_ACA_AMM, BSX_ACA_SHARE_ID, BSX_ACA_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_yield_farm(BOB, 2, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 3, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + set_block_number(50_000); + + //Act + assert_noop!( + LiquidityMining::redeposit_shares(Origin::signed(CHARLIE), 2, 4, BSX_ACA_ASSET_PAIR, 1), + Error::::InvalidAssetPair + ); + }) +} diff --git a/pallets/xyk-liquidity-mining/src/tests/resume_yield_farm.rs b/pallets/xyk-liquidity-mining/src/tests/resume_yield_farm.rs new file mode 100644 index 000000000..b2060614e --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/tests/resume_yield_farm.rs @@ -0,0 +1,146 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2022 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. + +use super::*; + +#[test] +fn resume_yield_farm_should_work() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(BOB, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + //Arrange + assert_ok!(LiquidityMining::stop_yield_farm( + Origin::signed(BOB), + 1, + BSX_KSM_ASSET_PAIR + )); + + set_block_number(13_420_000); + + //Act + let new_multiplier = FixedU128::from(7_490_000); + assert_ok!(LiquidityMining::resume_yield_farm( + Origin::signed(BOB), + 1, + 2, + BSX_KSM_ASSET_PAIR, + new_multiplier + )); + + //Assert + assert_last_event!(crate::Event::YieldFarmResumed { + global_farm_id: 1, + yield_farm_id: 2, + who: BOB, + asset_pair: BSX_KSM_ASSET_PAIR, + multiplier: new_multiplier, + } + .into()); + }); +} + +#[test] +fn resume_yield_farm_should_fail_when_amm_pool_does_not_exist() { + let pair_without_amm: AssetPair = AssetPair { + asset_in: BSX, + asset_out: DOT, + }; + + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(BOB, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + //Arrange + assert_ok!(LiquidityMining::stop_yield_farm( + Origin::signed(BOB), + 1, + BSX_KSM_ASSET_PAIR + )); + set_block_number(13_420_000); + + //Act and assert + assert_noop!( + LiquidityMining::resume_yield_farm( + Origin::signed(BOB), + 1, + 2, + pair_without_amm, + FixedU128::from(7_490_000) + ), + Error::::XykPoolDoesntExist + ); + }); +} + +#[test] +fn resume_yield_farm_should_fail_when_caller_is_not_signed() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(BOB, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + assert_noop!( + LiquidityMining::resume_yield_farm( + Origin::none(), + 1, + 2, + BSX_KSM_ASSET_PAIR, + FixedU128::from(7_490_000) + ), + BadOrigin + ); + }); +} diff --git a/pallets/xyk-liquidity-mining/src/tests/stop_yield_farm.rs b/pallets/xyk-liquidity-mining/src/tests/stop_yield_farm.rs new file mode 100644 index 000000000..b5ae191a6 --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/tests/stop_yield_farm.rs @@ -0,0 +1,81 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2022 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. + +use super::*; + +#[test] +fn stop_yield_farm_should_work() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(BOB, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + //Act + assert_ok!(LiquidityMining::stop_yield_farm( + Origin::signed(BOB), + 1, + BSX_KSM_ASSET_PAIR + )); + + //Assert + assert_last_event!(crate::Event::YieldFarmStopped { + global_farm_id: 1, + yield_farm_id: 2, + who: BOB, + asset_pair: BSX_KSM_ASSET_PAIR, + } + .into()); + }); +} + +#[test] +fn stop_yield_farm_should_fail_when_caller_is_not_signed() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(BOB, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + assert_noop!( + LiquidityMining::stop_yield_farm(Origin::none(), 1, BSX_KSM_ASSET_PAIR), + BadOrigin + ); + }); +} diff --git a/pallets/xyk-liquidity-mining/src/tests/terminate_global_farm.rs b/pallets/xyk-liquidity-mining/src/tests/terminate_global_farm.rs new file mode 100644 index 000000000..7e2cfb91e --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/tests/terminate_global_farm.rs @@ -0,0 +1,71 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2022 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. + +use super::*; + +#[test] +fn terminate_global_farm_should_work() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE)]) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .build() + .execute_with(|| { + assert_ok!(LiquidityMining::terminate_global_farm(Origin::signed(BOB), 1)); + + assert_last_event!(crate::Event::GlobalFarmTerminated { + global_farm_id: 1, + who: BOB, + reward_currency: BSX, + undistributed_rewards: 500_000 * ONE, + } + .into()); + }); +} + +#[test] +fn terminate_global_farm_should_fail_when_origin_is_not_signed() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE)]) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .build() + .execute_with(|| { + assert_noop!( + LiquidityMining::terminate_global_farm(Origin::none(), BOB_FARM), + BadOrigin + ); + }); +} diff --git a/pallets/xyk-liquidity-mining/src/tests/terminate_yield_farm.rs b/pallets/xyk-liquidity-mining/src/tests/terminate_yield_farm.rs new file mode 100644 index 000000000..cc7781cf3 --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/tests/terminate_yield_farm.rs @@ -0,0 +1,82 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2022 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. + +use super::*; + +#[test] +fn terminate_yield_farm_should_work() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(BOB, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + //Act + assert_ok!(LiquidityMining::terminate_yield_farm( + Origin::signed(BOB), + 1, + 2, + BSX_KSM_ASSET_PAIR + )); + + //Assert + assert_last_event!(crate::Event::YieldFarmTerminated { + global_farm_id: 1, + yield_farm_id: 2, + who: BOB, + asset_pair: BSX_KSM_ASSET_PAIR, + } + .into()); + }); +} + +#[test] +fn terminate_yield_farm_should_fail_when_caller_is_not_signed() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(BOB, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + assert_noop!( + LiquidityMining::terminate_yield_farm(Origin::none(), 1, 2, BSX_KSM_ASSET_PAIR), + BadOrigin + ); + }); +} diff --git a/pallets/xyk-liquidity-mining/src/tests/update_global_farm.rs b/pallets/xyk-liquidity-mining/src/tests/update_global_farm.rs new file mode 100644 index 000000000..43660c11f --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/tests/update_global_farm.rs @@ -0,0 +1,79 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2022 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. + +use super::*; + +#[test] +fn update_global_farm_should_work() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE)]) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .build() + .execute_with(|| { + let new_price_adjustment = FixedU128::from_float(234.1234_f64); + + set_block_number(100_000); + + //Act + assert_ok!(LiquidityMining::update_global_farm( + Origin::signed(BOB), + 1, + new_price_adjustment + )); + + //Assert + assert_last_event!(crate::Event::GlobalFarmUpdated { + id: 1, + price_adjustment: new_price_adjustment, + } + .into()); + }); +} + +#[test] +fn update_global_farm_should_fail_when_origin_is_not_signed() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE)]) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .build() + .execute_with(|| { + assert_noop!( + LiquidityMining::update_global_farm(Origin::none(), BOB_FARM, FixedU128::from_float(0.268_756_6)), + BadOrigin + ); + }); +} diff --git a/pallets/xyk-liquidity-mining/src/tests/update_yield_farm.rs b/pallets/xyk-liquidity-mining/src/tests/update_yield_farm.rs new file mode 100644 index 000000000..6e5afa2a1 --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/tests/update_yield_farm.rs @@ -0,0 +1,114 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2022 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. + +use super::*; + +#[test] +fn update_yield_farm_should_work() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(BOB, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + //Arrange + let new_multiplier = FixedU128::from(5_000_u128); + + //Act + assert_ok!(LiquidityMining::update_yield_farm( + Origin::signed(BOB), + 1, + BSX_KSM_ASSET_PAIR, + new_multiplier + )); + + //Assert + assert_last_event!(crate::Event::YieldFarmUpdated { + global_farm_id: 1, + yield_farm_id: 2, + who: BOB, + asset_pair: BSX_KSM_ASSET_PAIR, + multiplier: new_multiplier, + } + .into()); + }); +} + +#[test] +fn update_yield_farm_should_fail_when_caller_is_not_signed() { + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(BOB, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + assert_noop!( + LiquidityMining::update_yield_farm(Origin::none(), 1, BSX_KSM_ASSET_PAIR, FixedU128::from(10_001)), + BadOrigin + ); + }); +} + +#[test] +fn update_yield_farm_should_fail_when_amm_pool_does_not_exist() { + let pair_without_amm = BSX_DOT_ASSET_PAIR; + + ExtBuilder::default() + .with_endowed_accounts(vec![(BOB, BSX, 1_000_000 * ONE)]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(BOB, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .build() + .execute_with(|| { + assert_noop!( + LiquidityMining::update_yield_farm(Origin::signed(BOB), 1, pair_without_amm, FixedU128::from(10_001)), + Error::::XykPoolDoesntExist + ); + }); +} diff --git a/pallets/xyk-liquidity-mining/src/tests/withdraw_shares.rs b/pallets/xyk-liquidity-mining/src/tests/withdraw_shares.rs new file mode 100644 index 000000000..7d24db698 --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/tests/withdraw_shares.rs @@ -0,0 +1,474 @@ +// This file is part of Basilisk-node + +// Copyright (C) 2020-2022 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. + +use super::*; + +#[test] +fn withdraw_shares_should_work() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + BOB, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_yield_farm(BOB, 2, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 3, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + //Arrange + set_block_number(1_000); + + //redeposit lp shares + assert_ok!(LiquidityMining::redeposit_shares( + Origin::signed(CHARLIE), + 2, + 4, + BSX_KSM_ASSET_PAIR, + 1 + )); + + let charlie_lp_token_balance = Tokens::free_balance(BSX_KSM_SHARE_ID, &CHARLIE); + pretty_assertions::assert_eq!( + Tokens::free_balance(BSX_KSM_SHARE_ID, &LiquidityMining::account_id()), + 100 * ONE + ); + + //Act + assert_ok!(LiquidityMining::withdraw_shares( + Origin::signed(CHARLIE), + 1, + 3, + BSX_KSM_ASSET_PAIR + )); + + //Assert + pretty_assertions::assert_eq!( + has_event( + crate::Event::RewardClaimed { + global_farm_id: 1, + yield_farm_id: 3, + who: CHARLIE, + claimed: 20_000_000 * ONE, + reward_currency: BSX, + deposit_id: 1, + } + .into(), + ), + true + ); + + pretty_assertions::assert_eq!( + has_event( + crate::Event::SharesWithdrawn { + global_farm_id: 1, + yield_farm_id: 3, + who: CHARLIE, + lp_token: BSX_KSM_SHARE_ID, + amount: 100 * ONE, + deposit_id: 1, + } + .into(), + ), + true + ); + + //NOTE: balance should not change because deposit is not destroyed + pretty_assertions::assert_eq!( + Tokens::free_balance(BSX_KSM_SHARE_ID, &CHARLIE), + charlie_lp_token_balance + ); + pretty_assertions::assert_eq!( + Tokens::free_balance(BSX_KSM_SHARE_ID, &LiquidityMining::account_id()), + 100 * ONE + ); + + //Second claim with desposit destruction + set_block_number(2_000); + //Act + assert_ok!(LiquidityMining::withdraw_shares( + Origin::signed(CHARLIE), + 1, + 4, + BSX_KSM_ASSET_PAIR + )); + + //Assert + pretty_assertions::assert_eq!( + has_event( + crate::Event::RewardClaimed { + global_farm_id: 2, + yield_farm_id: 4, + who: CHARLIE, + claimed: 20_000_000 * ONE, + reward_currency: BSX, + deposit_id: 1, + } + .into(), + ), + true + ); + + pretty_assertions::assert_eq!( + has_event( + crate::Event::SharesWithdrawn { + global_farm_id: 2, + yield_farm_id: 4, + who: CHARLIE, + lp_token: BSX_KSM_SHARE_ID, + amount: 100 * ONE, + deposit_id: 1, + } + .into(), + ), + true + ); + + pretty_assertions::assert_eq!( + has_event( + crate::Event::DepositDestroyed { + who: CHARLIE, + deposit_id: 1, + } + .into(), + ), + true + ); + + //NOTE: deposit was destroyed and LP shares unlocked + pretty_assertions::assert_eq!( + Tokens::free_balance(BSX_KSM_SHARE_ID, &CHARLIE), + charlie_lp_token_balance + 100 * ONE + ); + pretty_assertions::assert_eq!( + Tokens::free_balance(BSX_KSM_SHARE_ID, &LiquidityMining::account_id()), + 0 + ); + }); +} + +#[test] +fn withdraw_should_work_when_it_is_in_same_period_as_claim() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 2, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + //Arrange + set_block_number(1_000); + assert_ok!(LiquidityMining::claim_rewards(Origin::signed(CHARLIE), 1, 2)); + + pretty_assertions::assert_eq!(Tokens::free_balance(BSX_KSM_SHARE_ID, &CHARLIE), 100 * ONE); + pretty_assertions::assert_eq!( + Tokens::free_balance(BSX_KSM_SHARE_ID, &LiquidityMining::account_id()), + 100 * ONE + ); + + //Act + assert_ok!(LiquidityMining::withdraw_shares( + Origin::signed(CHARLIE), + 1, + 2, + BSX_KSM_ASSET_PAIR + )); + + //Assert + pretty_assertions::assert_eq!( + has_event( + crate::Event::SharesWithdrawn { + global_farm_id: 1, + yield_farm_id: 2, + who: CHARLIE, + lp_token: BSX_KSM_SHARE_ID, + amount: 100 * ONE, + deposit_id: 1, + } + .into(), + ), + true + ); + + pretty_assertions::assert_eq!( + has_event( + crate::Event::DepositDestroyed { + who: CHARLIE, + deposit_id: 1, + } + .into(), + ), + true + ); + + //lp shares unlocked + pretty_assertions::assert_eq!(Tokens::free_balance(BSX_KSM_SHARE_ID, &CHARLIE), 200 * ONE); + pretty_assertions::assert_eq!( + Tokens::free_balance(BSX_KSM_SHARE_ID, &LiquidityMining::account_id()), + 0 + ); + }); +} + +#[test] +fn withdraw_shares_should_not_work_when_not_owner() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 2, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + const NOT_FNT_OWNER: u128 = BOB; + + assert_noop!( + LiquidityMining::withdraw_shares(Origin::signed(NOT_FNT_OWNER), 1, 2, BSX_KSM_ASSET_PAIR), + Error::::NotDepositOwner + ); + }); +} + +#[test] +fn withdraw_shares_should_fail_when_global_farm_is_not_found() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 2, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + let non_known_farm: u32 = 99999; + + assert_noop!( + LiquidityMining::withdraw_shares(Origin::signed(CHARLIE), 1, non_known_farm, BSX_KSM_ASSET_PAIR), + Error::::DepositDataNotFound + ); + }); +} + +#[test] +fn withdraw_shares_should_fail_when_origin_is_not_signed() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 2, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + assert_noop!( + LiquidityMining::withdraw_shares(Origin::none(), 1, 2, BSX_KSM_ASSET_PAIR), + BadOrigin + ); + }); +} + +#[test] +fn withdraw_shares_should_fail_when_nft_owner_is_not_found() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 2, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + set_block_number(1_000); + + const NOT_EXISTS_DEPOSIT: u128 = 2; + + assert_noop!( + LiquidityMining::withdraw_shares(Origin::signed(CHARLIE), NOT_EXISTS_DEPOSIT, 2, BSX_KSM_ASSET_PAIR), + Error::::CantFindDepositOwner + ); + }); +} + +#[test] +fn withdraw_shares_should_work_when_yield_farm_is_not_claimable() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (BOB, BSX, 1_000_000 * ONE), + (CHARLIE, BSX_KSM_SHARE_ID, 200 * ONE), + ]) + .with_amm_pool(BSX_KSM_AMM, BSX_KSM_SHARE_ID, BSX_KSM_ASSET_PAIR) + .with_global_farm( + 500_000 * ONE, + 20_000, + 10, + BSX, + BSX, + ALICE, + Perquintill::from_percent(1), + ONE, + One::one(), + ) + .with_yield_farm(ALICE, 1, One::one(), None, BSX_KSM_ASSET_PAIR) + .with_deposit(CHARLIE, 1, 2, BSX_KSM_ASSET_PAIR, 100 * ONE) + .build() + .execute_with(|| { + //Arrange + set_block_number(1_000); + + let charlie_lp_token_balance = Tokens::free_balance(BSX_KSM_SHARE_ID, &CHARLIE); + pretty_assertions::assert_eq!( + Tokens::free_balance(BSX_KSM_SHARE_ID, &LiquidityMining::account_id()), + 100 * ONE + ); + + assert_ok!(LiquidityMining::stop_yield_farm( + Origin::signed(ALICE), + 1, + BSX_KSM_ASSET_PAIR + )); + + //Act + assert_ok!(LiquidityMining::withdraw_shares( + Origin::signed(CHARLIE), + 1, + 2, + BSX_KSM_ASSET_PAIR + )); + + //Assert + pretty_assertions::assert_eq!( + has_event( + crate::Event::SharesWithdrawn { + global_farm_id: 1, + yield_farm_id: 2, + who: CHARLIE, + lp_token: BSX_KSM_SHARE_ID, + amount: 100 * ONE, + deposit_id: 1, + } + .into(), + ), + true + ); + + pretty_assertions::assert_eq!( + has_event( + crate::Event::DepositDestroyed { + who: CHARLIE, + deposit_id: 1, + } + .into(), + ), + true + ); + + //NOTE: deposit was destroyed and LP shares unlocked + pretty_assertions::assert_eq!( + Tokens::free_balance(BSX_KSM_SHARE_ID, &CHARLIE), + charlie_lp_token_balance + 100 * ONE + ); + pretty_assertions::assert_eq!( + Tokens::free_balance(BSX_KSM_SHARE_ID, &LiquidityMining::account_id()), + 0 + ); + }); +} diff --git a/pallets/xyk-liquidity-mining/src/weights.rs b/pallets/xyk-liquidity-mining/src/weights.rs new file mode 100644 index 000000000..967fa8e9a --- /dev/null +++ b/pallets/xyk-liquidity-mining/src/weights.rs @@ -0,0 +1,649 @@ +// This file is part of Basilisk. + +// 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_xyk_liquidity_mining` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-06, STEPS: `10`, REPEAT: `30`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// target/release/basilisk +// benchmark +// pallet +// --chain=dev +// --steps=10 +// --repeat=30 +// --wasm-execution=compiled +// --heap-pages=4096 +// --template=.maintain/pallet-weight-template-no-back.hbs +// --pallet=pallet-xyk-liquidity-mining +// --output=xyk_liquidity_mining.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_xyk_liquidity_mining. +pub trait WeightInfo { + fn create_global_farm() -> Weight; + fn update_global_farm() -> Weight; + fn terminate_global_farm() -> Weight; + fn create_yield_farm() -> Weight; + fn update_yield_farm() -> Weight; + fn stop_yield_farm() -> Weight; + fn terminate_yield_farm() -> Weight; + fn deposit_shares() -> Weight; + fn redeposit_shares() -> Weight; + fn claim_rewards() -> Weight; + fn withdraw_shares() -> Weight; + fn resume_yield_farm() -> Weight; +} + +pub struct BasiliskWeight(PhantomData); + +impl WeightInfo for BasiliskWeight { + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::FarmSequencer` (r:1 w:1) + /// Proof: `XYKWarehouseLM::FarmSequencer` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Duster::AccountBlacklist` (r:0 w:1) + /// Proof: `Duster::AccountBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:0 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + fn create_global_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `473` + // Estimated: `6196` + // Minimum execution time: 93_370_000 picoseconds. + Weight::from_parts(94_187_000, 6196) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn update_global_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `879` + // Estimated: `6196` + // Minimum execution time: 98_533_000 picoseconds. + Weight::from_parts(99_357_000, 6196) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Duster::AccountBlacklist` (r:1 w:1) + /// Proof: `Duster::AccountBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + fn terminate_global_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `1002` + // Estimated: `6196` + // Minimum execution time: 95_311_000 picoseconds. + Weight::from_parts(95_938_000, 6196) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::ActiveYieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::ActiveYieldFarm` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::FarmSequencer` (r:1 w:1) + /// Proof: `XYKWarehouseLM::FarmSequencer` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:0 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + fn create_yield_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `1213` + // Estimated: `6196` + // Minimum execution time: 122_647_000 picoseconds. + Weight::from_parts(124_077_000, 6196) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::ActiveYieldFarm` (r:1 w:0) + /// Proof: `XYKWarehouseLM::ActiveYieldFarm` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn update_yield_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `1361` + // Estimated: `6196` + // Minimum execution time: 129_497_000 picoseconds. + Weight::from_parts(130_156_000, 6196) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `XYKWarehouseLM::ActiveYieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::ActiveYieldFarm` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn stop_yield_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `1195` + // Estimated: `6196` + // Minimum execution time: 124_385_000 picoseconds. + Weight::from_parts(125_118_000, 6196) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `XYKWarehouseLM::ActiveYieldFarm` (r:1 w:0) + /// Proof: `XYKWarehouseLM::ActiveYieldFarm` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn terminate_yield_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `904` + // Estimated: `6196` + // Minimum execution time: 99_601_000 picoseconds. + Weight::from_parts(100_152_000, 6196) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:3 w:2) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, 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:4 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `XYK::PoolAssets` (r:1 w:0) + /// Proof: `XYK::PoolAssets` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Storage: `XYK::TotalLiquidity` (r:1 w:0) + /// Proof: `XYK::TotalLiquidity` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::DepositSequencer` (r:1 w:1) + /// Proof: `XYKWarehouseLM::DepositSequencer` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `MultiTransactionPayment::AccountCurrencyMap` (r:1 w:0) + /// Proof: `MultiTransactionPayment::AccountCurrencyMap` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `NFT::Collections` (r:1 w:0) + /// Proof: `NFT::Collections` (`max_values`: None, `max_size`: Some(99), added: 2574, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Asset` (r:1 w:1) + /// Proof: `Uniques::Asset` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Class` (r:1 w:1) + /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(190), added: 2665, mode: `MaxEncodedLen`) + /// Storage: `Uniques::CollectionMaxSupply` (r:1 w:0) + /// Proof: `Uniques::CollectionMaxSupply` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::NextAssetId` (r:1 w:0) + /// Proof: `AssetRegistry::NextAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) + /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Account` (r:0 w:1) + /// Proof: `Uniques::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `NFT::Items` (r:0 w:1) + /// Proof: `NFT::Items` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::Deposit` (r:0 w:1) + /// Proof: `XYKWarehouseLM::Deposit` (`max_values`: None, `max_size`: Some(413), added: 2888, mode: `MaxEncodedLen`) + fn deposit_shares() -> Weight { + // Proof Size summary in bytes: + // Measured: `3236` + // Estimated: `11402` + // Minimum execution time: 257_650_000 picoseconds. + Weight::from_parts(258_966_000, 11402) + .saturating_add(T::DbWeight::get().reads(22_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) + } + /// Storage: `Uniques::Asset` (r:1 w:0) + /// Proof: `Uniques::Asset` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::Deposit` (r:1 w:1) + /// Proof: `XYKWarehouseLM::Deposit` (`max_values`: None, `max_size`: Some(413), added: 2888, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `XYK::PoolAssets` (r:1 w:0) + /// Proof: `XYK::PoolAssets` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Storage: `XYK::TotalLiquidity` (r:1 w:0) + /// Proof: `XYK::TotalLiquidity` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:1 w:0) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + fn redeposit_shares() -> Weight { + // Proof Size summary in bytes: + // Measured: `2363` + // Estimated: `3878` + // Minimum execution time: 82_242_000 picoseconds. + Weight::from_parts(83_084_000, 3878) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Uniques::Asset` (r:1 w:0) + /// Proof: `Uniques::Asset` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::Deposit` (r:1 w:1) + /// Proof: `XYKWarehouseLM::Deposit` (`max_values`: None, `max_size`: Some(413), added: 2888, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:3 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn claim_rewards() -> Weight { + // Proof Size summary in bytes: + // Measured: `2097` + // Estimated: `8799` + // Minimum execution time: 176_232_000 picoseconds. + Weight::from_parts(177_357_000, 8799) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `Uniques::Asset` (r:1 w:1) + /// Proof: `Uniques::Asset` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::Deposit` (r:1 w:1) + /// Proof: `XYKWarehouseLM::Deposit` (`max_values`: None, `max_size`: Some(413), added: 2888, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, 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:4 w:4) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `XYK::PoolAssets` (r:1 w:0) + /// Proof: `XYK::PoolAssets` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:2 w:2) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `MultiTransactionPayment::AccountCurrencyMap` (r:1 w:1) + /// 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`) + /// Storage: `Uniques::Class` (r:1 w:1) + /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(190), added: 2665, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::NextAssetId` (r:1 w:0) + /// Proof: `AssetRegistry::NextAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) + /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Account` (r:0 w:1) + /// Proof: `Uniques::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `Uniques::ItemPriceOf` (r:0 w:1) + /// Proof: `Uniques::ItemPriceOf` (`max_values`: None, `max_size`: Some(113), added: 2588, mode: `MaxEncodedLen`) + /// Storage: `NFT::Items` (r:0 w:1) + /// Proof: `NFT::Items` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + fn withdraw_shares() -> Weight { + // Proof Size summary in bytes: + // Measured: `2928` + // Estimated: `11402` + // Minimum execution time: 383_107_000 picoseconds. + Weight::from_parts(385_120_000, 11402) + .saturating_add(T::DbWeight::get().reads(19_u64)) + .saturating_add(T::DbWeight::get().writes(15_u64)) + } + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::ActiveYieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::ActiveYieldFarm` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn resume_yield_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `1469` + // Estimated: `6196` + // Minimum execution time: 123_753_000 picoseconds. + Weight::from_parts(124_758_000, 6196) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::FarmSequencer` (r:1 w:1) + /// Proof: `XYKWarehouseLM::FarmSequencer` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Duster::AccountBlacklist` (r:0 w:1) + /// Proof: `Duster::AccountBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:0 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + fn create_global_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `473` + // Estimated: `6196` + // Minimum execution time: 93_370_000 picoseconds. + Weight::from_parts(94_187_000, 6196) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn update_global_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `879` + // Estimated: `6196` + // Minimum execution time: 98_533_000 picoseconds. + Weight::from_parts(99_357_000, 6196) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Duster::AccountBlacklist` (r:1 w:1) + /// Proof: `Duster::AccountBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + fn terminate_global_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `1002` + // Estimated: `6196` + // Minimum execution time: 95_311_000 picoseconds. + Weight::from_parts(95_938_000, 6196) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::ActiveYieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::ActiveYieldFarm` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::FarmSequencer` (r:1 w:1) + /// Proof: `XYKWarehouseLM::FarmSequencer` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:0 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + fn create_yield_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `1213` + // Estimated: `6196` + // Minimum execution time: 122_647_000 picoseconds. + Weight::from_parts(124_077_000, 6196) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::ActiveYieldFarm` (r:1 w:0) + /// Proof: `XYKWarehouseLM::ActiveYieldFarm` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn update_yield_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `1361` + // Estimated: `6196` + // Minimum execution time: 129_497_000 picoseconds. + Weight::from_parts(130_156_000, 6196) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: `XYKWarehouseLM::ActiveYieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::ActiveYieldFarm` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn stop_yield_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `1195` + // Estimated: `6196` + // Minimum execution time: 124_385_000 picoseconds. + Weight::from_parts(125_118_000, 6196) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: `XYKWarehouseLM::ActiveYieldFarm` (r:1 w:0) + /// Proof: `XYKWarehouseLM::ActiveYieldFarm` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn terminate_yield_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `904` + // Estimated: `6196` + // Minimum execution time: 99_601_000 picoseconds. + Weight::from_parts(100_152_000, 6196) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:3 w:2) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, 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:4 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `XYK::PoolAssets` (r:1 w:0) + /// Proof: `XYK::PoolAssets` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Storage: `XYK::TotalLiquidity` (r:1 w:0) + /// Proof: `XYK::TotalLiquidity` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::DepositSequencer` (r:1 w:1) + /// Proof: `XYKWarehouseLM::DepositSequencer` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `MultiTransactionPayment::AccountCurrencyMap` (r:1 w:0) + /// Proof: `MultiTransactionPayment::AccountCurrencyMap` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `NFT::Collections` (r:1 w:0) + /// Proof: `NFT::Collections` (`max_values`: None, `max_size`: Some(99), added: 2574, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Asset` (r:1 w:1) + /// Proof: `Uniques::Asset` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Class` (r:1 w:1) + /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(190), added: 2665, mode: `MaxEncodedLen`) + /// Storage: `Uniques::CollectionMaxSupply` (r:1 w:0) + /// Proof: `Uniques::CollectionMaxSupply` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::NextAssetId` (r:1 w:0) + /// Proof: `AssetRegistry::NextAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) + /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Account` (r:0 w:1) + /// Proof: `Uniques::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `NFT::Items` (r:0 w:1) + /// Proof: `NFT::Items` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::Deposit` (r:0 w:1) + /// Proof: `XYKWarehouseLM::Deposit` (`max_values`: None, `max_size`: Some(413), added: 2888, mode: `MaxEncodedLen`) + fn deposit_shares() -> Weight { + // Proof Size summary in bytes: + // Measured: `3236` + // Estimated: `11402` + // Minimum execution time: 257_650_000 picoseconds. + Weight::from_parts(258_966_000, 11402) + .saturating_add(RocksDbWeight::get().reads(22_u64)) + .saturating_add(RocksDbWeight::get().writes(13_u64)) + } + /// Storage: `Uniques::Asset` (r:1 w:0) + /// Proof: `Uniques::Asset` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::Deposit` (r:1 w:1) + /// Proof: `XYKWarehouseLM::Deposit` (`max_values`: None, `max_size`: Some(413), added: 2888, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `XYK::PoolAssets` (r:1 w:0) + /// Proof: `XYK::PoolAssets` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Storage: `XYK::TotalLiquidity` (r:1 w:0) + /// Proof: `XYK::TotalLiquidity` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:1 w:0) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + fn redeposit_shares() -> Weight { + // Proof Size summary in bytes: + // Measured: `2363` + // Estimated: `3878` + // Minimum execution time: 82_242_000 picoseconds. + Weight::from_parts(83_084_000, 3878) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `Uniques::Asset` (r:1 w:0) + /// Proof: `Uniques::Asset` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::Deposit` (r:1 w:1) + /// Proof: `XYKWarehouseLM::Deposit` (`max_values`: None, `max_size`: Some(413), added: 2888, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:3 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn claim_rewards() -> Weight { + // Proof Size summary in bytes: + // Measured: `2097` + // Estimated: `8799` + // Minimum execution time: 176_232_000 picoseconds. + Weight::from_parts(177_357_000, 8799) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: `Uniques::Asset` (r:1 w:1) + /// Proof: `Uniques::Asset` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::Deposit` (r:1 w:1) + /// Proof: `XYKWarehouseLM::Deposit` (`max_values`: None, `max_size`: Some(413), added: 2888, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, 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:4 w:4) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `XYK::PoolAssets` (r:1 w:0) + /// Proof: `XYK::PoolAssets` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:2 w:2) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `MultiTransactionPayment::AccountCurrencyMap` (r:1 w:1) + /// 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`) + /// Storage: `Uniques::Class` (r:1 w:1) + /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(190), added: 2665, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::NextAssetId` (r:1 w:0) + /// Proof: `AssetRegistry::NextAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) + /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Account` (r:0 w:1) + /// Proof: `Uniques::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `Uniques::ItemPriceOf` (r:0 w:1) + /// Proof: `Uniques::ItemPriceOf` (`max_values`: None, `max_size`: Some(113), added: 2588, mode: `MaxEncodedLen`) + /// Storage: `NFT::Items` (r:0 w:1) + /// Proof: `NFT::Items` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) + fn withdraw_shares() -> Weight { + // Proof Size summary in bytes: + // Measured: `2928` + // Estimated: `11402` + // Minimum execution time: 383_107_000 picoseconds. + Weight::from_parts(385_120_000, 11402) + .saturating_add(RocksDbWeight::get().reads(19_u64)) + .saturating_add(RocksDbWeight::get().writes(15_u64)) + } + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::ActiveYieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::ActiveYieldFarm` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn resume_yield_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `1469` + // Estimated: `6196` + // Minimum execution time: 123_753_000 picoseconds. + Weight::from_parts(124_758_000, 6196) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } +} diff --git a/pallets/xyk/Cargo.toml b/pallets/xyk/Cargo.toml index b995efea9..808f1958c 100644 --- a/pallets/xyk/Cargo.toml +++ b/pallets/xyk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = 'pallet-xyk' -version = "6.4.0" +version = "6.4.1" description = 'XYK automated market maker' authors = ['GalacticCouncil'] edition = '2021' diff --git a/runtime/adapters/Cargo.toml b/runtime/adapters/Cargo.toml index 01737a417..f86d28a54 100644 --- a/runtime/adapters/Cargo.toml +++ b/runtime/adapters/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-adapters" -version = "1.2.5" +version = "1.3.0" 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 02382533a..073ff7438 100644 --- a/runtime/adapters/src/lib.rs +++ b/runtime/adapters/src/lib.rs @@ -622,16 +622,16 @@ where } } -pub struct PriceAdjustmentAdapter(PhantomData<(Runtime, LMInstance)>); +pub struct PriceAdjustmentAdapter(PhantomData<(Runtime, LMInstance, OracleSource)>); -impl PriceAdjustment> - for PriceAdjustmentAdapter +impl PriceAdjustment> + for PriceAdjustmentAdapter where Runtime: warehouse_liquidity_mining::Config + pallet_ema_oracle::Config - + pallet_omnipool_liquidity_mining::Config + pallet_asset_registry::Config + pallet_bonds::Config, + OracleSource: Get<[u8; 8]>, u32: EncodeLike<::AssetId>, { type Error = DispatchError; @@ -641,12 +641,12 @@ where use pallet_asset_registry::AssetType; let asset_detail = pallet_asset_registry::Assets::::get(global_farm.reward_currency.into()) - .ok_or(pallet_omnipool_liquidity_mining::Error::::PriceAdjustmentNotAvailable)?; + .ok_or(DispatchError::Other("RewardCurrencyNotFoundInAssetRegistry"))?; let reward_currency_id = if asset_detail.asset_type == AssetType::Bond { let name = asset_detail .name - .ok_or(pallet_omnipool_liquidity_mining::Error::::PriceAdjustmentNotAvailable)?; + .ok_or(DispatchError::Other("PriceAdjustmentNotAvailable"))?; pallet_bonds::Pallet::::parse_bond_name(name.into())? } else { @@ -655,11 +655,11 @@ where let (price, _) = pallet_ema_oracle::Pallet::::get_price( reward_currency_id, - global_farm.incentivized_asset.into(), //LRNA + global_farm.incentivized_asset.into(), OraclePeriod::TenMinutes, - OMNIPOOL_SOURCE, + OracleSource::get(), ) - .map_err(|_| pallet_omnipool_liquidity_mining::Error::::PriceAdjustmentNotAvailable)?; + .map_err(|_| DispatchError::Other("PriceAdjustmentNotAvailable"))?; FixedU128::checked_from_rational(price.n, price.d).ok_or_else(|| ArithmeticError::Overflow.into()) } diff --git a/runtime/adapters/src/tests/mock.rs b/runtime/adapters/src/tests/mock.rs index 9a96428b6..6221899c1 100644 --- a/runtime/adapters/src/tests/mock.rs +++ b/runtime/adapters/src/tests/mock.rs @@ -351,6 +351,10 @@ impl hydradx_traits::registry::Inspect for MockedAssetRegistry { fn asset_symbol(_id: Self::AssetId) -> Option> { unimplemented!() } + + fn existential_deposit(_id: Self::AssetId) -> Option { + unimplemented!() + } } impl pallet_route_executor::Config for Test { type RuntimeEvent = RuntimeEvent; @@ -647,6 +651,10 @@ where fn asset_name(_id: Self::AssetId) -> Option> { unimplemented!() } + + fn existential_deposit(_id: Self::AssetId) -> Option { + unimplemented!() + } } impl CreateRegistry for DummyRegistry diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index aceda94c3..1202cae2b 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "225.0.0" +version = "226.0.0" authors = ["GalacticCouncil"] edition = "2021" license = "Apache 2.0" @@ -40,6 +40,7 @@ pallet-xyk = { workspace = true } pallet-referrals = { workspace = true } pallet-evm-accounts = { workspace = true } pallet-evm-accounts-rpc-runtime-api = { workspace = true } +pallet-xyk-liquidity-mining = { workspace = true } # pallets pallet-balances = { workspace = true } @@ -299,10 +300,10 @@ std = [ "pallet-evm-precompile-modexp/std", "pallet-evm-precompile-bn128/std", "pallet-evm-precompile-blake2/std", - "pallet-xyk/std", "pallet-referrals/std", "pallet-evm-accounts/std", "pallet-evm-accounts-rpc-runtime-api/std", + "pallet-xyk-liquidity-mining/std", ] try-runtime= [ "frame-try-runtime", @@ -371,4 +372,5 @@ try-runtime= [ "pallet-xyk/try-runtime", "pallet-referrals/try-runtime", "pallet-evm-accounts/try-runtime", + "pallet-xyk-liquidity-mining/try-runtime", ] diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 9e7d9cd6f..79c8293c6 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -553,6 +553,7 @@ parameter_types! { pub const MaxYieldFarmsPerGlobalFarm: u8 = 50; //NOTE: Includes deleted/destroyed farms, TODO: pub const MinPlannedYieldingPeriods: BlockNumber = 14_440; //1d with 6s blocks, TODO: pub const MinTotalFarmRewards: Balance = NATIVE_EXISTENTIAL_DEPOSIT * 100; //TODO: + pub const OmnipoolLmOracle: [u8; 8] = OMNIPOOL_SOURCE; } type OmnipoolLiquidityMiningInstance = warehouse_liquidity_mining::Instance1; @@ -569,7 +570,7 @@ impl warehouse_liquidity_mining::Config for Run type MaxYieldFarmsPerGlobalFarm = MaxYieldFarmsPerGlobalFarm; type AssetRegistry = AssetRegistry; type NonDustableWhitelistHandler = Duster; - type PriceAdjustment = PriceAdjustmentAdapter; + type PriceAdjustment = PriceAdjustmentAdapter; } parameter_types! { @@ -593,6 +594,52 @@ impl pallet_omnipool_liquidity_mining::Config for Runtime { type WeightInfo = weights::omnipool_lm::HydraWeight; } +parameter_types! { + pub const XYKWarehouseLMPalletId: PalletId = PalletId(*b"xykLMpID"); + #[derive(PartialEq, Eq)] + pub const XYKLmMaxEntriesPerDeposit: u8 = 5; //NOTE: Rebenchmark when this change + pub const XYKLmMaxYieldFarmsPerGlobalFarm: u8 = 50; //NOTE: Includes deleted/destroyed farms + pub const XYKLmMinPlannedYieldingPeriods: BlockNumber = 14_440; //1d with 6s blocks + pub const XYKLmMinTotalFarmRewards: Balance = NATIVE_EXISTENTIAL_DEPOSIT * 100; + pub const XYKLmOracle: [u8; 8] = XYK_SOURCE; +} + +type XYKLiquidityMiningInstance = warehouse_liquidity_mining::Instance2; +impl warehouse_liquidity_mining::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AssetId = AssetId; + type MultiCurrency = Currencies; + type PalletId = XYKWarehouseLMPalletId; + type MinTotalFarmRewards = XYKLmMinTotalFarmRewards; + type MinPlannedYieldingPeriods = XYKLmMinPlannedYieldingPeriods; + type BlockNumberProvider = RelayChainBlockNumberProvider; + type AmmPoolId = AccountId; + type MaxFarmEntriesPerDeposit = XYKLmMaxEntriesPerDeposit; + type MaxYieldFarmsPerGlobalFarm = XYKLmMaxYieldFarmsPerGlobalFarm; + type AssetRegistry = AssetRegistry; + type NonDustableWhitelistHandler = Duster; + type PriceAdjustment = PriceAdjustmentAdapter; +} + +parameter_types! { + pub const XYKLmPalletId: PalletId = PalletId(*b"XYK///LM"); + pub const XYKLmCollectionId: CollectionId = 5389_u128; +} + +impl pallet_xyk_liquidity_mining::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currencies = Currencies; + type CreateOrigin = AllTechnicalCommitteeMembers; + type PalletId = XYKLmPalletId; + type NFTCollectionId = XYKLmCollectionId; + type NFTHandler = Uniques; + type LiquidityMiningHandler = XYKWarehouseLM; + type NonDustableWhitelistHandler = Duster; + type AMM = XYK; + type AssetRegistry = AssetRegistry; + type WeightInfo = weights::xyk_lm::HydraWeight; +} + // The reason why there is difference between PROD and benchmark is that it is not possible // to set validation data in parachain system pallet in the benchmarks. // So for benchmarking, we mock it out and return some hardcoded parent hash diff --git a/runtime/hydradx/src/benchmarking/mod.rs b/runtime/hydradx/src/benchmarking/mod.rs index 946955e43..836262147 100644 --- a/runtime/hydradx/src/benchmarking/mod.rs +++ b/runtime/hydradx/src/benchmarking/mod.rs @@ -10,6 +10,7 @@ pub mod route_executor; pub mod tokens; pub mod vesting; pub mod xyk; +pub mod xyk_liquidity_mining; use crate::{AssetLocation, AssetRegistry, MultiTransactionPayment}; use frame_system::RawOrigin; diff --git a/runtime/hydradx/src/benchmarking/xyk_liquidity_mining.rs b/runtime/hydradx/src/benchmarking/xyk_liquidity_mining.rs new file mode 100644 index 000000000..d14697b59 --- /dev/null +++ b/runtime/hydradx/src/benchmarking/xyk_liquidity_mining.rs @@ -0,0 +1,588 @@ +// This file is part of HydraDX-node + +// Copyright (C) 2020-2022 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. +use crate::{ + AccountId, AssetId, Balance, BlockNumber, Currencies, EmaOracle, Runtime, System, XYKLiquidityMining, + XYKWarehouseLM, XYK, +}; + +use super::*; + +use frame_benchmarking::{account, BenchmarkError}; +use frame_support::{ + assert_ok, + sp_runtime::{DispatchResult, FixedU128, Perquintill}, + traits::{OnFinalize, OnInitialize}, +}; +use frame_system::RawOrigin; +use hydradx_traits::AMM; +use orml_benchmarking::runtime_benchmarks; +use orml_traits::{MultiCurrency, MultiCurrencyExtended}; +use pallet_xyk::types::AssetPair; +use sp_std::vec; +use warehouse_liquidity_mining::{GlobalFarmId, LoyaltyCurve}; + +pub const HDX: AssetId = 0; + +pub const ONE: Balance = 1_000_000_000_000; + +pub const INITIAL_BALANCE: Balance = 10_000_000 * ONE; + +fn create_gfarm( + owner: AccountId, + incentivized_asset: AssetId, + reward_currency: AssetId, + total_rewards: Balance, +) -> DispatchResult { + let planned_yielding_periods = BlockNumber::from(1_000_000_u32); + let yield_per_period = Perquintill::from_percent(20); + let blocks_per_period = BlockNumber::from(1_u32); + let min_deposit = 1_000; + + XYKLiquidityMining::create_global_farm( + RawOrigin::Root.into(), + total_rewards, + planned_yielding_periods, + blocks_per_period, + incentivized_asset, + reward_currency, + owner, + yield_per_period, + min_deposit, + FixedU128::one(), + ) +} + +fn create_yfarm(caller: AccountId, farm_id: GlobalFarmId, assets: AssetPair, multiplier: FixedU128) -> DispatchResult { + XYKLiquidityMining::create_yield_farm( + RawOrigin::Signed(caller).into(), + farm_id, + assets, + multiplier, + Some(LoyaltyCurve::default()), + ) +} + +fn xyk_add_liquidity(caller: AccountId, assets: AssetPair, amount_a: Balance, amount_b_max: Balance) -> DispatchResult { + XYK::add_liquidity( + RawOrigin::Signed(caller).into(), + assets.asset_in, + assets.asset_out, + amount_a, + amount_b_max, + ) +} + +fn run_to_block(to: u32) { + while System::block_number() < to { + let b = System::block_number(); + + System::on_finalize(b); + EmaOracle::on_finalize(b); + + System::on_initialize(b + 1_u32); + EmaOracle::on_initialize(b + 1_u32); + + System::set_block_number(b + 1_u32); + } +} + +runtime_benchmarks! { + {Runtime, pallet_xyk_liquidity_mining } + + create_global_farm { + let total_rewards = 1_000_000 * ONE; + let planned_yielding_periods = BlockNumber::from(1_000_000_u32); + let yield_per_period = Perquintill::from_percent(20); + let blocks_per_period = BlockNumber::from(1_u32); + let min_deposit = 1_000; + let reward_currency = register_external_asset(b"FCK".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?; + let owner = funded_account("caller", 0, &[HDX, reward_currency]); + }: _(RawOrigin::Root, total_rewards, planned_yielding_periods, blocks_per_period, HDX, reward_currency, owner, yield_per_period, min_deposit, FixedU128::one()) + verify { + assert!(XYKWarehouseLM::global_farm(1).is_some()); + } + + update_global_farm { + let pair = AssetPair { + asset_in: register_external_asset(b"TKN1".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?, + asset_out: register_external_asset(b"TKN2".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))? + }; + + let farm_owner = funded_account("caller", 0, &[HDX, pair.asset_in, pair.asset_out]); + let xyk_caller = funded_account("xyk_caller", 1, &[HDX, pair.asset_in, pair.asset_out]); + let liq_provider = funded_account("liq_provider", 2, &[HDX, pair.asset_in, pair.asset_out]); + + create_xyk_pool(xyk_caller, pair.asset_in, pair.asset_out); + let xyk_id = XYK::pair_account_from_assets(pair.asset_in, pair.asset_out); + xyk_add_liquidity(liq_provider.clone(), pair, 1_000 * ONE, 100_000 * ONE)?; + + let gfarm_id = 1; + let yfarm_id = 2; + create_gfarm(farm_owner.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(farm_owner.clone(), gfarm_id, pair, FixedU128::one())?; + + run_to_block(200); + XYKLiquidityMining::deposit_shares(RawOrigin::Signed(liq_provider).into(), gfarm_id, yfarm_id, pair, 10 * ONE)?; + run_to_block(300); + }: _(RawOrigin::Signed(farm_owner), gfarm_id, FixedU128::from_inner(234_456_677_000_000_000_u128)) + //NOTE: not verified because update prop is not public + + terminate_global_farm { + let pair = AssetPair { + asset_in: register_external_asset(b"TKN1".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?, + asset_out: register_external_asset(b"TKN2".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))? + }; + + let farm_owner = funded_account("caller", 0, &[HDX, pair.asset_in, pair.asset_out]); + let xyk_caller = funded_account("xyk_caller", 1, &[HDX, pair.asset_in, pair.asset_out]); + let liq_provider = funded_account("liq_provider", 2, &[HDX, pair.asset_in, pair.asset_out]); + + create_xyk_pool(xyk_caller, pair.asset_in, pair.asset_out); + let xyk_id = XYK::pair_account_from_assets(pair.asset_in, pair.asset_out); + xyk_add_liquidity(liq_provider, pair, 1_000 * ONE, 100_000 * ONE)?; + + + let gfarm_id = 1; + let yfarm_id = 2; + create_gfarm(farm_owner.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(farm_owner.clone(), gfarm_id, pair, FixedU128::one())?; + + run_to_block(300); + XYKLiquidityMining::stop_yield_farm(RawOrigin::Signed(farm_owner.clone()).into(), gfarm_id, pair)?; + XYKLiquidityMining::terminate_yield_farm(RawOrigin::Signed(farm_owner.clone()).into(), gfarm_id, yfarm_id, pair)?; + run_to_block(400); + }: _(RawOrigin::Signed(farm_owner), gfarm_id) + //NOTE: farm is removed from storage lazylly and prop to check is private + + create_yield_farm { + let pair = AssetPair { + asset_in: register_external_asset(b"TKN1".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?, + asset_out: register_external_asset(b"TKN2".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))? + }; + + let xyk_owner = funded_account("xyk", 0, &[HDX, pair.asset_in, pair.asset_out]); + create_xyk_pool(xyk_owner, pair.asset_in, pair.asset_out); + + let farm_owner = funded_account("caller", 1, &[HDX, pair.asset_in, pair.asset_out]); + let global_farm_id = 1; + create_gfarm(farm_owner.clone(), pair.asset_in, pair.asset_out, 1_000_000 * ONE)?; + }: _(RawOrigin::Signed(farm_owner), global_farm_id, pair, FixedU128::one(), Some(LoyaltyCurve::default())) + verify { + let amm_pool_id = ::AMM::get_pair_id(pair); + + assert!(XYKWarehouseLM::active_yield_farm(amm_pool_id, global_farm_id).is_some()); + } + + update_yield_farm { + let pair = AssetPair { + asset_in: register_external_asset(b"TKN1".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?, + asset_out: register_external_asset(b"TKN2".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))? + }; + + let farm_owner = funded_account("caller", 0, &[HDX, pair.asset_in, pair.asset_out]); + let xyk_caller = funded_account("xyk_caller", 1, &[HDX, pair.asset_in, pair.asset_out]); + let liq_provider = funded_account("liq_provider", 2, &[HDX, pair.asset_in, pair.asset_out]); + + create_xyk_pool(xyk_caller, pair.asset_in, pair.asset_out); + let xyk_id = XYK::pair_account_from_assets(pair.asset_in, pair.asset_out); + xyk_add_liquidity(liq_provider.clone(), pair, 1_000 * ONE, 100_000 * ONE)?; + + + let gfarm_id = 1; + let yfarm_id = 2; + create_gfarm(farm_owner.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(farm_owner.clone(), gfarm_id, pair, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + run_to_block(200); + XYKLiquidityMining::deposit_shares(RawOrigin::Signed(liq_provider).into(), gfarm_id, yfarm_id, pair, 10 * ONE)?; + run_to_block(300); + }: _(RawOrigin::Signed(farm_owner), gfarm_id, pair, FixedU128::one()) + //NOTE: updated field is not public + + stop_yield_farm { + let pair = AssetPair { + asset_in: register_external_asset(b"TKN1".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?, + asset_out: register_external_asset(b"TKN2".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))? + }; + + let farm_owner = funded_account("caller", 0, &[HDX, pair.asset_in, pair.asset_out]); + let xyk_caller = funded_account("xyk_caller", 1, &[HDX, pair.asset_in, pair.asset_out]); + let liq_provider = funded_account("liq_provider", 2, &[HDX, pair.asset_in, pair.asset_out]); + + create_xyk_pool(xyk_caller, pair.asset_in, pair.asset_out); + let xyk_id = XYK::pair_account_from_assets(pair.asset_in, pair.asset_out); + xyk_add_liquidity(liq_provider.clone(), pair, 1_000 * ONE, 100_000 * ONE)?; + + + let gfarm_id = 1; + let yfarm_id = 2; + create_gfarm(farm_owner.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(farm_owner.clone(), gfarm_id, pair, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + run_to_block(200); + XYKLiquidityMining::deposit_shares(RawOrigin::Signed(liq_provider).into(), gfarm_id, yfarm_id, pair, 10 * ONE)?; + run_to_block(300); + }: _(RawOrigin::Signed(farm_owner), gfarm_id, pair) + + terminate_yield_farm { + let pair = AssetPair { + asset_in: register_external_asset(b"TKN1".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?, + asset_out: register_external_asset(b"TKN2".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))? + }; + + let farm_owner = funded_account("caller", 0, &[HDX, pair.asset_in, pair.asset_out]); + let xyk_caller = funded_account("xyk_caller", 1, &[HDX, pair.asset_in, pair.asset_out]); + let liq_provider = funded_account("liq_provider", 2, &[HDX, pair.asset_in, pair.asset_out]); + + create_xyk_pool(xyk_caller, pair.asset_in, pair.asset_out); + let xyk_id = XYK::pair_account_from_assets(pair.asset_in, pair.asset_out); + xyk_add_liquidity(liq_provider.clone(), pair, 1_000 * ONE, 100_000 * ONE)?; + + + let gfarm_id = 1; + let yfarm_id = 2; + create_gfarm(farm_owner.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(farm_owner.clone(), gfarm_id, pair, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + run_to_block(200); + XYKLiquidityMining::deposit_shares(RawOrigin::Signed(liq_provider).into(), gfarm_id, yfarm_id, pair, 10 * ONE)?; + run_to_block(300); + + XYKLiquidityMining::stop_yield_farm(RawOrigin::Signed(farm_owner.clone()).into(), gfarm_id, pair)?; + }: _(RawOrigin::Signed(farm_owner), gfarm_id, yfarm_id, pair) + + deposit_shares { + let pair = AssetPair { + asset_in: register_external_asset(b"TKN1".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?, + asset_out: register_external_asset(b"TKN2".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))? + }; + + let farm_owner = funded_account("caller", 0, &[HDX, pair.asset_in, pair.asset_out]); + let xyk_caller = funded_account("xyk_caller", 1, &[HDX, pair.asset_in, pair.asset_out]); + let liq_provider = funded_account("liq_provider", 2, &[HDX, pair.asset_in, pair.asset_out]); + let liq_provider2 = funded_account("lp2", 3, &[HDX, pair.asset_in, pair.asset_out]); + + create_xyk_pool(xyk_caller, pair.asset_in, pair.asset_out); + let xyk_id = XYK::pair_account_from_assets(pair.asset_in, pair.asset_out); + xyk_add_liquidity(liq_provider.clone(), pair, 1_000 * ONE, 100_000 * ONE)?; + xyk_add_liquidity(liq_provider2.clone(), pair, 1_000 * ONE, 100_000 * ONE)?; + + + let gfarm_id = 1; + let yfarm_id = 2; + create_gfarm(farm_owner.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(farm_owner, gfarm_id, pair, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + run_to_block(200); + XYKLiquidityMining::deposit_shares(RawOrigin::Signed(liq_provider).into(), gfarm_id, yfarm_id, pair, 10 * ONE)?; + run_to_block(300); + + assert!(XYKWarehouseLM::deposit(2).is_none()); + }: _(RawOrigin::Signed(liq_provider2), gfarm_id, yfarm_id, pair, 10 * ONE) + verify { + assert!(XYKWarehouseLM::deposit(2).is_some()); + } + + redeposit_shares { + let pair = AssetPair { + asset_in: register_external_asset(b"TKN1".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?, + asset_out: register_external_asset(b"TKN2".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))? + }; + + let fowner1 = funded_account("fowner1", 0, &[HDX, pair.asset_in, pair.asset_out]); + let fowner2 = funded_account("fowner2", 1, &[HDX, pair.asset_in, pair.asset_out]); + let fowner3 = funded_account("fowner3", 2, &[HDX, pair.asset_in, pair.asset_out]); + let fowner4 = funded_account("fowner4", 3, &[HDX, pair.asset_in, pair.asset_out]); + let fowner5 = funded_account("fowner5", 4, &[HDX, pair.asset_in, pair.asset_out]); + + + let xyk_caller = funded_account("xyk_caller", 1, &[HDX, pair.asset_in, pair.asset_out]); + let lp1 = funded_account("liq_provider", 2, &[HDX, pair.asset_in, pair.asset_out]); + let lp2 = funded_account("lp2", 3, &[HDX, pair.asset_in, pair.asset_out]); + + create_xyk_pool(xyk_caller, pair.asset_in, pair.asset_out); + let xyk_id = XYK::pair_account_from_assets(pair.asset_in, pair.asset_out); + xyk_add_liquidity(lp1.clone(), pair, 1_000 * ONE, 100_000 * ONE)?; + xyk_add_liquidity(lp2.clone(), pair, 1_000 * ONE, 100_000 * ONE)?; + + let lp1_deposit_id = 1; + let gfarm_id1 = 1; + let yfarm_id1 = 2; + + //gId: 1, yId: 2 + create_gfarm(fowner1.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(fowner1, 1, pair, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + //gId: 3, yId: 4 + create_gfarm(fowner2.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(fowner2, 3, pair, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + //gId: 5, yId: 6 + create_gfarm(fowner3.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(fowner3, 5, pair, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + //gId: 7, yId: 8 + create_gfarm(fowner4.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(fowner4, 7, pair, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + //gId: 9, yId: 10 + create_gfarm(fowner5.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(fowner5, 9, pair, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + run_to_block(200); + + XYKLiquidityMining::deposit_shares(RawOrigin::Signed(lp1.clone()).into(), gfarm_id1, yfarm_id1, pair, 10 * ONE)?; + XYKLiquidityMining::redeposit_shares(RawOrigin::Signed(lp1.clone()).into(), 3, 4, pair, lp1_deposit_id)?; + XYKLiquidityMining::redeposit_shares(RawOrigin::Signed(lp1.clone()).into(), 5, 6, pair, lp1_deposit_id)?; + XYKLiquidityMining::redeposit_shares(RawOrigin::Signed(lp1.clone()).into(), 7, 8, pair, lp1_deposit_id)?; + + //Deposit into the global-farm so it will be updated + XYKLiquidityMining::deposit_shares(RawOrigin::Signed(lp2).into(), 9, 10, pair, 10 * ONE)?; + + run_to_block(400); + }: _(RawOrigin::Signed(lp1), 9, 10, pair, lp1_deposit_id) + + claim_rewards { + let pair = AssetPair { + asset_in: register_external_asset(b"TKN1".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?, + asset_out: register_external_asset(b"TKN2".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))? + }; + + let fowner1 = funded_account("fowner1", 0, &[HDX, pair.asset_in, pair.asset_out]); + let fowner2 = funded_account("fowner2", 1, &[HDX, pair.asset_in, pair.asset_out]); + let fowner3 = funded_account("fowner3", 2, &[HDX, pair.asset_in, pair.asset_out]); + let fowner4 = funded_account("fowner4", 3, &[HDX, pair.asset_in, pair.asset_out]); + let fowner5 = funded_account("fowner5", 4, &[HDX, pair.asset_in, pair.asset_out]); + + + let xyk_caller = funded_account("xyk_caller", 1, &[HDX, pair.asset_in, pair.asset_out]); + let lp1 = funded_account("liq_provider", 2, &[HDX, pair.asset_in, pair.asset_out]); + let lp2 = funded_account("lp2", 3, &[HDX, pair.asset_in, pair.asset_out]); + + create_xyk_pool(xyk_caller, pair.asset_in, pair.asset_out); + let xyk_id = XYK::pair_account_from_assets(pair.asset_in, pair.asset_out); + xyk_add_liquidity(lp1.clone(), pair, 1_000 * ONE, 100_000 * ONE)?; + xyk_add_liquidity(lp2.clone(), pair, 1_000 * ONE, 100_000 * ONE)?; + + let lp1_deposit_id = 1; + let gfarm_id1 = 1; + let yfarm_id1 = 2; + + //gId: 1, yId: 2 + create_gfarm(fowner1.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(fowner1, 1, pair, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + //gId: 3, yId: 4 + create_gfarm(fowner2.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(fowner2, 3, pair, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + //gId: 5, yId: 6 + create_gfarm(fowner3.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(fowner3, 5, pair, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + //gId: 7, yId: 8 + create_gfarm(fowner4.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(fowner4, 7, pair, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + //gId: 9, yId: 10 + create_gfarm(fowner5.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(fowner5, 9, pair, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + run_to_block(200); + + XYKLiquidityMining::deposit_shares(RawOrigin::Signed(lp1.clone()).into(), gfarm_id1, yfarm_id1, pair, 10 * ONE)?; + XYKLiquidityMining::redeposit_shares(RawOrigin::Signed(lp1.clone()).into(), 3, 4, pair, lp1_deposit_id)?; + XYKLiquidityMining::redeposit_shares(RawOrigin::Signed(lp1.clone()).into(), 5, 6, pair, lp1_deposit_id)?; + XYKLiquidityMining::redeposit_shares(RawOrigin::Signed(lp1.clone()).into(), 7, 8, pair, lp1_deposit_id)?; + + //Deposit into the global-farm so it will be updated + XYKLiquidityMining::deposit_shares(RawOrigin::Signed(lp2).into(), 9, 10, pair, 10 * ONE)?; + + run_to_block(400); + let lp1_rew_curr_balance = Currencies::free_balance(pair.asset_out, &lp1); + }: _(RawOrigin::Signed(lp1.clone()), lp1_deposit_id, yfarm_id1) + verify { + assert!(Currencies::free_balance(pair.asset_out, &lp1).gt(&lp1_rew_curr_balance)); + } + + withdraw_shares { + let pair = AssetPair { + asset_in: register_external_asset(b"TKN1".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?, + asset_out: register_external_asset(b"TKN2".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))? + }; + + let fowner1 = funded_account("fowner", 0, &[HDX, pair.asset_in, pair.asset_out]); + let xyk_caller = funded_account("xyk_caller", 1, &[HDX, pair.asset_in, pair.asset_out]); + let lp = funded_account("liq_provider", 2, &[HDX, pair.asset_in, pair.asset_out]); + + create_xyk_pool(xyk_caller, pair.asset_in, pair.asset_out); + let xyk_id = XYK::pair_account_from_assets(pair.asset_in, pair.asset_out); + xyk_add_liquidity(lp.clone(), pair, 1_000 * ONE, 100_000 * ONE)?; + + let lp_deposit_id = 1; + let gfarm_id = 1; + let yfarm_id = 2; + + //gId: 1, yId: 2 + create_gfarm(fowner1.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(fowner1, 1, pair, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + run_to_block(200); + + XYKLiquidityMining::deposit_shares(RawOrigin::Signed(lp.clone()).into(), gfarm_id, yfarm_id, pair, 10 * ONE)?; + + run_to_block(400); + + let lp_rew_curr_balance = Currencies::free_balance(pair.asset_out, &lp); + }: _(RawOrigin::Signed(lp.clone()), lp_deposit_id, yfarm_id, pair) + verify { + assert!(Currencies::free_balance(pair.asset_out, &lp).gt(&lp_rew_curr_balance)); + } + + resume_yield_farm { + let pair = AssetPair { + asset_in: register_external_asset(b"TKN1".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?, + asset_out: register_external_asset(b"TKN2".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))? + }; + + //NOTE: pair.asset_in is incentivized asset + let pair2 = AssetPair { + asset_in: pair.asset_in, + asset_out: register_external_asset(b"TKN3".to_vec()).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?, + }; + + let fowner = funded_account("fowner", 0, &[HDX, pair.asset_in, pair.asset_out, pair2.asset_out]); + let xyk_caller = funded_account("xyk_caller", 1, &[HDX, pair.asset_in, pair.asset_out, pair2.asset_out]); + let lp = funded_account("liq_provider", 2, &[HDX, pair.asset_in, pair.asset_out, pair2.asset_out]); + + create_xyk_pool(xyk_caller.clone(), pair.asset_in, pair.asset_out); + let xyk_id_1 = XYK::pair_account_from_assets(pair.asset_in, pair.asset_out); + + create_xyk_pool(xyk_caller, pair2.asset_in, pair2.asset_out); + let xyk_id_2 = XYK::pair_account_from_assets(pair.asset_in, pair.asset_out); + + xyk_add_liquidity(lp.clone(), pair, 1_000 * ONE, 100_000 * ONE)?; + xyk_add_liquidity(lp.clone(), pair2, 1_000 * ONE, 100_000 * ONE)?; + + let lp_deposit_id = 1; + let gfarm_id = 1; + let yfarm_id1 = 2; + let yfarm_id2 = 3; + + //gId: 1, yId: 2 + create_gfarm(fowner.clone(), pair.asset_in, pair.asset_out, 9_000_000 * ONE)?; + create_yfarm(fowner.clone(), 1, pair, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + create_yfarm(fowner.clone(), 1, pair2, FixedU128::from_inner(500_000_000_000_000_000_u128))?; + + run_to_block(200); + + XYKLiquidityMining::stop_yield_farm(RawOrigin::Signed(fowner.clone()).into(), gfarm_id, pair)?; + + run_to_block(300); + + XYKLiquidityMining::deposit_shares(RawOrigin::Signed(lp).into(), gfarm_id, yfarm_id2, pair2, 10 * ONE)?; + + run_to_block(400); + }: _(RawOrigin::Signed(fowner), gfarm_id, yfarm_id1, pair, FixedU128::from(12_452)) +} + +fn funded_account(name: &'static str, index: u32, assets: &[AssetId]) -> AccountId { + let account: AccountId = account(name, index, 0); + for asset in assets { + assert_ok!(>::update_balance( + *asset, + &account, + INITIAL_BALANCE.try_into().unwrap(), + )); + } + account +} + +fn create_xyk_pool(caller: AccountId, asset_a: u32, asset_b: u32) { + assert_ok!(Currencies::update_balance( + RawOrigin::Root.into(), + caller.clone(), + 0, + 10 * ONE as i128, + )); + + let amount = 100_000 * ONE; + assert_ok!(Currencies::update_balance( + RawOrigin::Root.into(), + caller.clone(), + asset_a, + amount as i128, + )); + + assert_ok!(Currencies::update_balance( + RawOrigin::Root.into(), + caller.clone(), + asset_b, + amount as i128, + )); + + assert_ok!(XYK::create_pool( + RawOrigin::Signed(caller.clone()).into(), + asset_a, + amount, + asset_b, + amount, + )); + + assert_ok!(XYK::sell( + RawOrigin::Signed(caller).into(), + asset_a, + asset_b, + 10 * ONE, + 0u128, + false, + )); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::NativeExistentialDeposit; + use orml_benchmarking::impl_benchmark_test_suite; + use sp_runtime::BuildStorage; + + fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_asset_registry::GenesisConfig:: { + registered_assets: vec![], + native_asset_name: b"HDX".to_vec().try_into().unwrap(), + native_existential_deposit: NativeExistentialDeposit::get(), + native_decimals: 12, + native_symbol: b"HDX".to_vec().try_into().unwrap(), + } + .assimilate_storage(&mut t) + .unwrap(); + + as BuildStorage>::assimilate_storage( + &pallet_xyk_liquidity_mining::GenesisConfig::::default(), + &mut t, + ) + .unwrap(); + + sp_io::TestExternalities::new(t) + } + + impl_benchmark_test_suite!(new_test_ext(),); +} diff --git a/runtime/hydradx/src/lib.rs b/runtime/hydradx/src/lib.rs index 8e2faf1c4..a4af2b3ad 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -109,7 +109,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("hydradx"), impl_name: create_runtime_str!("hydradx"), authoring_version: 1, - spec_version: 225, + spec_version: 226, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -191,6 +191,9 @@ construct_runtime!( EVMAccounts: pallet_evm_accounts = 93, DynamicEvmFee: pallet_dynamic_evm_fee = 94, + XYKLiquidityMining: pallet_xyk_liquidity_mining = 95, + XYKWarehouseLM: warehouse_liquidity_mining:: = 96, + // Parachain ParachainSystem: cumulus_pallet_parachain_system exclude_parts { Config } = 103, ParachainInfo: parachain_info = 105, @@ -734,6 +737,7 @@ impl_runtime_apis! { orml_list_benchmark!(list, extra, pallet_dca, benchmarking::dca); orml_list_benchmark!(list, extra, pallet_xyk, benchmarking::xyk); orml_list_benchmark!(list, extra, pallet_dynamic_evm_fee, benchmarking::dynamic_evm_fee); + orml_list_benchmark!(list, extra, pallet_xyk_liquidity_mining, benchmarking::xyk_liquidity_mining); let storage_info = AllPalletsWithSystem::storage_info(); @@ -818,6 +822,7 @@ impl_runtime_apis! { orml_add_benchmark!(params, batches, pallet_dca, benchmarking::dca); orml_add_benchmark!(params, batches, pallet_xyk, benchmarking::xyk); orml_add_benchmark!(params, batches, pallet_dynamic_evm_fee, benchmarking::dynamic_evm_fee); + orml_add_benchmark!(params, batches, pallet_xyk_liquidity_mining, benchmarking::xyk_liquidity_mining); if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } Ok(batches) diff --git a/runtime/hydradx/src/migrations.rs b/runtime/hydradx/src/migrations.rs index 653eb51e0..1a3fa767c 100644 --- a/runtime/hydradx/src/migrations.rs +++ b/runtime/hydradx/src/migrations.rs @@ -9,10 +9,6 @@ pub struct OnRuntimeUpgradeMigration; impl OnRuntimeUpgrade for OnRuntimeUpgradeMigration { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, sp_runtime::DispatchError> { - log::info!("PreMigrate Asset Registry Pallet start"); - pallet_asset_registry::migration::v2::pre_migrate::(); - log::info!("PreMigrate Asset Registry Pallet end"); - log::info!("PreMigrate Collator Selection Pallet start"); let number_of_invulnerables = pallet_collator_selection::migration::v1::MigrateToV1::::pre_upgrade()?; log::info!("PreMigrate Collator Selection Pallet end"); @@ -22,10 +18,6 @@ impl OnRuntimeUpgrade for OnRuntimeUpgradeMigration { fn on_runtime_upgrade() -> Weight { let mut weight: Weight = Weight::zero(); - log::info!("Migrate Asset Registry Pallet"); - weight = weight.saturating_add(pallet_asset_registry::migration::v2::migrate::()); - log::info!("Migrate Asset Registry Pallet end"); - log::info!("Migrate Collator Selection Pallet to v1 start"); weight = weight .saturating_add(pallet_collator_selection::migration::v1::MigrateToV1::::on_runtime_upgrade()); @@ -35,6 +27,10 @@ impl OnRuntimeUpgrade for OnRuntimeUpgradeMigration { weight = weight.saturating_add(orml_unknown_tokens::Migration::::on_runtime_upgrade()); log::info!("Migrate Unknown Tokens Pallet to v2 end"); + log::info!("Migrate pallet xyk-liquidity-mining to v1 start"); + weight = weight.saturating_add(pallet_xyk_liquidity_mining::migration::migrate_to_v1::()); + log::info!("Migrate pallet xyk-liquidity-mining to v1 end"); + let evm_id: u64 = 222_222u64; ChainId::::put(evm_id); weight = weight.saturating_add(::DbWeight::get().reads_writes(0, 1)); @@ -46,10 +42,6 @@ impl OnRuntimeUpgrade for OnRuntimeUpgradeMigration { #[cfg(feature = "try-runtime")] fn post_upgrade(state: Vec) -> Result<(), sp_runtime::DispatchError> { - log::info!("PostMigrate Asset Registry Pallet start"); - pallet_asset_registry::migration::v2::post_migrate::(); - log::info!("PostMigrate Asset Registry Pallet end"); - log::info!("PostMigrate Collator Selection Pallet start"); let migration_result = pallet_collator_selection::migration::v1::MigrateToV1::::post_upgrade(state); log::info!("PostMigrate Collator Selection Pallet end"); diff --git a/runtime/hydradx/src/weights/mod.rs b/runtime/hydradx/src/weights/mod.rs index b4c47676c..c006d22b5 100644 --- a/runtime/hydradx/src/weights/mod.rs +++ b/runtime/hydradx/src/weights/mod.rs @@ -38,3 +38,4 @@ pub mod vesting; pub mod xcm; pub mod xcmp_queue; pub mod xyk; +pub mod xyk_lm; diff --git a/runtime/hydradx/src/weights/xyk_lm.rs b/runtime/hydradx/src/weights/xyk_lm.rs new file mode 100644 index 000000000..3fefbd143 --- /dev/null +++ b/runtime/hydradx/src/weights/xyk_lm.rs @@ -0,0 +1,419 @@ +// 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_xyk_liquidity_mining` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-04-08, STEPS: `10`, REPEAT: `30`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: 1024 + +// Executed Command: +// target/release/hydradx +// benchmark +// pallet +// --chain=dev +// --steps=10 +// --repeat=30 +// --wasm-execution=compiled +// --heap-pages=4096 +// --template=.maintain/pallet-weight-template-no-back.hbs +// --pallet=pallet-xyk-liquidity-mining +// --output=xyk_lm.rs +// --extrinsic=* + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_xyk_liquidity_mining`. +pub struct HydraWeight(PhantomData); +impl pallet_xyk_liquidity_mining::weights::WeightInfo for HydraWeight { + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `Duster::AccountBlacklist` (r:1 w:1) + /// Proof: `Duster::AccountBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::BannedAssets` (r:1 w:0) + /// Proof: `AssetRegistry::BannedAssets` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:3 w:3) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `MultiTransactionPayment::AccountCurrencyMap` (r:3 w:0) + /// Proof: `MultiTransactionPayment::AccountCurrencyMap` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:4 w:4) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::ExistentialDepositCounter` (r:1 w:1) + /// Proof: `AssetRegistry::ExistentialDepositCounter` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `MultiTransactionPayment::AcceptedCurrencies` (r:1 w:0) + /// Proof: `MultiTransactionPayment::AcceptedCurrencies` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::FarmSequencer` (r:1 w:1) + /// Proof: `XYKWarehouseLM::FarmSequencer` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:0 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + fn create_global_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `3099` + // Estimated: `11402` + // Minimum execution time: 345_032_000 picoseconds. + Weight::from_parts(347_056_000, 11402) + .saturating_add(T::DbWeight::get().reads(19)) + .saturating_add(T::DbWeight::get().writes(12)) + } + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:2 w:2) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Oracles` (r:1 w:0) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `Duster::AccountBlacklist` (r:1 w:0) + /// Proof: `Duster::AccountBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::BannedAssets` (r:1 w:0) + /// Proof: `AssetRegistry::BannedAssets` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn update_global_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `4314` + // Estimated: `6156` + // Minimum execution time: 121_473_000 picoseconds. + Weight::from_parts(122_923_000, 6156) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:2 w:2) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `Duster::AccountBlacklist` (r:1 w:1) + /// Proof: `Duster::AccountBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::BannedAssets` (r:1 w:0) + /// Proof: `AssetRegistry::BannedAssets` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `MultiTransactionPayment::AccountCurrencyMap` (r:1 w:0) + /// Proof: `MultiTransactionPayment::AccountCurrencyMap` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::ExistentialDepositCounter` (r:1 w:1) + /// Proof: `AssetRegistry::ExistentialDepositCounter` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + fn terminate_global_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `3811` + // Estimated: `6196` + // Minimum execution time: 203_724_000 picoseconds. + Weight::from_parts(204_983_000, 6196) + .saturating_add(T::DbWeight::get().reads(12)) + .saturating_add(T::DbWeight::get().writes(8)) + } + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::ActiveYieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::ActiveYieldFarm` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::FarmSequencer` (r:1 w:1) + /// Proof: `XYKWarehouseLM::FarmSequencer` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:0 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + fn create_yield_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `1548` + // Estimated: `3670` + // Minimum execution time: 55_477_000 picoseconds. + Weight::from_parts(56_139_000, 3670) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::ActiveYieldFarm` (r:1 w:0) + /// Proof: `XYKWarehouseLM::ActiveYieldFarm` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:2 w:2) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Oracles` (r:1 w:0) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `Duster::AccountBlacklist` (r:1 w:0) + /// Proof: `Duster::AccountBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::BannedAssets` (r:1 w:0) + /// Proof: `AssetRegistry::BannedAssets` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn update_yield_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `4796` + // Estimated: `6156` + // Minimum execution time: 149_365_000 picoseconds. + Weight::from_parts(150_999_000, 6156) + .saturating_add(T::DbWeight::get().reads(11)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `XYKWarehouseLM::ActiveYieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::ActiveYieldFarm` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:2 w:2) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Oracles` (r:1 w:0) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `Duster::AccountBlacklist` (r:1 w:0) + /// Proof: `Duster::AccountBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::BannedAssets` (r:1 w:0) + /// Proof: `AssetRegistry::BannedAssets` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn stop_yield_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `4630` + // Estimated: `6156` + // Minimum execution time: 143_827_000 picoseconds. + Weight::from_parts(144_970_000, 6156) + .saturating_add(T::DbWeight::get().reads(10)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: `XYKWarehouseLM::ActiveYieldFarm` (r:1 w:0) + /// Proof: `XYKWarehouseLM::ActiveYieldFarm` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `Duster::AccountBlacklist` (r:1 w:0) + /// Proof: `Duster::AccountBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::BannedAssets` (r:1 w:0) + /// Proof: `AssetRegistry::BannedAssets` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:2 w:2) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn terminate_yield_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `4677` + // Estimated: `6156` + // Minimum execution time: 114_877_000 picoseconds. + Weight::from_parts(115_998_000, 6156) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:6 w:4) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Oracles` (r:1 w:0) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `Duster::AccountBlacklist` (r:2 w:0) + /// Proof: `Duster::AccountBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::BannedAssets` (r:2 w:0) + /// Proof: `AssetRegistry::BannedAssets` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `XYK::PoolAssets` (r:1 w:0) + /// Proof: `XYK::PoolAssets` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Storage: `XYK::TotalLiquidity` (r:1 w:0) + /// Proof: `XYK::TotalLiquidity` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::DepositSequencer` (r:1 w:1) + /// Proof: `XYKWarehouseLM::DepositSequencer` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Asset` (r:1 w:1) + /// Proof: `Uniques::Asset` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Class` (r:1 w:1) + /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(190), added: 2665, mode: `MaxEncodedLen`) + /// Storage: `Uniques::CollectionMaxSupply` (r:1 w:0) + /// Proof: `Uniques::CollectionMaxSupply` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Account` (r:0 w:1) + /// Proof: `Uniques::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::Deposit` (r:0 w:1) + /// Proof: `XYKWarehouseLM::Deposit` (`max_values`: None, `max_size`: Some(413), added: 2888, mode: `MaxEncodedLen`) + fn deposit_shares() -> Weight { + // Proof Size summary in bytes: + // Measured: `5965` + // Estimated: `16488` + // Minimum execution time: 256_115_000 picoseconds. + Weight::from_parts(257_670_000, 16488) + .saturating_add(T::DbWeight::get().reads(24)) + .saturating_add(T::DbWeight::get().writes(11)) + } + /// Storage: `Uniques::Asset` (r:1 w:0) + /// Proof: `Uniques::Asset` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::Deposit` (r:1 w:1) + /// Proof: `XYKWarehouseLM::Deposit` (`max_values`: None, `max_size`: Some(413), added: 2888, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:4 w:2) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Oracles` (r:1 w:0) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `Duster::AccountBlacklist` (r:1 w:0) + /// Proof: `Duster::AccountBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::BannedAssets` (r:1 w:0) + /// Proof: `AssetRegistry::BannedAssets` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `XYK::PoolAssets` (r:1 w:0) + /// Proof: `XYK::PoolAssets` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Storage: `XYK::TotalLiquidity` (r:1 w:0) + /// Proof: `XYK::TotalLiquidity` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) + fn redeposit_shares() -> Weight { + // Proof Size summary in bytes: + // Measured: `6894` + // Estimated: `11322` + // Minimum execution time: 194_931_000 picoseconds. + Weight::from_parts(196_752_000, 11322) + .saturating_add(T::DbWeight::get().reads(16)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: `Uniques::Asset` (r:1 w:0) + /// Proof: `Uniques::Asset` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::Deposit` (r:1 w:1) + /// Proof: `XYKWarehouseLM::Deposit` (`max_values`: None, `max_size`: Some(413), added: 2888, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:3 w:3) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Oracles` (r:1 w:0) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `Duster::AccountBlacklist` (r:2 w:0) + /// Proof: `Duster::AccountBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::BannedAssets` (r:1 w:0) + /// Proof: `AssetRegistry::BannedAssets` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn claim_rewards() -> Weight { + // Proof Size summary in bytes: + // Measured: `6769` + // Estimated: `8739` + // Minimum execution time: 205_147_000 picoseconds. + Weight::from_parts(207_113_000, 8739) + .saturating_add(T::DbWeight::get().reads(14)) + .saturating_add(T::DbWeight::get().writes(6)) + } + /// Storage: `Uniques::Asset` (r:1 w:1) + /// Proof: `Uniques::Asset` (`max_values`: None, `max_size`: Some(146), added: 2621, mode: `MaxEncodedLen`) + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::Deposit` (r:1 w:1) + /// Proof: `XYKWarehouseLM::Deposit` (`max_values`: None, `max_size`: Some(413), added: 2888, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:5 w:5) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Oracles` (r:1 w:0) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `Duster::AccountBlacklist` (r:3 w:0) + /// Proof: `Duster::AccountBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::BannedAssets` (r:2 w:0) + /// Proof: `AssetRegistry::BannedAssets` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:4 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `XYK::PoolAssets` (r:1 w:0) + /// Proof: `XYK::PoolAssets` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Storage: `MultiTransactionPayment::AccountCurrencyMap` (r:1 w:0) + /// Proof: `MultiTransactionPayment::AccountCurrencyMap` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::ExistentialDepositCounter` (r:1 w:1) + /// Proof: `AssetRegistry::ExistentialDepositCounter` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Class` (r:1 w:1) + /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(190), added: 2665, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Account` (r:0 w:1) + /// Proof: `Uniques::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `Uniques::ItemPriceOf` (r:0 w:1) + /// Proof: `Uniques::ItemPriceOf` (`max_values`: None, `max_size`: Some(113), added: 2588, mode: `MaxEncodedLen`) + fn withdraw_shares() -> Weight { + // Proof Size summary in bytes: + // Measured: `6442` + // Estimated: `13905` + // Minimum execution time: 439_033_000 picoseconds. + Weight::from_parts(442_308_000, 13905) + .saturating_add(T::DbWeight::get().reads(28)) + .saturating_add(T::DbWeight::get().writes(16)) + } + /// Storage: `XYK::ShareToken` (r:1 w:0) + /// Proof: `XYK::ShareToken` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::ActiveYieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::ActiveYieldFarm` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::YieldFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::YieldFarm` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Storage: `XYKWarehouseLM::GlobalFarm` (r:1 w:1) + /// Proof: `XYKWarehouseLM::GlobalFarm` (`max_values`: None, `max_size`: Some(205), added: 2680, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:2 w:2) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Oracles` (r:1 w:0) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `Duster::AccountBlacklist` (r:1 w:0) + /// Proof: `Duster::AccountBlacklist` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::BannedAssets` (r:1 w:0) + /// Proof: `AssetRegistry::BannedAssets` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn resume_yield_farm() -> Weight { + // Proof Size summary in bytes: + // Measured: `5088` + // Estimated: `6156` + // Minimum execution time: 145_447_000 picoseconds. + Weight::from_parts(147_388_000, 6156) + .saturating_add(T::DbWeight::get().reads(11)) + .saturating_add(T::DbWeight::get().writes(5)) + } +} diff --git a/traits/Cargo.toml b/traits/Cargo.toml index dbc0b43e4..b83038bd9 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-traits" -version = "3.1.1" +version = "3.2.0" description = "Shared traits" authors = ["GalacticCouncil"] edition = "2021" diff --git a/traits/src/liquidity_mining.rs b/traits/src/liquidity_mining.rs index 550f3a3da..d79a8b809 100644 --- a/traits/src/liquidity_mining.rs +++ b/traits/src/liquidity_mining.rs @@ -173,3 +173,7 @@ pub trait PriceAdjustment { /// Returns value of `PriceAdjustment` for given `GlobalFarm`. fn get(global_farm: &GlobalFarm) -> Result; } + +pub trait Inspect { + fn pot_account() -> Option; +} diff --git a/traits/src/registry.rs b/traits/src/registry.rs index 3dbcac14f..55f64e4b8 100644 --- a/traits/src/registry.rs +++ b/traits/src/registry.rs @@ -46,6 +46,8 @@ pub trait Inspect { fn asset_name(id: Self::AssetId) -> Option>; fn asset_symbol(id: Self::AssetId) -> Option>; + + fn existential_deposit(id: Self::AssetId) -> Option; } #[allow(clippy::too_many_arguments)]