diff --git a/Cargo.lock b/Cargo.lock index 1aa1d42e33..b27ff4d0e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4024,6 +4024,7 @@ dependencies = [ "interbtc-primitives", "log", "mocktopus", + "nomination", "oracle", "orml-tokens", "orml-traits", @@ -5619,6 +5620,7 @@ dependencies = [ "sp-runtime", "sp-std", "staking", + "traits", "vault-registry", ] @@ -9169,6 +9171,7 @@ dependencies = [ "frame-system", "interbtc-primitives", "mocktopus", + "nomination", "oracle", "orml-tokens", "orml-traits", diff --git a/crates/issue/Cargo.toml b/crates/issue/Cargo.toml index 8707a2ff98..905cfb5cc3 100644 --- a/crates/issue/Cargo.toml +++ b/crates/issue/Cargo.toml @@ -31,6 +31,7 @@ fee = { path = "../fee", default-features = false } security = { path = "../security", default-features = false } currency = { path = "../currency", default-features = false } vault-registry = { path = "../vault-registry", default-features = false } +nomination = { path = "../nomination", default-features = false } primitives = { package = "interbtc-primitives", path = "../../primitives", default-features = false } @@ -74,6 +75,7 @@ std = [ "security/std", "currency/std", "vault-registry/std", + "nomination/std", "primitives/std", diff --git a/crates/issue/src/mock.rs b/crates/issue/src/mock.rs index 687f2dbaad..5dc5e370a1 100644 --- a/crates/issue/src/mock.rs +++ b/crates/issue/src/mock.rs @@ -45,6 +45,7 @@ frame_support::construct_runtime!( Fee: fee::{Pallet, Call, Config, Storage}, Staking: staking::{Pallet, Storage, Event}, Currency: currency::{Pallet}, + Nomination: nomination::{Pallet, Call, Config, Storage, Event}, } ); @@ -150,6 +151,12 @@ impl vault_registry::Config for Test { type Balance = Balance; type WeightInfo = (); type GetGriefingCollateralCurrencyId = GetNativeCurrencyId; + type NominationApi = Nomination; +} + +impl nomination::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); } pub struct CurrencyConvert; diff --git a/crates/nomination/Cargo.toml b/crates/nomination/Cargo.toml index 297821925f..a4c9b475b5 100644 --- a/crates/nomination/Cargo.toml +++ b/crates/nomination/Cargo.toml @@ -31,6 +31,7 @@ reward = { path = "../reward", default-features = false } staking = { path = "../staking", default-features = false } primitives = { package = "interbtc-primitives", path = "../../primitives", default-features = false } +traits = { path = "../../crates/traits", default-features = false } # Orml dependencies orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "3fcd3cf9e63fe80fd9671912833a900ba09d1cc0", default-features = false, optional = true } diff --git a/crates/nomination/src/lib.rs b/crates/nomination/src/lib.rs index f150765fac..c15c4d8f33 100644 --- a/crates/nomination/src/lib.rs +++ b/crates/nomination/src/lib.rs @@ -245,9 +245,10 @@ impl Pallet { Error::::CannotWithdrawCollateral ); - ensure!(Self::is_nomination_enabled(), Error::::VaultNominationDisabled); - ensure!(Self::is_opted_in(vault_id)?, Error::::VaultNotOptedInToNomination); - + if &vault_id.account_id != nominator_id { + ensure!(Self::is_nomination_enabled(), Error::::VaultNominationDisabled); + ensure!(Self::is_opted_in(vault_id)?, Error::::VaultNotOptedInToNomination); + } ext::vault_registry::decrease_total_backing_collateral(&vault_id.currencies, &amount)?; } @@ -271,25 +272,28 @@ impl Pallet { nominator_id: &T::AccountId, amount: BalanceOf, ) -> DispatchResult { - ensure!(Self::is_nomination_enabled(), Error::::VaultNominationDisabled); - ensure!(Self::is_opted_in(vault_id)?, Error::::VaultNotOptedInToNomination); - let amount = Amount::new(amount, vault_id.collateral_currency()); - let total_nominated_collateral = Self::get_total_nominated_collateral(vault_id)?; - let new_nominated_collateral = total_nominated_collateral.checked_add(&amount)?; - let max_nominatable_collateral = Self::get_nomination_limit(vault_id); - ensure!( - new_nominated_collateral.le(&max_nominatable_collateral)?, - Error::::NominationExceedsLimit - ); + if &vault_id.account_id != nominator_id { + let total_nominated_collateral = Self::get_total_nominated_collateral(vault_id)?; + let new_nominated_collateral = total_nominated_collateral.checked_add(&amount)?; + let max_nominatable_collateral = Self::get_nomination_limit(vault_id); + + ensure!(Self::is_nomination_enabled(), Error::::VaultNominationDisabled); + ensure!(Self::is_opted_in(vault_id)?, Error::::VaultNotOptedInToNomination); + + ensure!( + new_nominated_collateral.le(&max_nominatable_collateral)?, + Error::::NominationExceedsLimit + ); + amount.transfer(&nominator_id, &vault_id.account_id)?; + } // Withdraw all vault rewards first, to prevent the nominator from withdrawing past rewards ext::fee::withdraw_all_vault_rewards::(vault_id)?; // Deposit `amount` of stake into the vault staking pool ext::staking::deposit_stake::(vault_id, nominator_id, amount.amount())?; - amount.transfer(&nominator_id, &vault_id.account_id)?; amount.lock_on(&vault_id.account_id)?; ext::vault_registry::try_increase_total_backing_collateral(&vault_id.currencies, &amount)?; @@ -366,3 +370,9 @@ impl Pallet { Ok(Amount::new(amount, vault_id.collateral_currency())) } } + +impl traits::NominationApi, Amount> for Pallet { + fn deposit_vault_collateral(vault_id: &DefaultVaultId, amount: &Amount) -> Result<(), DispatchError> { + Pallet::::_deposit_collateral(vault_id, &vault_id.account_id, amount.amount()) + } +} diff --git a/crates/nomination/src/mock.rs b/crates/nomination/src/mock.rs index 7847227a51..4decb22784 100644 --- a/crates/nomination/src/mock.rs +++ b/crates/nomination/src/mock.rs @@ -155,6 +155,7 @@ impl vault_registry::Config for Test { type Balance = Balance; type WeightInfo = (); type GetGriefingCollateralCurrencyId = GetNativeCurrencyId; + type NominationApi = Nomination; } pub struct CurrencyConvert; diff --git a/crates/redeem/Cargo.toml b/crates/redeem/Cargo.toml index 7949aea061..a80c963bec 100644 --- a/crates/redeem/Cargo.toml +++ b/crates/redeem/Cargo.toml @@ -44,6 +44,7 @@ frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = reward = { path = "../reward" } staking = { path = "../staking" } currency = { path = "../currency", features = ["testing-utils"] } +nomination = { path = "../nomination" } # Orml dependencies orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "3fcd3cf9e63fe80fd9671912833a900ba09d1cc0" } @@ -73,6 +74,7 @@ std = [ "fee/std", "security/std", "vault-registry/std", + "nomination/std", "primitives/std", "orml-tokens/std", diff --git a/crates/redeem/src/mock.rs b/crates/redeem/src/mock.rs index c15aa6fd7a..fd5715b783 100644 --- a/crates/redeem/src/mock.rs +++ b/crates/redeem/src/mock.rs @@ -46,6 +46,7 @@ frame_support::construct_runtime!( Fee: fee::{Pallet, Call, Config, Storage}, Staking: staking::{Pallet, Storage, Event}, Currency: currency::{Pallet}, + Nomination: nomination::{Pallet, Call, Config, Storage, Event}, } ); @@ -146,6 +147,12 @@ impl vault_registry::Config for Test { type Balance = Balance; type WeightInfo = (); type GetGriefingCollateralCurrencyId = GetNativeCurrencyId; + type NominationApi = Nomination; +} + +impl nomination::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); } pub struct CurrencyConvert; diff --git a/crates/replace/src/mock.rs b/crates/replace/src/mock.rs index 959c8394bf..87ec71b5cf 100644 --- a/crates/replace/src/mock.rs +++ b/crates/replace/src/mock.rs @@ -177,6 +177,12 @@ impl vault_registry::Config for Test { type Balance = Balance; type WeightInfo = (); type GetGriefingCollateralCurrencyId = GetNativeCurrencyId; + type NominationApi = Nomination; +} + +impl nomination::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); } impl staking::Config for Test { @@ -201,11 +207,6 @@ impl security::Config for Test { type RuntimeEvent = RuntimeEvent; } -impl nomination::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); -} - parameter_types! { pub const MinimumPeriod: Moment = 5; } diff --git a/crates/traits/src/lib.rs b/crates/traits/src/lib.rs index 152e21a3e7..f69c2ecc34 100644 --- a/crates/traits/src/lib.rs +++ b/crates/traits/src/lib.rs @@ -19,3 +19,7 @@ impl ConvertToBigUint for u128 { pub trait OracleApi { fn convert(amount: &Amount, to: CurrencyId) -> Result; } + +pub trait NominationApi { + fn deposit_vault_collateral(vault_id: &VaultId, amount: &Amount) -> Result<(), DispatchError>; +} diff --git a/crates/vault-registry/Cargo.toml b/crates/vault-registry/Cargo.toml index e332cf7814..2df51edab3 100644 --- a/crates/vault-registry/Cargo.toml +++ b/crates/vault-registry/Cargo.toml @@ -35,6 +35,7 @@ currency = { path = "../currency", default-features = false } reward = { path = "../reward", default-features = false } staking = { path = "../staking", default-features = false } primitives = { package = "interbtc-primitives", path = "../../primitives", default-features = false } +traits = { path = "../../crates/traits", default-features = false } # Orml dependencies orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "3fcd3cf9e63fe80fd9671912833a900ba09d1cc0", default-features = false } @@ -44,8 +45,8 @@ orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-li mocktopus = "0.7.0" frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.31", default-features = false } currency = { path = "../currency", default-features = false, features = ["testing-utils"] } -traits = { path = "../../crates/traits", default-features = false } pretty_assertions = "0.7.2" +visibility = { version = "0.0.1" } [features] default = ["std"] diff --git a/crates/vault-registry/src/benchmarking.rs b/crates/vault-registry/src/benchmarking.rs index 87252c9ab9..5bfbb3d858 100644 --- a/crates/vault-registry/src/benchmarking.rs +++ b/crates/vault-registry/src/benchmarking.rs @@ -58,26 +58,6 @@ benchmarks! { VaultRegistry::::register_public_key(origin.clone().into(), public_key).unwrap(); }: _(origin, vault_id.currencies.clone(), amount.into()) - deposit_collateral { - let vault_id = get_vault_id::(); - mint_collateral::(&vault_id.account_id, (1u32 << 31).into()); - let amount = 100u32.into(); - register_vault_with_collateral::(vault_id.clone(), 100000000); - Oracle::::_set_exchange_rate(get_collateral_currency_id::(), - UnsignedFixedPoint::::one() - ).unwrap(); - }: _(RawOrigin::Signed(vault_id.account_id), vault_id.currencies.clone(), amount) - - withdraw_collateral { - let vault_id = get_vault_id::(); - mint_collateral::(&vault_id.account_id, (1u32 << 31).into()); - let amount = 100u32.into(); - register_vault_with_collateral::(vault_id.clone(), 100000000); - Oracle::::_set_exchange_rate(get_collateral_currency_id::(), - UnsignedFixedPoint::::one() - ).unwrap(); - }: _(RawOrigin::Signed(vault_id.account_id), vault_id.currencies.clone(), amount) - register_public_key { let vault_id = get_vault_id::(); mint_collateral::(&vault_id.account_id, (1u32 << 31).into()); diff --git a/crates/vault-registry/src/lib.rs b/crates/vault-registry/src/lib.rs index 619c95beb1..ac536b8879 100644 --- a/crates/vault-registry/src/lib.rs +++ b/crates/vault-registry/src/lib.rs @@ -65,6 +65,7 @@ use sp_std::{ fmt::Debug, vec::Vec, }; +use traits::NominationApi; // value taken from https://github.com/substrate-developer-hub/recipes/blob/master/pallets/ocw-demo/src/lib.rs pub const UNSIGNED_TXS_PRIORITY: u64 = 100; @@ -123,6 +124,8 @@ pub mod pallet { /// Currency used for griefing collateral, e.g. DOT. #[pallet::constant] type GetGriefingCollateralCurrencyId: Get>; + + type NominationApi: NominationApi, Amount>; } #[pallet::hooks] @@ -190,74 +193,6 @@ pub mod pallet { Ok(().into()) } - /// Deposit collateral as a security against stealing the - /// Bitcoin locked with the caller. - /// - /// # Arguments - /// * `amount` - the amount of extra collateral to lock - #[pallet::weight(::WeightInfo::deposit_collateral())] - #[transactional] - pub fn deposit_collateral( - origin: OriginFor, - currency_pair: DefaultVaultCurrencyPair, - #[pallet::compact] amount: BalanceOf, - ) -> DispatchResultWithPostInfo { - let account_id = ensure_signed(origin)?; - - let vault_id = VaultId::new(account_id, currency_pair.collateral, currency_pair.wrapped); - - let vault = Self::get_active_rich_vault_from_id(&vault_id)?; - - let amount = Amount::new(amount, currency_pair.collateral); - - Self::try_deposit_collateral(&vault_id, &amount)?; - - Self::deposit_event(Event::::DepositCollateral { - vault_id: vault.id(), - new_collateral: amount.amount(), - total_collateral: vault.get_total_collateral()?.amount(), - free_collateral: vault.get_free_collateral()?.amount(), - }); - Ok(().into()) - } - - /// Withdraws `amount` of the collateral from the amount locked by - /// the vault corresponding to the origin account - /// The collateral left after withdrawal must be more - /// (free or used in collateral issued tokens) than MinimumCollateralVault - /// and above the SecureCollateralThreshold. Collateral that is currently - /// being used to back issued tokens remains locked until the Vault - /// is used for a redeem request (full release can take multiple redeem requests). - /// - /// # Arguments - /// * `amount` - the amount of collateral to withdraw - /// - /// # Errors - /// * `VaultNotFound` - if no vault exists for the origin account - /// * `InsufficientCollateralAvailable` - if the vault does not own enough collateral - #[pallet::weight(::WeightInfo::withdraw_collateral())] - #[transactional] - pub fn withdraw_collateral( - origin: OriginFor, - currency_pair: DefaultVaultCurrencyPair, - #[pallet::compact] amount: BalanceOf, - ) -> DispatchResultWithPostInfo { - let account_id = ensure_signed(origin)?; - - let vault_id = VaultId::new(account_id, currency_pair.collateral, currency_pair.wrapped); - let vault = Self::get_rich_vault_from_id(&vault_id)?; - - let amount = Amount::new(amount, currency_pair.collateral); - Self::try_withdraw_collateral(&vault_id, &amount)?; - - Self::deposit_event(Event::::WithdrawCollateral { - vault_id: vault.id(), - withdrawn_amount: amount.amount(), - total_collateral: vault.get_total_collateral()?.amount(), - }); - Ok(().into()) - } - /// Registers a new Bitcoin address for the vault. /// /// # Arguments @@ -864,21 +799,7 @@ impl Pallet { /// * `vault_id` - the id of the vault /// * `amount` - the amount of collateral pub fn try_deposit_collateral(vault_id: &DefaultVaultId, amount: &Amount) -> DispatchResult { - // ensure the vault is active - let _vault = Self::get_active_rich_vault_from_id(vault_id)?; - - // will fail if collateral ceiling exceeded - Self::try_increase_total_backing_collateral(&vault_id.currencies, amount)?; - // will fail if free_balance is insufficient - amount.lock_on(&vault_id.account_id)?; - - // withdraw first such that past rewards don't get changed by this deposit - ext::fee::withdraw_all_vault_rewards::(vault_id)?; - - // Deposit `amount` of stake in the pool - ext::staking::deposit_stake::(vault_id, &vault_id.account_id, amount)?; - - Ok(()) + T::NominationApi::deposit_vault_collateral(&vault_id, &amount) } /// Withdraw an `amount` of collateral without checking collateralization @@ -900,20 +821,6 @@ impl Pallet { Ok(()) } - /// Withdraw an `amount` of collateral, ensuring that the vault is sufficiently - /// over-collateralized - /// - /// # Arguments - /// * `vault_id` - the id of the vault - /// * `amount` - the amount of collateral - pub fn try_withdraw_collateral(vault_id: &DefaultVaultId, amount: &Amount) -> DispatchResult { - ensure!( - Self::is_allowed_to_withdraw_collateral(vault_id, amount)?, - Error::::InsufficientCollateral - ); - Self::force_withdraw_collateral(vault_id, amount) - } - /// Checks if the vault would be above the secure threshold after withdrawing collateral pub fn is_allowed_to_withdraw_collateral( vault_id: &DefaultVaultId, diff --git a/crates/vault-registry/src/mock.rs b/crates/vault-registry/src/mock.rs index cf84a471cb..140057b10b 100644 --- a/crates/vault-registry/src/mock.rs +++ b/crates/vault-registry/src/mock.rs @@ -1,5 +1,5 @@ use crate as vault_registry; -use crate::{Config, Error}; +use crate::{ext, Config, Error}; use frame_support::{ parameter_types, traits::{ConstU32, Everything, GenesisBuild}, @@ -14,6 +14,7 @@ use sp_core::H256; use sp_runtime::{ testing::{Header, TestXt}, traits::{BlakeTwo256, IdentityLookup, One, Zero}, + DispatchError, }; use traits::OracleApi; @@ -201,6 +202,30 @@ impl fee::Config for Test { parameter_types! { pub const VaultPalletId: PalletId = PalletId(*b"mod/vreg"); } +pub struct MockDeposit; + +impl traits::NominationApi, currency::Amount> for MockDeposit { + fn deposit_vault_collateral( + vault_id: &VaultId, + amount: ¤cy::Amount, + ) -> Result<(), DispatchError> { + // ensure the vault is active + let _vault = VaultRegistry::get_active_rich_vault_from_id(vault_id)?; + + // will fail if collateral ceiling exceeded + VaultRegistry::try_increase_total_backing_collateral(&vault_id.currencies, amount)?; + // will fail if free_balance is insufficient + amount.lock_on(&vault_id.account_id)?; + + // withdraw first such that past rewards don't get changed by this deposit + ext::fee::withdraw_all_vault_rewards::(vault_id)?; + + // Deposit `amount` of stake in the pool + ext::staking::deposit_stake::(vault_id, &vault_id.account_id, amount)?; + + Ok(()) + } +} impl Config for Test { type PalletId = VaultPalletId; @@ -208,6 +233,7 @@ impl Config for Test { type Balance = Balance; type WeightInfo = (); type GetGriefingCollateralCurrencyId = GetNativeCurrencyId; + type NominationApi = MockDeposit; } impl frame_system::offchain::SendTransactionTypes for Test diff --git a/crates/vault-registry/src/tests.rs b/crates/vault-registry/src/tests.rs index fda58a78d6..83723b62b8 100644 --- a/crates/vault-registry/src/tests.rs +++ b/crates/vault-registry/src/tests.rs @@ -1,8 +1,7 @@ use crate::{ - ext, mock::*, types::{BalanceOf, UpdatableVault}, - BtcPublicKey, CurrencySource, DefaultVaultId, DispatchError, Vault, VaultStatus, + BtcPublicKey, CurrencySource, DefaultVaultId, DispatchError, Vault, }; use codec::Decode; use currency::Amount; @@ -217,70 +216,6 @@ fn register_vault_fails_when_already_registered() { }); } -#[test] -fn deposit_collateral_succeeds() { - run_test(|| { - let id = create_vault(RICH_ID); - let additional = RICH_COLLATERAL - DEFAULT_COLLATERAL; - let res = - VaultRegistry::deposit_collateral(RuntimeOrigin::signed(id.account_id), DEFAULT_CURRENCY_PAIR, additional); - assert_ok!(res); - let new_collateral = ext::currency::get_reserved_balance::(Token(DOT), &id.account_id); - assert_eq!(new_collateral, amount(DEFAULT_COLLATERAL + additional)); - assert_emitted!(Event::DepositCollateral { - vault_id: id, - new_collateral: additional, - total_collateral: RICH_COLLATERAL, - free_collateral: RICH_COLLATERAL - }); - }); -} - -#[test] -fn deposit_collateral_fails_when_vault_does_not_exist() { - run_test(|| { - let res = VaultRegistry::deposit_collateral(RuntimeOrigin::signed(3), DEFAULT_CURRENCY_PAIR, 50); - assert_err!(res, TestError::VaultNotFound); - }) -} - -#[test] -fn withdraw_collateral_succeeds() { - run_test(|| { - let id = create_sample_vault(); - let res = VaultRegistry::withdraw_collateral(RuntimeOrigin::signed(id.account_id), id.currencies.clone(), 50); - assert_ok!(res); - let new_collateral = ext::currency::get_reserved_balance::(Token(DOT), &id.account_id); - assert_eq!(new_collateral, amount(DEFAULT_COLLATERAL - 50)); - assert_emitted!(Event::WithdrawCollateral { - vault_id: id, - withdrawn_amount: 50, - total_collateral: DEFAULT_COLLATERAL - 50 - }); - }); -} - -#[test] -fn withdraw_collateral_fails_when_vault_does_not_exist() { - run_test(|| { - let res = VaultRegistry::withdraw_collateral(RuntimeOrigin::signed(3), DEFAULT_CURRENCY_PAIR, 50); - assert_err!(res, TestError::VaultNotFound); - }) -} - -#[test] -fn withdraw_collateral_fails_when_not_enough_collateral() { - run_test(|| { - let id = create_sample_vault(); - let res = VaultRegistry::withdraw_collateral( - RuntimeOrigin::signed(id.account_id), - id.currencies, - DEFAULT_COLLATERAL + 1, - ); - assert_err!(res, TestError::InsufficientCollateral); - }) -} - #[test] fn try_increase_to_be_issued_tokens_succeeds() { run_test(|| { @@ -717,204 +652,6 @@ fn cancel_replace_tokens_succeeds() { }); } -#[test] -fn liquidate_at_most_liquidation_threshold() { - run_test(|| { - let vault_id = DEFAULT_ID; - - let issued_tokens = 100; - let to_be_issued_tokens = 25; - let to_be_redeemed_tokens = 40; - - let backing_collateral = 10000; - let liquidated_collateral = 1875; // 125 * 10 * 1.5 - let liquidated_for_issued = 1275; // 125 * 10 * 1.5 - - create_vault_with_collateral(&vault_id, backing_collateral); - let liquidation_vault_before = VaultRegistry::get_rich_liquidation_vault(&DEFAULT_CURRENCY_PAIR); - - VaultRegistry::_set_liquidation_collateral_threshold( - DEFAULT_CURRENCY_PAIR, - FixedU128::checked_from_rational(150, 100).unwrap(), // 150% - ); - - VaultRegistry::_set_premium_redeem_threshold( - DEFAULT_CURRENCY_PAIR, - FixedU128::checked_from_rational(175, 100).unwrap(), // 175% - ); - - VaultRegistry::_set_secure_collateral_threshold( - DEFAULT_CURRENCY_PAIR, - FixedU128::checked_from_rational(200, 100).unwrap(), // 200% - ); - - let collateral_before = ext::currency::get_reserved_balance::(Token(DOT), &vault_id.account_id); - assert_eq!(collateral_before, amount(backing_collateral)); // sanity check - - // required for `issue_tokens` to work - assert_ok!(VaultRegistry::try_increase_to_be_issued_tokens( - &vault_id, - &wrapped(issued_tokens) - )); - assert_ok!(VaultRegistry::issue_tokens(&vault_id, &wrapped(issued_tokens))); - assert_ok!(VaultRegistry::try_increase_to_be_issued_tokens( - &vault_id, - &wrapped(to_be_issued_tokens) - )); - assert_ok!(VaultRegistry::try_increase_to_be_redeemed_tokens( - &vault_id, - &wrapped(to_be_redeemed_tokens) - )); - - let vault_orig = >::get(&vault_id).unwrap(); - let backing_collateral_orig = VaultRegistry::get_backing_collateral(&vault_id).unwrap(); - - // set exchange rate - convert_to.mock_safe(convert_with_exchange_rate(10)); - - assert_ok!(VaultRegistry::liquidate_vault(&vault_id)); - - // should only be able to withdraw excess above secure threshold - assert_err!( - VaultRegistry::withdraw_collateral( - RuntimeOrigin::signed(vault_id.account_id), - vault_id.currencies.clone(), - backing_collateral - ), - TestError::InsufficientCollateral - ); - assert_ok!(VaultRegistry::withdraw_collateral( - RuntimeOrigin::signed(vault_id.account_id), - vault_id.currencies.clone(), - backing_collateral - liquidated_collateral - )); - - let liquidation_vault_after = VaultRegistry::get_rich_liquidation_vault(&DEFAULT_CURRENCY_PAIR); - let liquidated_vault = >::get(&vault_id).unwrap(); - assert!(matches!(liquidated_vault.status, VaultStatus::Liquidated)); - assert_emitted!(Event::LiquidateVault { - vault_id: vault_id.clone(), - issued_tokens: vault_orig.issued_tokens, - to_be_issued_tokens: vault_orig.to_be_issued_tokens, - to_be_redeemed_tokens: vault_orig.to_be_redeemed_tokens, - to_be_replaced_tokens: vault_orig.to_be_replaced_tokens, - backing_collateral: backing_collateral_orig.amount(), - status: VaultStatus::Liquidated, - replace_collateral: vault_orig.replace_collateral, - }); - - // check liquidation_vault tokens & collateral - assert_eq!( - liquidation_vault_after.data.issued_tokens, - liquidation_vault_before.data.issued_tokens + issued_tokens - ); - assert_eq!( - liquidation_vault_after.data.to_be_issued_tokens, - liquidation_vault_before.data.to_be_issued_tokens + to_be_issued_tokens - ); - assert_eq!( - liquidation_vault_after.data.to_be_redeemed_tokens, - liquidation_vault_before.data.to_be_redeemed_tokens + to_be_redeemed_tokens - ); - assert_eq!( - ext::currency::get_reserved_balance::(Token(DOT), &liquidation_vault_before.id().account_id), - amount(liquidated_for_issued) - ); - - // check vault tokens & collateral - let user_vault_after = VaultRegistry::get_rich_vault_from_id(&vault_id).unwrap(); - assert_eq!(user_vault_after.data.issued_tokens, 0); - assert_eq!(user_vault_after.data.to_be_issued_tokens, 0); - assert_eq!(user_vault_after.data.to_be_redeemed_tokens, to_be_redeemed_tokens); - assert_eq!( - ext::currency::get_reserved_balance::(Token(DOT), &vault_id.account_id), - amount(liquidated_collateral - liquidated_for_issued) - ); - assert_eq!( - ext::currency::get_free_balance::(Token(DOT), &vault_id.account_id), - amount(DEFAULT_COLLATERAL - liquidated_collateral) - ); - }); -} - -#[test] -fn can_withdraw_only_up_to_custom_threshold() { - run_test(|| { - let vault_id = DEFAULT_ID; - - let issued_tokens = 100; - let exchange_rate = 10; - let secure_threshold = 200u32; // % - let custom_threshold = 300u32; // % - let minimum_backing_collateral = 100 * 10 * 2; - let backing_collateral = 100 * 10 * 3 + 1; - - create_vault_with_collateral(&vault_id, backing_collateral); - - VaultRegistry::_set_secure_collateral_threshold( - DEFAULT_CURRENCY_PAIR, - FixedU128::checked_from_rational(secure_threshold, 100).unwrap(), // 200% - ); - - // set custom threshold for vault - assert_ok!(VaultRegistry::set_custom_secure_threshold( - RuntimeOrigin::signed(vault_id.account_id), - DEFAULT_CURRENCY_PAIR, - UnsignedFixedPoint::checked_from_rational(custom_threshold, 100), - )); - - // set exchange rate - convert_to.mock_safe(convert_with_exchange_rate(exchange_rate)); - - // issue - assert_ok!(VaultRegistry::try_increase_to_be_issued_tokens( - &vault_id, - &wrapped(issued_tokens) - )); - assert_ok!(VaultRegistry::issue_tokens(&vault_id, &wrapped(issued_tokens))); - - // should not be able to withdraw below custom threshold - assert_err!( - VaultRegistry::withdraw_collateral( - RuntimeOrigin::signed(vault_id.account_id), - vault_id.currencies.clone(), - 2 - ), - TestError::InsufficientCollateral - ); - // should be able to withdraw up to custom threshold - assert_ok!(VaultRegistry::withdraw_collateral( - RuntimeOrigin::signed(vault_id.account_id), - vault_id.currencies.clone(), - 1 - )); - - // reset custom threshold - assert_ok!(VaultRegistry::set_custom_secure_threshold( - RuntimeOrigin::signed(vault_id.account_id), - DEFAULT_CURRENCY_PAIR, - None - )); - - // should not be able to withdraw below base secure threshold now - assert_err!( - VaultRegistry::withdraw_collateral( - RuntimeOrigin::signed(vault_id.account_id), - vault_id.currencies.clone(), - backing_collateral - minimum_backing_collateral // will be 1 over - ), - TestError::InsufficientCollateral - ); - - // should be able to withdraw to the secure threshold - assert_ok!(VaultRegistry::withdraw_collateral( - RuntimeOrigin::signed(vault_id.account_id), - vault_id.currencies.clone(), - backing_collateral - minimum_backing_collateral - 1 - )); - }); -} - #[test] fn is_collateral_below_threshold_true_succeeds() { run_test(|| { diff --git a/crates/vault-registry/src/types.rs b/crates/vault-registry/src/types.rs index 709dbeb072..3c0e71b69c 100644 --- a/crates/vault-registry/src/types.rs +++ b/crates/vault-registry/src/types.rs @@ -230,7 +230,7 @@ pub type DefaultVault = Vault< pub type DefaultSystemVault = SystemVault, CurrencyId>; -#[cfg_attr(feature = "integration-tests", visibility::make(pub))] +#[cfg_attr(any(test, feature = "integration-tests"), visibility::make(pub))] trait UpdatableVault { fn increase_issued(&mut self, tokens: &Amount) -> DispatchResult; @@ -701,16 +701,6 @@ pub(crate) struct RichSystemVault { #[cfg_attr(test, mockable)] impl RichSystemVault { - #[cfg(test)] - pub(crate) fn id(&self) -> DefaultVaultId { - let account_id = Pallet::::liquidation_vault_account_id(); - VaultId::new( - account_id, - self.data.currency_pair.collateral, - self.data.currency_pair.wrapped, - ) - } - pub(crate) fn burn_issued(&mut self, tokens: &Amount) -> DispatchResult { self.decrease_issued(tokens) } diff --git a/parachain/runtime/interlay/src/lib.rs b/parachain/runtime/interlay/src/lib.rs index 1423264a5d..c1f9b56f99 100644 --- a/parachain/runtime/interlay/src/lib.rs +++ b/parachain/runtime/interlay/src/lib.rs @@ -976,6 +976,7 @@ impl vault_registry::Config for Runtime { type Balance = Balance; type WeightInfo = (); type GetGriefingCollateralCurrencyId = GetNativeCurrencyId; + type NominationApi = Nomination; } impl frame_system::offchain::SendTransactionTypes for Runtime diff --git a/parachain/runtime/kintsugi/src/lib.rs b/parachain/runtime/kintsugi/src/lib.rs index 3f86caba63..b3523c19bf 100644 --- a/parachain/runtime/kintsugi/src/lib.rs +++ b/parachain/runtime/kintsugi/src/lib.rs @@ -976,6 +976,7 @@ impl vault_registry::Config for Runtime { type Balance = Balance; type WeightInfo = (); type GetGriefingCollateralCurrencyId = GetNativeCurrencyId; + type NominationApi = Nomination; } impl frame_system::offchain::SendTransactionTypes for Runtime diff --git a/parachain/runtime/testnet-interlay/src/lib.rs b/parachain/runtime/testnet-interlay/src/lib.rs index 1019ee7a8c..954ddfe860 100644 --- a/parachain/runtime/testnet-interlay/src/lib.rs +++ b/parachain/runtime/testnet-interlay/src/lib.rs @@ -945,6 +945,7 @@ impl vault_registry::Config for Runtime { type Balance = Balance; type WeightInfo = (); type GetGriefingCollateralCurrencyId = GetNativeCurrencyId; + type NominationApi = Nomination; } impl frame_system::offchain::SendTransactionTypes for Runtime diff --git a/parachain/runtime/testnet-kintsugi/src/lib.rs b/parachain/runtime/testnet-kintsugi/src/lib.rs index 750b3510ef..8aef778e1d 100644 --- a/parachain/runtime/testnet-kintsugi/src/lib.rs +++ b/parachain/runtime/testnet-kintsugi/src/lib.rs @@ -945,6 +945,7 @@ impl vault_registry::Config for Runtime { type Balance = Balance; type WeightInfo = (); type GetGriefingCollateralCurrencyId = GetNativeCurrencyId; + type NominationApi = Nomination; } impl frame_system::offchain::SendTransactionTypes for Runtime diff --git a/standalone/runtime/src/lib.rs b/standalone/runtime/src/lib.rs index 9226aa48a3..9116d9d46d 100644 --- a/standalone/runtime/src/lib.rs +++ b/standalone/runtime/src/lib.rs @@ -874,6 +874,7 @@ impl vault_registry::Config for Runtime { type Balance = Balance; type WeightInfo = (); type GetGriefingCollateralCurrencyId = GetNativeCurrencyId; + type NominationApi = Nomination; } impl frame_system::offchain::SendTransactionTypes for Runtime diff --git a/standalone/runtime/tests/mock/mod.rs b/standalone/runtime/tests/mock/mod.rs index a8c73df499..d511e9125f 100644 --- a/standalone/runtime/tests/mock/mod.rs +++ b/standalone/runtime/tests/mock/mod.rs @@ -1064,9 +1064,8 @@ pub fn get_register_vault_result(vault_id: &VaultId, collateral: Amount pub fn try_register_vault(collateral: Amount, vault_id: &VaultId) { if VaultRegistryPallet::get_vault_from_id(vault_id).is_err() { - if TokensPallet::accounts(vault_id.account_id.clone(), vault_id.collateral_currency()).free - < collateral.amount() - { + let q = TokensPallet::accounts(vault_id.account_id.clone(), vault_id.collateral_currency()); + if q.free < collateral.amount() { // register vault if not yet registered assert_ok!(RuntimeCall::Tokens(TokensCall::set_balance { who: vault_id.account_id.clone(), diff --git a/standalone/runtime/tests/mock/nomination_testing_utils.rs b/standalone/runtime/tests/mock/nomination_testing_utils.rs index 6e584c9aaf..d9d7f55224 100644 --- a/standalone/runtime/tests/mock/nomination_testing_utils.rs +++ b/standalone/runtime/tests/mock/nomination_testing_utils.rs @@ -74,8 +74,9 @@ pub fn assert_nominate_collateral(vault_id: &VaultId, nominator_id: AccountId, a } pub fn withdraw_vault_collateral(vault_id: &VaultId, amount_collateral: Amount) -> DispatchResultWithPostInfo { - RuntimeCall::VaultRegistry(VaultRegistryCall::withdraw_collateral { - currency_pair: vault_id.currencies.clone(), + RuntimeCall::Nomination(NominationCall::withdraw_collateral { + vault_id: vault_id.clone(), + index: None, amount: amount_collateral.amount(), }) .dispatch(origin_of(vault_id.account_id.clone())) diff --git a/standalone/runtime/tests/test_fee_pool.rs b/standalone/runtime/tests/test_fee_pool.rs index 22560ac061..a9f8820a68 100644 --- a/standalone/runtime/tests/test_fee_pool.rs +++ b/standalone/runtime/tests/test_fee_pool.rs @@ -590,8 +590,8 @@ fn do_random_nomination_sequence() { let max_amount = CoreVaultData::vault(vault_id.clone()).free_balance[&vault_id.collateral_currency()].amount(); let amount = rng.gen_range(0..max_amount); - assert_ok!(RuntimeCall::VaultRegistry(VaultRegistryCall::deposit_collateral { - currency_pair: vault_id.currencies.clone(), + assert_ok!(RuntimeCall::Nomination(NominationCall::deposit_collateral { + vault_id: vault_id.clone(), amount, }) .dispatch(origin_of(vault_id.account_id.clone()))); @@ -616,8 +616,9 @@ fn do_random_nomination_sequence() { continue; } let amount = rng.gen_range(0..max_amount); - assert_ok!(RuntimeCall::VaultRegistry(VaultRegistryCall::withdraw_collateral { - currency_pair: vault_id.currencies.clone(), + assert_ok!(RuntimeCall::Nomination(NominationCall::withdraw_collateral { + vault_id: vault_id.clone(), + index: None, amount, }) .dispatch(origin_of(vault_id.account_id.clone()))); diff --git a/standalone/runtime/tests/test_issue.rs b/standalone/runtime/tests/test_issue.rs index 8b5d97d631..bd0f73e875 100644 --- a/standalone/runtime/tests/test_issue.rs +++ b/standalone/runtime/tests/test_issue.rs @@ -562,8 +562,9 @@ fn integration_test_withdraw_after_request_issue() { .is_err()); // should not be possible to withdraw the collateral now - assert!(RuntimeCall::VaultRegistry(VaultRegistryCall::withdraw_collateral { - currency_pair: vault_id.currencies.clone(), + assert!(RuntimeCall::Nomination(NominationCall::withdraw_collateral { + vault_id: vault_id.clone(), + index: None, amount: collateral_vault.amount() }) .dispatch(origin_of(account_of(vault))) diff --git a/standalone/runtime/tests/test_nomination.rs b/standalone/runtime/tests/test_nomination.rs index 2b0ffe71ed..7809d3cc14 100644 --- a/standalone/runtime/tests/test_nomination.rs +++ b/standalone/runtime/tests/test_nomination.rs @@ -344,8 +344,9 @@ mod spec_based_tests { fn integration_test_withdraw_collateral_preconditions_collateralization() { // PRECONDITION: The Vault MUST remain above the secure collateralization threshold. test_with_nomination_enabled(|vault_id| { - assert_ok!(RuntimeCall::VaultRegistry(VaultRegistryCall::withdraw_collateral { - currency_pair: vault_id.currencies.clone(), + assert_ok!(RuntimeCall::Nomination(NominationCall::withdraw_collateral { + vault_id: vault_id.clone(), + index: None, amount: 750000 }) .dispatch(origin_of(account_of(VAULT)))); @@ -456,7 +457,7 @@ fn integration_test_vaults_cannot_withdraw_nominated_collateral() { &vault_id, default_backing_collateral(vault_id.collateral_currency()).with_amount(|x| x + 1) ), - VaultRegistryError::InsufficientCollateral + NominationError::CannotWithdrawCollateral ); }); } @@ -551,8 +552,9 @@ fn integration_test_nominator_withdrawal_request_reduces_issuable_tokens() { #[test] fn integration_test_nominator_withdrawal_below_collateralization_threshold_fails() { test_with_nomination_enabled(|vault_id| { - assert_ok!(RuntimeCall::VaultRegistry(VaultRegistryCall::withdraw_collateral { - currency_pair: vault_id.currencies.clone(), + assert_ok!(RuntimeCall::Nomination(NominationCall::withdraw_collateral { + vault_id: vault_id.clone(), + index: None, amount: 750000 }) .dispatch(origin_of(account_of(VAULT)))); diff --git a/standalone/runtime/tests/test_vault_registry.rs b/standalone/runtime/tests/test_vault_registry.rs index 2b33be61d0..d8978afa19 100644 --- a/standalone/runtime/tests/test_vault_registry.rs +++ b/standalone/runtime/tests/test_vault_registry.rs @@ -39,8 +39,8 @@ fn test_with(execute: impl Fn(VaultId) -> R) { fn deposit_collateral_and_issue(vault_id: VaultId) { let new_collateral = 10_000; - assert_ok!(RuntimeCall::VaultRegistry(VaultRegistryCall::deposit_collateral { - currency_pair: vault_id.currencies.clone(), + assert_ok!(RuntimeCall::Nomination(NominationCall::deposit_collateral { + vault_id: vault_id.clone(), amount: new_collateral, }) .dispatch(origin_of(account_of(VAULT)))); @@ -58,8 +58,8 @@ mod deposit_collateral_test { let currency_id = vault_id.collateral_currency(); let amount = Amount::new(1_000, currency_id); - assert_ok!(RuntimeCall::VaultRegistry(VaultRegistryCall::deposit_collateral { - currency_pair: vault_id.currencies.clone(), + assert_ok!(RuntimeCall::Nomination(NominationCall::deposit_collateral { + vault_id: vault_id.clone(), amount: amount.amount(), }) .dispatch(origin_of(account_of(VAULT)))); @@ -80,8 +80,8 @@ mod deposit_collateral_test { let currency_id = vault_id.collateral_currency(); let amount = default_vault_free_balance(currency_id); - assert_ok!(RuntimeCall::VaultRegistry(VaultRegistryCall::deposit_collateral { - currency_pair: vault_id.currencies.clone(), + assert_ok!(RuntimeCall::Nomination(NominationCall::deposit_collateral { + vault_id: vault_id.clone(), amount: amount.amount() }) .dispatch(origin_of(account_of(VAULT)))); @@ -103,8 +103,8 @@ mod deposit_collateral_test { let amount = default_vault_free_balance(currency_id).amount() + 1; assert_noop!( - RuntimeCall::VaultRegistry(VaultRegistryCall::deposit_collateral { - currency_pair: vault_id.currencies.clone(), + RuntimeCall::Nomination(NominationCall::deposit_collateral { + vault_id: vault_id.clone(), amount: amount }) .dispatch(origin_of(account_of(VAULT))), @@ -141,8 +141,8 @@ mod deposit_collateral_test { .dispatch(origin_of(vault_id.account_id.clone()))); assert_noop!( - RuntimeCall::VaultRegistry(VaultRegistryCall::deposit_collateral { - currency_pair: vault_id.currencies.clone(), + RuntimeCall::Nomination(NominationCall::deposit_collateral { + vault_id: vault_id.clone(), amount: amount_1 }) .dispatch(origin_of(vault_id.account_id.clone())), @@ -163,16 +163,16 @@ mod deposit_collateral_test { let remaining = FUND_LIMIT_CEILING - current.amount(); assert_noop!( - RuntimeCall::VaultRegistry(VaultRegistryCall::deposit_collateral { - currency_pair: vault_id.currencies.clone(), + RuntimeCall::Nomination(NominationCall::deposit_collateral { + vault_id: vault_id.clone(), amount: remaining + 1 }) .dispatch(origin_of(account_of(VAULT))), VaultRegistryError::CurrencyCeilingExceeded ); - assert_ok!(RuntimeCall::VaultRegistry(VaultRegistryCall::deposit_collateral { - currency_pair: vault_id.currencies.clone(), + assert_ok!(RuntimeCall::Nomination(NominationCall::deposit_collateral { + vault_id: vault_id.clone(), amount: remaining, }) .dispatch(origin_of(account_of(VAULT)))); @@ -194,9 +194,10 @@ mod withdraw_collateral_test { let currency_id = vault_id.collateral_currency(); let amount = Amount::new(1_000, currency_id); - assert_ok!(RuntimeCall::VaultRegistry(VaultRegistryCall::withdraw_collateral { - currency_pair: vault_id.currencies.clone(), - amount: amount.amount() + assert_ok!(RuntimeCall::Nomination(NominationCall::withdraw_collateral { + vault_id: vault_id.clone(), + amount: amount.amount(), + index: None, }) .dispatch(origin_of(account_of(VAULT)))); @@ -216,8 +217,9 @@ mod withdraw_collateral_test { let currency_id = vault_id.collateral_currency(); let amount = default_vault_backing_collateral(currency_id) - required_collateral(vault_id.clone()); - assert_ok!(RuntimeCall::VaultRegistry(VaultRegistryCall::withdraw_collateral { - currency_pair: vault_id.currencies.clone(), + assert_ok!(RuntimeCall::Nomination(NominationCall::withdraw_collateral { + vault_id: vault_id.clone(), + index: None, amount: amount.amount() }) .dispatch(origin_of(account_of(VAULT)))); @@ -241,12 +243,13 @@ mod withdraw_collateral_test { + 1; assert_noop!( - RuntimeCall::VaultRegistry(VaultRegistryCall::withdraw_collateral { - currency_pair: vault_id.currencies.clone(), + RuntimeCall::Nomination(NominationCall::withdraw_collateral { + vault_id: vault_id.clone(), + index: None, amount: amount }) .dispatch(origin_of(account_of(VAULT))), - VaultRegistryError::InsufficientCollateral + NominationError::CannotWithdrawCollateral ); }); } @@ -266,12 +269,13 @@ mod withdraw_collateral_test { ); assert_err!( - RuntimeCall::VaultRegistry(VaultRegistryCall::withdraw_collateral { - currency_pair: vault_id.currencies.clone(), + RuntimeCall::Nomination(NominationCall::withdraw_collateral { + vault_id: vault_id.clone(), + index: None, amount: amount.amount() }) .dispatch(origin_of(account_of(VAULT))), - VaultRegistryError::InsufficientCollateral + NominationError::CannotWithdrawCollateral ); assert_ok!( @@ -282,8 +286,9 @@ mod withdraw_collateral_test { .dispatch(origin_of(vault_id.account_id.clone())) ); - assert_ok!(RuntimeCall::VaultRegistry(VaultRegistryCall::withdraw_collateral { - currency_pair: vault_id.currencies.clone(), + assert_ok!(RuntimeCall::Nomination(NominationCall::withdraw_collateral { + vault_id: vault_id.clone(), + index: None, amount: amount.amount() }) .dispatch(origin_of(account_of(VAULT)))); @@ -313,16 +318,17 @@ fn integration_test_vault_registry_with_parachain_shutdown_fails() { SystemError::CallFiltered ); assert_noop!( - RuntimeCall::VaultRegistry(VaultRegistryCall::deposit_collateral { - currency_pair: vault_id.currencies.clone(), + RuntimeCall::Nomination(NominationCall::deposit_collateral { + vault_id: vault_id.clone(), amount: 0 }) .dispatch(origin_of(account_of(VAULT))), SystemError::CallFiltered ); assert_noop!( - RuntimeCall::VaultRegistry(VaultRegistryCall::withdraw_collateral { - currency_pair: vault_id.currencies.clone(), + RuntimeCall::Nomination(NominationCall::withdraw_collateral { + vault_id: vault_id.clone(), + index: None, amount: 0 }) .dispatch(origin_of(account_of(VAULT))),