From 3b8cca792a902a1cdfb76a56146c11ad8e53910f Mon Sep 17 00:00:00 2001 From: martinfridrich Date: Tue, 13 Feb 2024 17:53:38 +0100 Subject: [PATCH 1/4] LM-price-adjustment-adapter: added support to use oracle of underlying asset if rewards currency is bond. pallet-bonds: added fn to parse underlying asset id from bond's name --- Cargo.lock | 2 ++ pallets/bonds/src/lib.rs | 12 ++++++++++- pallets/bonds/src/tests/mod.rs | 2 ++ pallets/bonds/src/tests/tests.rs | 37 ++++++++++++++++++++++++++++++++ runtime/adapters/Cargo.toml | 5 +++++ runtime/adapters/src/lib.rs | 20 ++++++++++++++--- 6 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 pallets/bonds/src/tests/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 3424f3319..060cf40a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4586,7 +4586,9 @@ dependencies = [ "orml-utilities", "orml-vesting", "orml-xcm-support", + "pallet-asset-registry", "pallet-balances", + "pallet-bonds", "pallet-circuit-breaker", "pallet-currencies", "pallet-dynamic-fees", diff --git a/pallets/bonds/src/lib.rs b/pallets/bonds/src/lib.rs index fd3132122..2b97c478d 100644 --- a/pallets/bonds/src/lib.rs +++ b/pallets/bonds/src/lib.rs @@ -53,7 +53,7 @@ use frame_support::{ }; use frame_system::{ensure_signed, pallet_prelude::OriginFor}; use sp_core::MaxEncodedLen; -use sp_std::vec::Vec; +use sp_std::{mem, vec::Vec}; use hydradx_traits::{AssetKind, CreateRegistry, Registry}; use orml_traits::{GetByKey, MultiCurrency}; @@ -179,6 +179,8 @@ pub mod pallet { InvalidMaturity, /// Asset type not allowed for underlying asset DisallowedAsset, + /// Bond's name parsing was now successful + FailToParseName, } #[pallet::call] @@ -313,4 +315,12 @@ impl Pallet { buf } + + pub fn parse_bond_name(name: Vec) -> Result> { + Ok(AssetId::from_le_bytes( + name[..mem::size_of::()] + .try_into() + .map_err(|_| Error::::FailToParseName)?, + )) + } } diff --git a/pallets/bonds/src/tests/mod.rs b/pallets/bonds/src/tests/mod.rs index 7119df845..314d45977 100644 --- a/pallets/bonds/src/tests/mod.rs +++ b/pallets/bonds/src/tests/mod.rs @@ -1,3 +1,5 @@ mod issue; pub mod mock; mod redeem; +#[allow(clippy::module_inception)] +mod tests; diff --git a/pallets/bonds/src/tests/tests.rs b/pallets/bonds/src/tests/tests.rs new file mode 100644 index 000000000..1443ef77b --- /dev/null +++ b/pallets/bonds/src/tests/tests.rs @@ -0,0 +1,37 @@ +// 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. + +use crate::tests::mock::*; +use crate::*; + +#[test] +fn parse_name_should_work() { + let n = Pallet::::bond_name(0_u32, 1689844300000_u64); + assert_eq!(Pallet::::parse_bond_name(n), Ok(0_u32)); + + let n = Pallet::::bond_name(u32::max_value(), 1689844300000_u64); + assert_eq!(Pallet::::parse_bond_name(n), Ok(u32::max_value())); + + let n = Pallet::::bond_name(1, 1689844300000_u64); + assert_eq!(Pallet::::parse_bond_name(n), Ok(1)); + + let n = Pallet::::bond_name(13_124, 1689844300000_u64); + assert_eq!(Pallet::::parse_bond_name(n), Ok(13_124)); + + let n = Pallet::::bond_name(789_970_979, 1689844300000_u64); + assert_eq!(Pallet::::parse_bond_name(n), Ok(789_970_979)); +} diff --git a/runtime/adapters/Cargo.toml b/runtime/adapters/Cargo.toml index 7c3582797..b998ff638 100644 --- a/runtime/adapters/Cargo.toml +++ b/runtime/adapters/Cargo.toml @@ -30,6 +30,8 @@ pallet-currencies = { workspace = true } pallet-stableswap = { workspace = true } pallet-referrals = { workspace = true } pallet-lbp = { workspace = true } +pallet-asset-registry = { workspace = true } +pallet-bonds = { workspace = true } # Substrate dependencies frame-support = { workspace = true } @@ -87,4 +89,7 @@ std = [ "cumulus-pallet-parachain-system/std", "polkadot-parachain/std", "orml-tokens/std", + "pallet-asset-registry/std", + "pallet-omnipool-liquidity-mining/std", + "pallet-bonds/std", ] diff --git a/runtime/adapters/src/lib.rs b/runtime/adapters/src/lib.rs index 76cd29ac9..4ed4d16f1 100644 --- a/runtime/adapters/src/lib.rs +++ b/runtime/adapters/src/lib.rs @@ -17,7 +17,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use codec::FullCodec; +use codec::{EncodeLike, FullCodec}; use cumulus_primitives_core::relay_chain::Hash; use frame_support::{ sp_runtime::{ @@ -629,14 +629,28 @@ impl PriceAdjustment> where Runtime: warehouse_liquidity_mining::Config + pallet_ema_oracle::Config - + pallet_omnipool_liquidity_mining::Config, + + pallet_omnipool_liquidity_mining::Config + + pallet_asset_registry::Config + + pallet_bonds::Config, + u32: EncodeLike<::AssetId>, { type Error = DispatchError; type PriceAdjustment = FixedU128; fn get(global_farm: &GlobalFarmData) -> Result { + use pallet_asset_registry::AssetType; + + let rew_curr = pallet_asset_registry::Assets::::get(global_farm.reward_currency.into()) + .ok_or(pallet_omnipool_liquidity_mining::Error::::PriceAdjustmentNotAvailable)?; + + let reward_currency_id = if rew_curr.asset_type == AssetType::Bond { + pallet_bonds::Pallet::::parse_bond_name(rew_curr.name.into())? + } else { + global_farm.reward_currency.into() + }; + let (price, _) = pallet_ema_oracle::Pallet::::get_price( - global_farm.reward_currency.into(), + reward_currency_id, global_farm.incentivized_asset.into(), //LRNA OraclePeriod::TenMinutes, OMNIPOOL_SOURCE, From 2de68b671062b5174131f860b95a24afc9f55f81 Mon Sep 17 00:00:00 2001 From: martinfridrich Date: Thu, 15 Feb 2024 10:44:12 +0100 Subject: [PATCH 2/4] omnipool-liquidity-mining: added integration tests for bonds distribution --- .../src/omnipool_liquidity_mining.rs | 141 ++++++++++++++++-- 1 file changed, 127 insertions(+), 14 deletions(-) diff --git a/integration-tests/src/omnipool_liquidity_mining.rs b/integration-tests/src/omnipool_liquidity_mining.rs index b117f3d8d..6d6cd12d1 100644 --- a/integration-tests/src/omnipool_liquidity_mining.rs +++ b/integration-tests/src/omnipool_liquidity_mining.rs @@ -26,15 +26,17 @@ use warehouse_liquidity_mining::{ }; use orml_traits::MultiCurrency; -use primitives::AssetId; +use primitives::{constants::currency::UNITS, AssetId}; use sp_runtime::{ traits::{One, Zero}, FixedPointNumber, FixedU128, Permill, Perquintill, }; use xcm_emulator::TestExt; -use hydradx_runtime::{Balance, RuntimeOrigin}; +use hydradx_runtime::{AssetRegistry, Balance, Bonds, RuntimeOrigin}; +use pallet_asset_registry::AssetType; use pretty_assertions::assert_eq; +use primitives::constants::time::unix_time::MONTH; #[macro_export] macro_rules! assert_nft_owner { @@ -113,7 +115,7 @@ fn create_yield_farm_should_work_when_asset_is_in_omnipool() { init_omnipool(); set_relaychain_block_number(100); - create_global_farm(); + create_global_farm(None); set_relaychain_block_number(200); assert_ok!(hydradx_runtime::OmnipoolLiquidityMining::create_yield_farm( @@ -152,7 +154,7 @@ fn deposit_shares_should_work_when_yield_farm_exists() { //NOTE: necessary to get oracle price. hydradx_run_to_block(100); set_relaychain_block_number(100); - create_global_farm(); + create_global_farm(None); set_relaychain_block_number(200); create_yield_farm(global_farm_id, ETH); @@ -223,8 +225,8 @@ fn redeposit_shares_multiple_times_should_work_when_shares_already_deposited() { //NOTE: necessary to get oracle price. hydradx_run_to_block(100); set_relaychain_block_number(100); - create_global_farm(); - create_global_farm(); + create_global_farm(None); + create_global_farm(None); set_relaychain_block_number(200); create_yield_farm(global_farm_1_id, ETH); @@ -314,8 +316,8 @@ fn claim_rewards_should_work_when_rewards_are_accumulated_for_deposit() { //NOTE: necessary to get oracle price. hydradx_run_to_block(100); set_relaychain_block_number(100); - create_global_farm(); - create_global_farm(); + create_global_farm(None); + create_global_farm(None); set_relaychain_block_number(200); create_yield_farm(global_farm_1_id, ETH); @@ -419,8 +421,8 @@ fn withdraw_shares_should_work_when_deposit_exists() { //NOTE: necessary to get oracle price. hydradx_run_to_block(100); set_relaychain_block_number(100); - create_global_farm(); - create_global_farm(); + create_global_farm(None); + create_global_farm(None); set_relaychain_block_number(200); create_yield_farm(global_farm_1_id, ETH); @@ -595,7 +597,7 @@ fn init_omnipool() { )); } -fn create_global_farm() { +fn create_global_farm(rewards_currency: Option) { let total_rewards = 1_000_000 * UNITS; assert_ok!(hydradx_runtime::Balances::force_set_balance( @@ -609,7 +611,7 @@ fn create_global_farm() { total_rewards, 1_000_000, 10, - HDX, + rewards_currency.unwrap_or(HDX), Treasury::account_id(), Perquintill::from_parts(570_776_255_707), 1_000, @@ -688,7 +690,7 @@ fn position_should_be_valued_correctly_when_oracle_is_used() { //NOTE: necessary to get oracle price. hydradx_run_to_block(100); set_relaychain_block_number(100); - create_global_farm(); + create_global_farm(None); set_relaychain_block_number(200); create_yield_farm(global_farm_id, ETH); @@ -767,7 +769,7 @@ fn price_adjustment_from_oracle_should_be_saved_in_global_farm_when_oracle_is_av //NOTE: necessary to get oracle price. hydradx_run_to_block(100); set_relaychain_block_number(100); - create_global_farm(); + create_global_farm(None); set_relaychain_block_number(200); create_yield_farm(global_farm_1_id, ETH); @@ -814,3 +816,114 @@ fn price_adjustment_from_oracle_should_be_saved_in_global_farm_when_oracle_is_av ); }); } + +#[test] +fn liquidity_mining_should_work_when_farm_distributes_bonds() { + TestNet::reset(); + + Hydra::execute_with(|| { + let global_farm_1_id = 1; + let yield_farm_1_id = 2; + + //Arrange + init_omnipool(); + seed_lm_pot(); + //necessary for oracle to have a price. + do_lrna_hdx_trade(); + + //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); + + //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, + )); + + //NOTE: necessary to get oracle price. + hydradx_run_to_block(100); + set_relaychain_block_number(100); + create_global_farm(Some(bond_id)); + + set_relaychain_block_number(200); + create_yield_farm(global_farm_1_id, ETH); + + set_relaychain_block_number(300); + + assert_ok!(hydradx_runtime::Currencies::update_balance( + hydradx_runtime::RuntimeOrigin::root(), + CHARLIE.into(), + ETH, + 10_000 * UNITS as i128, + )); + + let position_id = omnipool_add_liquidity(CHARLIE.into(), ETH, 1_000 * UNITS); + assert_nft_owner!( + hydradx_runtime::OmnipoolCollectionId::get(), + position_id, + CHARLIE.into() + ); + + set_relaychain_block_number(400); + let deposit_id = 1; + assert_ok!(hydradx_runtime::OmnipoolLiquidityMining::deposit_shares( + RuntimeOrigin::signed(CHARLIE.into()), + global_farm_1_id, + yield_farm_1_id, + position_id + )); + + let charlie_bonds_balance_0 = hydradx_runtime::Currencies::free_balance(bond_id, &CHARLIE.into()); + set_relaychain_block_number(600); + assert_ok!(hydradx_runtime::OmnipoolLiquidityMining::claim_rewards( + RuntimeOrigin::signed(CHARLIE.into()), + deposit_id, + yield_farm_1_id + )); + + //Assert + let expected_claimed_amount = 393_607_131_u128; + assert_eq!( + hydradx_runtime::Currencies::free_balance(bond_id, &CHARLIE.into()), + charlie_bonds_balance_0 + expected_claimed_amount + ); + + set_relaychain_block_number(700); + let charlie_bonds_balance_0 = hydradx_runtime::Currencies::free_balance(bond_id, &CHARLIE.into()); + assert_ok!(hydradx_runtime::OmnipoolLiquidityMining::withdraw_shares( + RuntimeOrigin::signed(CHARLIE.into()), + deposit_id, + yield_farm_1_id + )); + + let expected_claimed_amount = 229_243_713_u128; + assert_eq!( + hydradx_runtime::Currencies::free_balance(bond_id, &CHARLIE.into()), + charlie_bonds_balance_0 + expected_claimed_amount + ); + + // NOTE: make sure oracle's price adjustment was used. + let global_farm = hydradx_runtime::OmnipoolWarehouseLM::global_farm(global_farm_1_id).unwrap(); + let price_adjustment = DefaultPriceAdjustment::get(&global_farm).unwrap(); + assert_eq!( + price_adjustment, + FixedU128::from_inner(830_817_151_946_084_689_817_u128) + ); + }); +} From dbec1f6b1802181cffd125a60966fce45a10b7ba Mon Sep 17 00:00:00 2001 From: martinfridrich Date: Mon, 19 Feb 2024 17:05:17 +0100 Subject: [PATCH 3/4] adapters: fix PR comments --- runtime/adapters/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/adapters/src/lib.rs b/runtime/adapters/src/lib.rs index 4ed4d16f1..58b97ec05 100644 --- a/runtime/adapters/src/lib.rs +++ b/runtime/adapters/src/lib.rs @@ -640,11 +640,11 @@ where fn get(global_farm: &GlobalFarmData) -> Result { use pallet_asset_registry::AssetType; - let rew_curr = pallet_asset_registry::Assets::::get(global_farm.reward_currency.into()) + let asset_detail = pallet_asset_registry::Assets::::get(global_farm.reward_currency.into()) .ok_or(pallet_omnipool_liquidity_mining::Error::::PriceAdjustmentNotAvailable)?; - let reward_currency_id = if rew_curr.asset_type == AssetType::Bond { - pallet_bonds::Pallet::::parse_bond_name(rew_curr.name.into())? + let reward_currency_id = if asset_detail.asset_type == AssetType::Bond { + pallet_bonds::Pallet::::parse_bond_name(asset_detail.name.into())? } else { global_farm.reward_currency.into() }; From f495f81144456af17b202d3b13cb7a6edf521b2f Mon Sep 17 00:00:00 2001 From: martinfridrich Date: Thu, 22 Feb 2024 11:56:29 +0100 Subject: [PATCH 4/4] bump versions --- Cargo.lock | 8 ++++---- integration-tests/Cargo.toml | 2 +- pallets/bonds/Cargo.toml | 2 +- runtime/adapters/Cargo.toml | 2 +- runtime/hydradx/Cargo.toml | 2 +- runtime/hydradx/src/lib.rs | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ae2076f1d..4d5e11747 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4571,7 +4571,7 @@ dependencies = [ [[package]] name = "hydradx-adapters" -version = "1.1.1" +version = "1.2.0" dependencies = [ "cumulus-pallet-parachain-system", "cumulus-primitives-core", @@ -4621,7 +4621,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "212.0.0" +version = "213.0.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", @@ -7196,7 +7196,7 @@ dependencies = [ [[package]] name = "pallet-bonds" -version = "2.1.0" +version = "2.2.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -11333,7 +11333,7 @@ dependencies = [ [[package]] name = "runtime-integration-tests" -version = "1.18.1" +version = "1.19.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 9a9e1f811..429da4c53 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runtime-integration-tests" -version = "1.18.1" +version = "1.19.0" description = "Integration tests" authors = ["GalacticCouncil"] edition = "2021" diff --git a/pallets/bonds/Cargo.toml b/pallets/bonds/Cargo.toml index 7556569ab..abec15d8f 100644 --- a/pallets/bonds/Cargo.toml +++ b/pallets/bonds/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-bonds" -version = "2.1.0" +version = "2.2.0" authors = ['GalacticCouncil'] edition = "2021" license = "Apache-2.0" diff --git a/runtime/adapters/Cargo.toml b/runtime/adapters/Cargo.toml index 92b7301a2..7a2b14d73 100644 --- a/runtime/adapters/Cargo.toml +++ b/runtime/adapters/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-adapters" -version = "1.1.1" +version = "1.2.0" description = "Structs and other generic types for building runtimes." authors = ["GalacticCouncil"] edition = "2021" diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index 6d98783d9..60179b704 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "212.0.0" +version = "213.0.0" authors = ["GalacticCouncil"] edition = "2021" license = "Apache 2.0" diff --git a/runtime/hydradx/src/lib.rs b/runtime/hydradx/src/lib.rs index 91d059731..eac6d9a40 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -107,7 +107,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("hydradx"), impl_name: create_runtime_str!("hydradx"), authoring_version: 1, - spec_version: 212, + spec_version: 213, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1,