diff --git a/Cargo.lock b/Cargo.lock index 48edf7636da..22347a482b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1423,7 +1423,7 @@ dependencies = [ "frame-system", "is_sorted", "parity-scale-codec", - "proptest 1.0.0", + "proptest", "scale-info", "serde", "serde_json", @@ -1456,7 +1456,7 @@ dependencies = [ "frame-system", "parity-scale-codec", "plotters", - "proptest 1.0.0", + "proptest", "scale-info", "serde", "sp-arithmetic", @@ -2316,6 +2316,7 @@ dependencies = [ "pallet-governance-registry", "pallet-identity", "pallet-indices", + "pallet-lending", "pallet-liquidations", "pallet-membership", "pallet-mosaic", @@ -5985,7 +5986,7 @@ dependencies = [ "pallet-balances", "pallet-governance-registry", "parity-scale-codec", - "proptest 0.9.6", + "proptest", "scale-info", "sp-api", "sp-arithmetic", @@ -6174,7 +6175,7 @@ dependencies = [ "orml-traits", "pallet-vesting 0.0.1", "parity-scale-codec", - "proptest 1.0.0", + "proptest", "proptest-derive", "scale-info", "serde", @@ -6353,7 +6354,7 @@ dependencies = [ "frame-system", "pallet-balances", "parity-scale-codec", - "proptest 0.9.6", + "proptest", "scale-info", "sp-arithmetic", "sp-core", @@ -6380,7 +6381,7 @@ dependencies = [ "pallet-vault", "parity-scale-codec", "plotters", - "proptest 0.9.6", + "proptest", "scale-info", "serde", "sp-arithmetic", @@ -6470,7 +6471,7 @@ dependencies = [ "pallet-currency-factory", "pallet-timestamp", "parity-scale-codec", - "proptest 1.0.0", + "proptest", "scale-info", "serde", "smallvec 1.8.0", @@ -6658,7 +6659,7 @@ dependencies = [ "pallet-vault", "parity-scale-codec", "plotters", - "proptest 0.9.6", + "proptest", "scale-info", "serde", "smallvec 1.8.0", @@ -6688,7 +6689,7 @@ dependencies = [ "pallet-dutch-auction", "pallet-timestamp", "parity-scale-codec", - "proptest 1.0.0", + "proptest", "scale-info", "smallvec 1.8.0", "sp-arithmetic", @@ -6781,7 +6782,7 @@ dependencies = [ "orml-traits", "parity-scale-codec", "plotters", - "proptest 0.9.6", + "proptest", "scale-info", "sp-core", "sp-io", @@ -7257,7 +7258,7 @@ dependencies = [ "orml-traits", "pallet-balances", "parity-scale-codec", - "proptest 0.9.6", + "proptest", "scale-info", "serde", "sp-arithmetic", @@ -9203,26 +9204,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "proptest" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c477819b845fe023d33583ebf10c9f62518c8d79a0960ba5c36d6ac8a55a5b" -dependencies = [ - "bit-set", - "bitflags", - "byteorder", - "lazy_static", - "num-traits", - "quick-error 1.2.3", - "rand 0.6.5", - "rand_chacha 0.1.1", - "rand_xorshift 0.1.1", - "regex-syntax", - "rusty-fork 0.2.2", - "tempfile", -] - [[package]] name = "proptest" version = "1.0.0" @@ -9239,7 +9220,7 @@ dependencies = [ "rand_chacha 0.3.1", "rand_xorshift 0.3.0", "regex-syntax", - "rusty-fork 0.3.0", + "rusty-fork", "tempfile", ] @@ -10059,18 +10040,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" -[[package]] -name = "rusty-fork" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dd93264e10c577503e926bd1430193eeb5d21b059148910082245309b424fae" -dependencies = [ - "fnv", - "quick-error 1.2.3", - "tempfile", - "wait-timeout", -] - [[package]] name = "rusty-fork" version = "0.3.0" diff --git a/docs/benchmarking.md b/docs/benchmarking.md index e9ab9e34d0e..b1ef6230233 100644 --- a/docs/benchmarking.md +++ b/docs/benchmarking.md @@ -216,4 +216,3 @@ https://www.shawntabrizi.com/substrate-graph-benchmarks/docs/#/ https://substrate.dev/docs/en/knowledgebase/runtime/benchmarking https://crates.io/crates/frame-benchmarking https://github.com/paritytech/substrate/tree/polkadot-v0.9.8/frame/benchmarking - diff --git a/frame/assets/Cargo.toml b/frame/assets/Cargo.toml index c3455e996f5..61972bbe861 100644 --- a/frame/assets/Cargo.toml +++ b/frame/assets/Cargo.toml @@ -5,7 +5,6 @@ authors = ["Composable Developers"] homepage = "https://composable.finance" edition = "2021" - [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -36,7 +35,7 @@ num-traits = { version = "0.2.14", default-features = false } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false } orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "2b1c9fb367ccb8e13601b2da43d1c5d9737b93c6", default-features = false } governance-registry = { package = "pallet-governance-registry", path = "../governance-registry", default-features = false } -proptest = "0.9.6" +proptest = "1.0" composable-tests-helpers = { path = "../composable-tests-helpers", default-features = false } [package.metadata.cargo-udeps.ignore] diff --git a/frame/composable-traits/src/defi.rs b/frame/composable-traits/src/defi.rs index b4f4dadb2bd..3fed14e03a5 100644 --- a/frame/composable-traits/src/defi.rs +++ b/frame/composable-traits/src/defi.rs @@ -8,6 +8,8 @@ use sp_runtime::{ ArithmeticError, DispatchError, FixedPointNumber, FixedPointOperand, FixedU128, }; +use sp_std::fmt::Debug; + use crate::currency::{AssetIdLike, BalanceLike, MathBalance}; #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, Clone, PartialEq)] @@ -205,7 +207,7 @@ pub trait SellEngine: DeFiEngine { } pub trait DeFiComposableConfig: frame_system::Config { - type MayBeAssetId: AssetIdLike + MaybeSerializeDeserialize + Default; + type MayBeAssetId: AssetIdLike + MaybeSerializeDeserialize + Default + MaxEncodedLen + Debug; type Balance: BalanceLike + MathBalance diff --git a/frame/composable-traits/src/lending/mod.rs b/frame/composable-traits/src/lending/mod.rs index 976890be68a..cc592c99bb8 100644 --- a/frame/composable-traits/src/lending/mod.rs +++ b/frame/composable-traits/src/lending/mod.rs @@ -42,9 +42,9 @@ pub struct CreateInput { pub reserved_factor: Perquintill, } -#[derive(Clone, Copy, Debug, PartialEq, TypeInfo)] +#[derive(Clone, Copy, Debug, PartialEq, TypeInfo, Default)] pub struct MarketModelValid; -#[derive(Clone, Copy, Debug, PartialEq, TypeInfo)] +#[derive(Clone, Copy, Debug, PartialEq, TypeInfo, Default)] pub struct CurrencyPairIsNotSame; impl diff --git a/frame/composable-traits/src/oracle.rs b/frame/composable-traits/src/oracle.rs index 6af79b70698..6304e79e6d3 100644 --- a/frame/composable-traits/src/oracle.rs +++ b/frame/composable-traits/src/oracle.rs @@ -5,8 +5,10 @@ use crate::{ use frame_support::{dispatch::DispatchError, pallet_prelude::*}; use sp_std::vec::Vec; -#[derive(Encode, Decode, Default, Debug, PartialEq)] +// block timestamped value +#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, PartialEq, TypeInfo, Clone)] pub struct Price { + /// value pub price: PriceValue, pub block: BlockNumber, } @@ -89,6 +91,7 @@ pub trait Oracle { /// Given `asset_id` and `amount` of price asset. /// Returns what amount of `asset_id` will be required to be same price as `amount` of /// normalized currency + /// `amount` - in smallest units fn get_price_inverse( asset_id: Self::AssetId, amount: Self::Balance, diff --git a/frame/crowdloan-rewards/Cargo.toml b/frame/crowdloan-rewards/Cargo.toml index d25b97ff1a9..82cd2fcac0f 100644 --- a/frame/crowdloan-rewards/Cargo.toml +++ b/frame/crowdloan-rewards/Cargo.toml @@ -22,7 +22,7 @@ libsecp256k1 = { version = "0.7.0", default-features = false, features = [ "hmac", "static-context", ] } -hex-literal = "0.3" +hex-literal = "0.3.3" balances = { package = "pallet-balances", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false, features = [ "std" ]} [dependencies] diff --git a/frame/currency-factory/Cargo.toml b/frame/currency-factory/Cargo.toml index 666e72992c0..827c927a08a 100644 --- a/frame/currency-factory/Cargo.toml +++ b/frame/currency-factory/Cargo.toml @@ -34,7 +34,7 @@ composable-traits = { path = "../composable-traits", default-features = false } [dev-dependencies] pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16" } -proptest = "0.9.6" +proptest = "1.0.0" composable-tests-helpers = { version = "0.0.1", path = "../composable-tests-helpers", default-features = false } [features] diff --git a/frame/curve-amm/Cargo.toml b/frame/curve-amm/Cargo.toml index 23ef3319011..40b3c47dc89 100644 --- a/frame/curve-amm/Cargo.toml +++ b/frame/curve-amm/Cargo.toml @@ -44,7 +44,7 @@ pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "p orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "2b1c9fb367ccb8e13601b2da43d1c5d9737b93c6", default-features = false } orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "2b1c9fb367ccb8e13601b2da43d1c5d9737b93c6", default-features = false } pallet-currency-factory = { version = "0.0.1", path = "../currency-factory" } -proptest = "0.9.6" +proptest = "1.0" [features] default = ["std"] diff --git a/frame/dutch-auction/src/mock/runtime.rs b/frame/dutch-auction/src/mock/runtime.rs index 8311781c22e..ffa2a4e10fd 100644 --- a/frame/dutch-auction/src/mock/runtime.rs +++ b/frame/dutch-auction/src/mock/runtime.rs @@ -54,56 +54,32 @@ frame_support::construct_runtime! { parameter_types! { pub const SS58Prefix: u8 = 42; pub const BlockHashCount: u64 = 250; - } impl frame_system::Config for Runtime { type BaseCallFilter = Everything; - type BlockWeights = (); - type BlockLength = (); - type Origin = Origin; - type Call = Call; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Header = Header; - type Event = Event; - type BlockHashCount = BlockHashCount; - type DbWeight = (); - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = SS58Prefix; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; } diff --git a/frame/lending/Cargo.toml b/frame/lending/Cargo.toml index e9a1fcddb36..336d9bd7ed4 100644 --- a/frame/lending/Cargo.toml +++ b/frame/lending/Cargo.toml @@ -22,6 +22,8 @@ version = "2.0.0" frame-benchmarking = { default-features = false, optional = true, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.16' } frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16" } frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16" } +pallet-timestamp = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", optional = true } +pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", optional = true } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16" } sp-arithmetic = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16" } @@ -31,21 +33,20 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/subst composable-traits = { default-features = false, path = "../composable-traits" } composable-support = { default-features = false, path = "../composable-support" } -pallet-oracle = { default-features = false, optional = true, version = "1.0.0", path = "../oracle" } +pallet-oracle = { default-features = false, optional = true, path = "../oracle" } pallet-vault = { default-features = false, path = "../vault", optional = true } log = { version = "0.4.14", default-features = false } num-traits = { version = "0.2.14", default-features = false } plotters = { version = "0.3.1", optional = true } scale-info = { version = "1.0", default-features = false, features = ["derive"] } -serde = { version = '1.0.130' } [dev-dependencies] hex-literal = "0.3.3" once_cell = "1.8.0" -proptest = "0.9.6" +proptest = "1.0" smallvec = "1.7.0" -orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "2b1c9fb367ccb8e13601b2da43d1c5d9737b93c6", default-features = false } +orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "2b1c9fb367ccb8e13601b2da43d1c5d9737b93c6", default-features = true } orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "2b1c9fb367ccb8e13601b2da43d1c5d9737b93c6", default-features = false } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16" } pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16" } @@ -53,8 +54,9 @@ pallet-currency-factory = { path = "../currency-factory" } pallet-liquidations = { path = "../liquidations" } pallet-dutch-auction = { path = "../dutch-auction", default-features = false } composable-tests-helpers = { path = "../composable-tests-helpers", default-features = false } -pallet-assets = { path = '../assets', default-features = false } - +serde = { version = '1.0.130' } +pallet-assets = { path = '../assets', default-features = false} +frame-benchmarking = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.16' } [features] default = ["std"] @@ -70,8 +72,8 @@ std = [ "sp-arithmetic/std", "composable-traits/std", "pallet-vault/std", - "orml-tokens/std", "scale-info/std", + "pallet-oracle/std", ] runtime-benchmarks = [ @@ -79,6 +81,9 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-oracle/runtime-benchmarks", + "pallet-vault/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", ] visualization = ["plotters"] diff --git a/frame/lending/proptest-regressions/tests.txt b/frame/lending/proptest-regressions/tests.txt index c3a14efc06e..aae5affd53a 100644 --- a/frame/lending/proptest-regressions/tests.txt +++ b/frame/lending/proptest-regressions/tests.txt @@ -8,3 +8,4 @@ cc 105541697ded40d7111e1cd357efd01fe710dfdeda9d88044a73316520487f24 # shrinks to cc 738fc7fe1be060a08d13e3e2fa521af22fd53a5b0f5a50d8042fe88c4ca2a5a4 # shrinks to depth = 0 cc 52628c1411e4d9f8f7abaf6ad392c47e27083cf0a67294329a08f2f28a05e624 # shrinks to amount = 1000 cc ef61546023a270a01b0b7586ed06ea032b11e5b016464600d167dfc39cd3298c # shrinks to amount = 1000 +cc a86d61e12bf3f1b42d240a6c693f8845bede3f36f44274996b9af889df6d2fcc # shrinks to (amount1, amount2) = (1000, 1000) diff --git a/frame/lending/src/benchmarking.rs b/frame/lending/src/benchmarking.rs index 633b22eca19..3d8c23366ab 100644 --- a/frame/lending/src/benchmarking.rs +++ b/frame/lending/src/benchmarking.rs @@ -1,16 +1,23 @@ -use super::*; +//! Benchmarks and sanity tests for lending. Only test that action do not error, not that produce +//! positive side effects -use crate::Pallet as Lending; +use super::{setup::*, *}; +use crate::{self as pallet_lending, Pallet as Lending}; +use composable_support::validation::Validated; use composable_traits::{ - lending::{math::InterestRateModel, CreateInput, Lending as LendingTrait}, - vault::Vault, + defi::{CurrencyPair, DeFiComposableConfig, MoreThanOneFixedU128}, + lending::{math::InterestRateModel, CreateInput, Lending as LendingTrait, UpdateInput}, + oracle::Price, + vault::StrategicVault, }; use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, whitelisted_caller}; -use frame_system::{EventRecord, RawOrigin}; +use frame_support::traits::fungibles::Mutate; +use frame_system::EventRecord; use sp_runtime::{FixedPointNumber, Percent, Perquintill}; use sp_std::prelude::*; -fn assert_last_event(generic_event: ::Event) { +#[allow(dead_code)] +pub fn assert_last_event(generic_event: ::Event) { let events = frame_system::Pallet::::events(); let system_event: ::Event = generic_event.into(); // compare to the last event record @@ -18,224 +25,289 @@ fn assert_last_event(generic_event: ::Event) { assert_eq!(event, &system_event); } -const BTC: u128 = 1000; -const USDT: u128 = 2000; - -fn set_price(asset_id: u128, price: u64) { +pub fn set_price< + T: pallet_lending::Config + pallet_oracle::Config + composable_traits::defi::DeFiComposableConfig, +>( + asset_id: T::MayBeAssetId, + price: u64, +) { + let asset_id = asset_id.encode(); + let asset_id = ::AssetId::decode(&mut &asset_id[..]).unwrap(); pallet_oracle::Prices::::insert( - ::AssetId::from(asset_id), - pallet_oracle::Price { - price: ::PriceValue::from(price), - block: 0u32.into(), - }, + asset_id, + Price { price: ::PriceValue::from(price), block: 0_u32.into() }, ); } fn set_prices() { - set_price::(BTC, 48_000u64); - set_price::(USDT, 1u64); + let pair = assets::(); + set_price::(pair.base, 48_000_000_000_u64); + set_price::(pair.quote, 1_000_000_000_u64); } -fn create_market( +#[allow(dead_code)] +fn create_new_market( manager: T::AccountId, - borrow_asset: u128, - collateral_asset: u128, + borrow_asset: ::MayBeAssetId, + collateral_asset: ::MayBeAssetId, ) -> (crate::MarketIndex, ::VaultId) { - let market_config = CreateInput { - liquidator: None, - manager, - reserved: Perquintill::from_percent(10), - collateral_factor: MoreThanOneFixedU128::saturating_from_rational(200, 100), - under_collaterized_warn_percent: Percent::from_percent(10), - }; - Lending::::create( - ::AssetId::from(borrow_asset), - ::AssetId::from(collateral_asset), - market_config, - &InterestRateModel::default(), - ) - .unwrap() + let market_config = create_market_config::(borrow_asset, collateral_asset); + Lending::::create(manager, market_config).unwrap() } -benchmarks! { - create_new_market { - let caller: T::AccountId = whitelisted_caller(); - let borrow_asset_id = ::AssetId::from(BTC); - let collateral_asset_id = ::AssetId::from(USDT); - let reserved_factor = Perquintill::from_percent(10); - let collateral_factor = MoreThanOneFixedU128::saturating_from_rational(200, 100); - let under_collaterized_warn_percent = Percent::from_percent(10); - let market_id = MarketIndex::new(1); - let vault_id = 1u64.into(); - set_prices::(); - }: _( - RawOrigin::Signed(caller.clone()), - borrow_asset_id, - collateral_asset_id, - reserved_factor, - collateral_factor, - under_collaterized_warn_percent, - InterestRateModel::default(), - None - ) - verify { - assert_last_event::(Event::NewMarketCreated { - market_id, - vault_id, - manager: caller, - borrow_asset_id, - collateral_asset_id, - reserved_factor, - collateral_factor, - }.into()) - } - - deposit_collateral { - let caller: T::AccountId = whitelisted_caller(); - let market: MarketIndex = MarketIndex::new(1u32); - let amount: T::Balance = 1_000_000u64.into(); - set_prices::(); - let _ = create_market::(caller.clone(), BTC, USDT); - ::Currency::mint_into(USDT.into(), &caller, amount).unwrap(); - }: _(RawOrigin::Signed(caller.clone()), market, amount) - verify { - assert_last_event::(Event::CollateralDeposited { - sender: caller, - market_id: market, - amount, - }.into()) +fn create_market_config( + collateral_asset: ::MayBeAssetId, + borrow_asset: ::MayBeAssetId, +) -> CreateInput<::LiquidationStrategyId, ::MayBeAssetId> { + CreateInput { + updatable: UpdateInput { + collateral_factor: MoreThanOneFixedU128::saturating_from_rational(200_u128, 100_u128), + under_collaterized_warn_percent: Percent::from_percent(10), + liquidators: vec![], + interest_rate_model: InterestRateModel::default(), + }, + reserved_factor: Perquintill::from_percent(10), + currency_pair: CurrencyPair::new(collateral_asset, borrow_asset), } +} - withdraw_collateral { - let caller: T::AccountId = whitelisted_caller(); - let market: MarketIndex = MarketIndex::new(1_u32); - let amount: T::Balance = 1_000_000_u64.into(); - set_prices::(); - let (market, _vault_id) = create_market::(caller.clone(), BTC, USDT); - ::Currency::mint_into(USDT.into(), &caller, amount).unwrap(); - Lending::::deposit_collateral_internal(&market, &caller, amount).unwrap(); - }: _(RawOrigin::Signed(caller.clone()), market, amount) - verify { - assert_last_event::(Event::CollateralWithdrawn { - sender: caller, - market_id: market, - amount - }.into()) +benchmarks! { + where_clause { + where + T: + pallet_oracle::Config + + pallet_lending::Config + + DeFiComposableConfig + + pallet_balances::Config + + frame_system::Config + + pallet_timestamp::Config + + pallet_vault::Config, + ::Balance : From, + ::BlockNumber : From, + ::Moment : From, + ::Balance : From, } - - borrow { - let caller: T::AccountId = whitelisted_caller(); - let balance: T::Balance = 1_000_000u64.into(); + create_market { + let caller= whitelisted_caller::(); + let origin = whitelisted_origin::(); + let amount: ::Balance = 1_000_000_u64.into(); + let bank: ::Balance = 10_000_000_000_000_u64.into(); + let pair = assets::(); set_prices::(); - let (market_id, vault_id) = create_market::(caller.clone(), BTC, USDT); - ::Currency::mint_into(USDT.into(), &caller, balance * 6u64.into()).unwrap(); - ::Currency::mint_into(BTC.into(), &caller, balance * 6u64.into()).unwrap(); - Lending::::deposit_collateral_internal(&market_id, &caller, balance * 2u64.into()).unwrap(); - ::Vault::deposit(&vault_id, &caller, balance * 2u64.into()).unwrap(); - let amount_to_borrow: T::Balance = 0u32.into(); - }: _(RawOrigin::Signed(caller.clone()), market_id, amount_to_borrow) - verify { - assert_last_event::(Event::Borrowed { - sender: caller, - market_id, - amount: amount_to_borrow - }.into()) + let input = create_market_config::(pair.base, pair.quote); + ::MultiCurrency::mint_into(pair.base, &caller, bank).unwrap(); + ::MultiCurrency::mint_into(pair.quote, &caller, bank).unwrap(); + } : { + Lending::::create_market(origin.into(), Validated::new(input).unwrap()).unwrap() } - - repay_borrow { - let caller: T::AccountId = whitelisted_caller(); + deposit_collateral { + let caller= whitelisted_caller::(); + let origin = whitelisted_origin::(); + let amount: ::Balance = 1_000_000_u64.into(); + let bank: ::Balance = 10_000_000_000_000_u64.into(); + let pair = assets::(); + let input = create_market_config::(pair.base, pair.quote); set_prices::(); - let (market_id, vault_id) = create_market::(caller.clone(), BTC, USDT); - let balance: T::Balance = 1_000_000u64.into(); - ::Currency::mint_into(USDT.into(), &caller, balance * 6u64.into()).unwrap(); - ::Currency::mint_into(BTC.into(), &caller, balance * 6u64.into()).unwrap(); - Lending::::deposit_collateral_internal(&market_id, &caller, balance * 2u64.into()).unwrap(); - ::Vault::deposit(&vault_id, &caller, balance * 2u64.into()).unwrap(); - let repay_amount: T::Balance = 0u32.into(); - Lending::::borrow_internal(&market_id, &caller, repay_amount).unwrap(); - crate::LastBlockTimestamp::::put(6); - }: _(RawOrigin::Signed(caller.clone()), market_id, caller.clone(), repay_amount) - verify { - assert_last_event::( - Event::RepaidBorrow { - sender: caller.clone(), - market_id, - beneficiary: caller, - amount: repay_amount, - }.into() - ) - } - - now { - }: { - Lending::::now() - } - - accrue_interest { - let caller: T::AccountId = whitelisted_caller(); - let (borrow_asset_id, collateral_asset_id) = (1u32, 2u32); - set_price::(borrow_asset_id.into(), u64::from(borrow_asset_id) * 10); - set_price::(collateral_asset_id.into(), u64::from(collateral_asset_id) * 10); - let (market_id, _) = create_market::(caller, borrow_asset_id.into(), collateral_asset_id.into()); - }: { - Lending::::accrue_interest(&market_id, 6).unwrap() - } - - account_id { - let caller: T::AccountId = whitelisted_caller(); - let (borrow_asset_id, collateral_asset_id) = (1u32, 2u32); - set_price::(borrow_asset_id.into(), u64::from(borrow_asset_id) * 10); - set_price::(collateral_asset_id.into(), u64::from(collateral_asset_id) * 10); - let (market_id, _) = create_market::(caller, borrow_asset_id.into(), collateral_asset_id.into()); - }: { - Lending::::account_id(&market_id) - } - - available_funds { - let caller: T::AccountId = whitelisted_caller(); - let (borrow_asset_id, collateral_asset_id) = (1u32, 2u32); - set_price::(borrow_asset_id.into(), u64::from(borrow_asset_id) * 10); - set_price::(collateral_asset_id.into(), u64::from(collateral_asset_id) * 10); - let (market_id, _) = create_market::(caller.clone(), borrow_asset_id.into(), collateral_asset_id.into()); - let market_config = Markets::::try_get(market_id).unwrap(); - }: { - Lending::::available_funds(&market_config, &caller).unwrap() - } - - handle_withdrawable { - let caller: T::AccountId = whitelisted_caller(); - let (borrow_asset_id, collateral_asset_id) = (1_u32, 2u32); - set_price::(borrow_asset_id.into(), u64::from(borrow_asset_id) * 10); - set_price::(collateral_asset_id.into(), u64::from(collateral_asset_id) * 10); - let (market_id, vault_id) = create_market::(caller.clone(), borrow_asset_id.into(), collateral_asset_id.into()); - let market_config = Markets::::try_get(market_id).unwrap(); - let balance = 0_u32.into(); - }: { - Lending::::handle_withdrawable(&market_config, &caller, balance).unwrap() - } + ::MultiCurrency::mint_into(pair.base, &caller, bank).unwrap(); + ::MultiCurrency::mint_into(pair.quote, &caller, bank).unwrap(); + Lending::::create_market(origin.clone().into(), Validated::new(input).unwrap()).unwrap(); + let market_id = MarketIndex::new(1); + }: { + Lending::::deposit_collateral(origin.into(), market_id, amount).unwrap(); + } + withdraw_collateral { + let caller= whitelisted_caller::(); + let origin = whitelisted_origin::(); + let amount: ::Balance = 1_000_000_u64.into(); + let part: ::Balance = 1_000_u64.into(); + let bank: ::Balance = 10_000_000_000_000_u64.into(); + let pair = assets::(); + let input = create_market_config::(pair.base, pair.quote); + set_prices::(); + ::MultiCurrency::mint_into(pair.base, &caller, bank).unwrap(); + ::MultiCurrency::mint_into(pair.quote, &caller, bank).unwrap(); + Lending::::create_market(origin.clone().into(), Validated::new(input).unwrap()).unwrap(); + let market_id = MarketIndex::new(1); + Lending::::deposit_collateral(origin.clone().into(), market_id, amount).unwrap(); + }: { + Lending::::withdraw_collateral(origin.into(), market_id, part).unwrap(); + } + borrow { + let caller= whitelisted_caller::(); + let origin = whitelisted_origin::(); + let amount: ::Balance = 1_000_000_u64.into(); + let part: ::Balance = 1_000_u64.into(); + let bank: ::Balance = 10_000_000_000_000_u64.into(); + let pair = assets::(); + let input = create_market_config::(pair.base, pair.quote); + set_prices::(); + //pallet_balances:::: + as frame_support::traits::fungible::Mutate>::mint_into(&caller, 10_000_000_000_000_u64.into()).unwrap(); + ::MultiCurrency::mint_into(pair.base, &caller, bank).unwrap(); + ::MultiCurrency::mint_into(pair.quote, &caller, bank).unwrap(); + Lending::::create_market(origin.clone().into(), Validated::new(input).unwrap()).unwrap(); + let market_id = MarketIndex::new(1); + Lending::::deposit_collateral(origin.clone().into(), market_id, amount).unwrap(); + }: { + Lending::::borrow(origin.into(), market_id, part).unwrap(); + } + repay_borrow { + let caller= whitelisted_caller::(); + let origin = whitelisted_origin::(); + let amount: ::Balance = 1_000_000_000_000_u64.into(); + let borrow_amount: ::Balance = 1_000_000_000_u64.into(); + let part: ::Balance = 1_000_u64.into(); + let bank: ::Balance = 10_000_000_000_000_u64.into(); + let pair = assets::(); + let input = create_market_config::(pair.base, pair.quote); + set_prices::(); + as frame_support::traits::fungible::Mutate>::mint_into(&caller, 10_000_000_000_000_u64.into()).unwrap(); + ::MultiCurrency::mint_into(pair.base, &caller, bank).unwrap(); + ::MultiCurrency::mint_into(pair.quote, &caller, bank).unwrap(); + Lending::::create_market(origin.clone().into(), Validated::new(input).unwrap()).unwrap(); + let market_id = MarketIndex::new(1); + Lending::::deposit_collateral(origin.clone().into(), market_id, amount).unwrap(); + Lending::::borrow(origin.clone().into(), market_id, borrow_amount).unwrap(); + produce_block::(42_u32.into(),4200_u64.into()); + produce_block::(43_u32.into(),4300_u64.into()); - handle_depositable { - let caller: T::AccountId = whitelisted_caller(); - let (borrow_asset_id, collateral_asset_id) = (1u32, 2u32); - set_price::(borrow_asset_id.into(), u64::from(borrow_asset_id) * 10); - set_price::(collateral_asset_id.into(), u64::from(collateral_asset_id) * 10); - let (market_id, _) = create_market::(caller.clone(), borrow_asset_id.into(), collateral_asset_id.into()); - let market_config = Markets::::try_get(market_id).unwrap(); - let balance = 1_000_000u32.into(); - }: { - Lending::::handle_depositable(&market_config, &caller, balance).unwrap() - } + }: { + Lending::::repay_borrow(origin.clone().into(), market_id, caller, part).unwrap(); + } + now { + }: { + Lending::::now() + } - handle_must_liquidate { - let caller: T::AccountId = whitelisted_caller(); - let (borrow_asset_id, collateral_asset_id) = (1u32, 2u32); - set_price::(borrow_asset_id.into(), u64::from(borrow_asset_id) * 10); - set_price::(collateral_asset_id.into(), u64::from(collateral_asset_id) * 10); - let (market_id, _) = create_market::(caller.clone(), borrow_asset_id.into(), collateral_asset_id.into()); - let market_config = Markets::::try_get(market_id).unwrap(); - }: { - Lending::::handle_must_liquidate(&market_config, &caller).unwrap() - } + accrue_interest { + let caller= whitelisted_caller::(); + let origin = whitelisted_origin::(); + let amount: ::Balance = 1_000_000_000_000_u64.into(); + let borrow_amount: ::Balance = 1_000_000_000_u64.into(); + let part: ::Balance = 1_000_u64.into(); + let bank: ::Balance = 10_000_000_000_000_u64.into(); + let pair = assets::(); + let input = create_market_config::(pair.base, pair.quote); + set_prices::(); + as frame_support::traits::fungible::Mutate>::mint_into(&caller, 10_000_000_000_000_u64.into()).unwrap(); + ::MultiCurrency::mint_into(pair.base, &caller, bank).unwrap(); + ::MultiCurrency::mint_into(pair.quote, &caller, bank).unwrap(); + Lending::::create_market(origin.clone().into(), Validated::new(input).unwrap()).unwrap(); + let market_id = MarketIndex::new(1); + Lending::::deposit_collateral(origin.clone().into(), market_id, amount).unwrap(); + Lending::::borrow(origin.into(), market_id, borrow_amount).unwrap(); + produce_block::(42_u32.into(),4200_u64.into()); + produce_block::(43_u32.into(),4300_u64.into()); + }: { + // TODO: fix it, make timestamp depend on x increased OR make the value passed be DELTA + Lending::::accrue_interest(&market_id, 4400_u64).unwrap(); + } + account_id { + let caller= whitelisted_caller::(); + let origin = whitelisted_origin::(); + let amount: ::Balance = 1_000_000_000_000_u64.into(); + let part: ::Balance = 1_000_u64.into(); + let bank: ::Balance = 10_000_000_000_000_u64.into(); + let pair = assets::(); + let input = create_market_config::(pair.base, pair.quote); + set_prices::(); + as frame_support::traits::fungible::Mutate>::mint_into(&caller, 10_000_000_000_000_u64.into()).unwrap(); + ::MultiCurrency::mint_into(pair.base, &caller, bank).unwrap(); + ::MultiCurrency::mint_into(pair.quote, &caller, bank).unwrap(); + Lending::::create_market(origin.into(), Validated::new(input).unwrap()).unwrap(); + let market_id = MarketIndex::new(1); + }: { + // TODO: fix it, make timestamp depend on x increased OR make the value passed be DELTA + Lending::::account_id(&market_id) + } + available_funds { + let caller= whitelisted_caller::(); + let origin = whitelisted_origin::(); + let amount: ::Balance = 1_000_000_000_000_u64.into(); + let part: ::Balance = 1_000_u64.into(); + let bank: ::Balance = 10_000_000_000_000_u64.into(); + let pair = assets::(); + let input = create_market_config::(pair.base, pair.quote); + set_prices::(); + as frame_support::traits::fungible::Mutate>::mint_into(&caller, 10_000_000_000_000_u64.into()).unwrap(); + ::MultiCurrency::mint_into(pair.base, &caller, bank).unwrap(); + ::MultiCurrency::mint_into(pair.quote, &caller, bank).unwrap(); + Lending::::create_market(origin.into(), Validated::new(input).unwrap()).unwrap(); + let market_id = MarketIndex::new(1); + let market_config = Markets::::try_get(market_id).unwrap(); + }: { + // TODO: make changes to vault state so something happens + Lending::::available_funds(&market_config, &caller).unwrap() + } + handle_withdrawable { + let caller= whitelisted_caller::(); + let origin = whitelisted_origin::(); + let amount: ::Balance = 1_000_000_000_000_u64.into(); + let part: ::Balance = 1_000_u64.into(); + let bank: ::Balance = 10_000_000_000_000_u64.into(); + let pair = assets::(); + let input = create_market_config::(pair.base, pair.quote); + set_prices::(); + as frame_support::traits::fungible::Mutate>::mint_into(&caller, 10_000_000_000_000_u64.into()).unwrap(); + ::MultiCurrency::mint_into(pair.base, &caller, bank).unwrap(); + ::MultiCurrency::mint_into(pair.quote, &caller, bank).unwrap(); + Lending::::create_market(origin.clone().into(), Validated::new(input).unwrap()).unwrap(); + let market_id = MarketIndex::new(1); + Lending::::deposit_collateral(origin.into(), market_id, amount).unwrap(); + let market_config = Markets::::try_get(market_id).unwrap(); + let account = &Lending::::account_id(&market_id); + ::MultiCurrency::mint_into(pair.base, account, bank).unwrap(); + ::MultiCurrency::mint_into(pair.quote, account, bank).unwrap(); + ::Vault::deposit(&market_config.borrow, account, bank).unwrap(); + }: { + Lending::::handle_withdrawable(&market_config, &caller, part ).unwrap() + } + handle_depositable { + let caller= whitelisted_caller::(); + let origin = whitelisted_origin::(); + let amount: ::Balance = 1_000_000_000_000_u64.into(); + let part: ::Balance = 1_000_u64.into(); + let bank: ::Balance = 10_000_000_000_000_u64.into(); + let pair = assets::(); + let input = create_market_config::(pair.base, pair.quote); + set_prices::(); + as frame_support::traits::fungible::Mutate>::mint_into(&caller, 10_000_000_000_000_u64.into()).unwrap(); + ::MultiCurrency::mint_into(pair.base, &caller, bank).unwrap(); + ::MultiCurrency::mint_into(pair.quote, &caller, bank).unwrap(); + Lending::::create_market(origin.clone().into(), Validated::new(input).unwrap()).unwrap(); + let market_id = MarketIndex::new(1); + Lending::::deposit_collateral(origin.into(), market_id, amount).unwrap(); + let market_config = Markets::::try_get(market_id).unwrap(); + let account = &Lending::::account_id(&market_id); + ::MultiCurrency::mint_into(pair.base, account, bank).unwrap(); + ::MultiCurrency::mint_into(pair.quote, account, bank).unwrap(); + }: { + // TODO: make it variable with x + Lending::::handle_depositable(&market_config, &caller, part ).unwrap() + } + handle_must_liquidate { + let caller= whitelisted_caller::(); + let origin = whitelisted_origin::(); + let amount: ::Balance = 1_000_000_000_000_u64.into(); + let part: ::Balance = 1_000_u64.into(); + let bank: ::Balance = 10_000_000_000_000_u64.into(); + let pair = assets::(); + let input = create_market_config::(pair.base, pair.quote); + set_prices::(); + as frame_support::traits::fungible::Mutate>::mint_into(&caller, 10_000_000_000_000_u64.into()).unwrap(); + ::MultiCurrency::mint_into(pair.base, &caller, bank).unwrap(); + ::MultiCurrency::mint_into(pair.quote, &caller, bank).unwrap(); + Lending::::create_market(origin.clone().into(), Validated::new(input).unwrap()).unwrap(); + let market_id = MarketIndex::new(1); + Lending::::deposit_collateral(origin.into(), market_id, amount).unwrap(); + let market_config = Markets::::try_get(market_id).unwrap(); + let account = &Lending::::account_id(&market_id); + ::MultiCurrency::mint_into(pair.base, account, bank).unwrap(); + ::MultiCurrency::mint_into(pair.quote, account, bank).unwrap(); + }: { + // TODO: make it variable with x + Lending::::handle_must_liquidate(&market_config, &caller ).unwrap() + } } -impl_benchmark_test_suite!(Lending, crate::mock::new_test_ext(), crate::mock::Test,); +impl_benchmark_test_suite!(Lending, crate::mocks::new_test_ext(), crate::mocks::Runtime,); diff --git a/frame/lending/src/currency.rs b/frame/lending/src/currency.rs new file mode 100644 index 00000000000..61112ac29c8 --- /dev/null +++ b/frame/lending/src/currency.rs @@ -0,0 +1,46 @@ +#![allow(clippy::upper_case_acronyms)] +use sp_std::ops::Deref; + +pub type CurrencyId = u128; + +#[derive(Copy, Debug, Clone)] +pub struct Currency {} + +impl Currency { + pub const EXPONENT: u8 = EXPONENT; + pub const ID: u128 = ID; + + pub fn units(ones: u128) -> u128 { + ones.saturating_mul(Self::one()) + } + pub const fn one() -> u128 { + 10_u128.pow(Self::EXPONENT as u32) + } +} + +impl From> for CurrencyId { + fn from(_val: Currency) -> Self { + ID + } +} +impl Deref for Currency { + type Target = CurrencyId; + + fn deref(&self) -> &Self::Target { + &ID + } +} + +impl AsRef for Currency { + #[inline(always)] + fn as_ref(&self) -> &CurrencyId { + &ID + } +} + +#[allow(dead_code)] +pub type PICA = Currency<1, 12>; +pub type BTC = Currency<2000, 12>; +pub type USDT = Currency<1000, 12>; +#[allow(dead_code)] +pub type NORAMLIZED = USDT; diff --git a/frame/lending/src/lib.rs b/frame/lending/src/lib.rs index 411d6331a7f..209138762ff 100644 --- a/frame/lending/src/lib.rs +++ b/frame/lending/src/lib.rs @@ -7,7 +7,8 @@ clippy::indexing_slicing, clippy::todo, clippy::unwrap_used, - clippy::panic + clippy::panic, + clippy::identity_op, ) )] // allow in tests #![warn(clippy::unseparated_literal_suffix)] @@ -43,8 +44,14 @@ mod mocks; #[cfg(test)] mod tests; -#[cfg(feature = "runtime-benchmarks")] +#[cfg(any(feature = "runtime-benchmarks", test))] mod benchmarking; +#[cfg(any(feature = "runtime-benchmarks", test))] +mod setup; + +#[cfg(any(feature = "runtime-benchmarks", test))] +mod currency; + pub mod weights; mod models; @@ -212,7 +219,8 @@ pub mod pallet { /// Id of proxy to liquidate type LiquidationStrategyId: Parameter + Default + PartialEq + Clone + Debug + TypeInfo; - /// Minimal price of borrow asset in Oracle price required to create + /// Minimal price of borrow asset in Oracle price required to create. + /// Examples, 100 USDC. /// Creators puts that amount and it is staked under Vault account. /// So he does not owns it anymore. /// So borrow is both stake and tool to create market. @@ -237,7 +245,7 @@ pub mod pallet { /// Vault can take that amount if reconfigured so, but that may be changed during runtime /// upgrades. #[pallet::constant] - type MarketCreationStake: Get; + type OracleMarketCreationStake: Get; #[pallet::constant] type PalletId: Get; @@ -282,6 +290,7 @@ pub mod pallet { return } for (market_id, account, _) in DebtIndex::::iter() { + // TODO: check that it should liqudate before liqudaitons let results = signer.send_signed_transaction(|_account| Call::liquidate { market_id, borrowers: vec![account.clone()], @@ -339,7 +348,8 @@ pub mod pallet { BorrowerDataCalculationFailed, Unauthorized, NotEnoughRent, - PriceOfInitialBorrowVaultShoyldBeGreaterThanZero, + /// borrow assets should have enough value as per oracle + PriceOfInitialBorrowVaultShouldBeGreaterThanZero, } #[pallet::event] @@ -530,7 +540,10 @@ pub mod pallet { impl Pallet { /// Create a new lending market. /// - `origin` : Sender of this extrinsic. Manager for new market to be created. Can pause - /// borrow & deposits of assets. + /// borrow operations. + /// - `input` : Borrow & deposits of assets, persentages. + /// + /// `origin` irreversibly pays `T::OracleMarketCreationStake`. #[pallet::weight(::WeightInfo::create_new_market())] #[transactional] pub fn create_market( @@ -1163,14 +1176,14 @@ pub mod pallet { }, )?; - let initial_price_amount = T::MarketCreationStake::get(); + let initial_price_amount = T::OracleMarketCreationStake::get(); let initial_pool_size = T::Oracle::get_price_inverse( config_input.borrow_asset(), initial_price_amount, )?; ensure!( initial_pool_size > T::Balance::zero(), - Error::::PriceOfInitialBorrowVaultShoyldBeGreaterThanZero + Error::::PriceOfInitialBorrowVaultShouldBeGreaterThanZero ); T::MultiCurrency::transfer( config_input.borrow_asset(), @@ -1180,13 +1193,6 @@ pub mod pallet { false, )?; - // TODO: discuss on why we do not reposit all amount to vault - // ::deposit( - // &borrow_asset_vault, - // &Self::account_id(&market_id), - // initial_pool_size, - // )?; - let config = MarketConfig { manager, borrow: borrow_asset_vault.clone(), @@ -1311,7 +1317,8 @@ pub mod pallet { total_repay_amount } else { let repay_borrow_amount = total_repay_amount - burn_amount; - remaining_borrow_amount -= repay_borrow_amount; + remaining_borrow_amount = + remaining_borrow_amount.safe_sub(&repay_borrow_amount)?; ::MultiCurrency::burn_from( debt_asset_id, &market_account, diff --git a/frame/lending/src/mocks/currency.rs b/frame/lending/src/mocks/currency.rs deleted file mode 100644 index 48c4b4a81d8..00000000000 --- a/frame/lending/src/mocks/currency.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub type CurrencyId = u128; - -pub const PICA: CurrencyId = 0; -pub const BTC: CurrencyId = 1; -pub const USDT: CurrencyId = 2; -pub const ETH: CurrencyId = 3; -pub const LTC: CurrencyId = 5; diff --git a/frame/lending/src/mocks/mod.rs b/frame/lending/src/mocks/mod.rs index d2c07efe70e..4359f8cfe6d 100644 --- a/frame/lending/src/mocks/mod.rs +++ b/frame/lending/src/mocks/mod.rs @@ -2,8 +2,10 @@ use self::currency::CurrencyId; pub use self::currency::*; use crate::{self as pallet_lending, *}; use composable_traits::{ + currency::{Exponent, LocalAssets}, defi::DeFiComposableConfig, governance::{GovernanceRegistry, SignedRawOrigin}, + oracle::Price, }; use frame_support::{ ord_parameter_types, parameter_types, @@ -23,14 +25,11 @@ use sp_runtime::{ traits::{ BlakeTwo256, ConvertInto, Extrinsic as ExtrinsicT, IdentifyAccount, IdentityLookup, Verify, }, - Perbill, + DispatchError, Perbill, }; -pub mod currency; -pub mod oracle; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; pub type Balance = u128; pub type Amount = i128; pub type BlockNumber = u64; @@ -45,7 +44,7 @@ parameter_types! { pub type ParachainId = u32; -pub const MINIMUM_BALANCE: Balance = 1000; +pub const MINIMUM_BALANCE: Balance = 1_000_000; pub static ALICE: Lazy = Lazy::new(|| { AccountId::from_raw(hex!("0000000000000000000000000000000000000000000000000000000000000000")) @@ -63,7 +62,7 @@ pub static UNRESERVED: Lazy = Lazy::new(|| { // Configure a mock runtime to test the pallet. frame_support::construct_runtime!( - pub enum Test where + pub enum Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, @@ -77,8 +76,8 @@ frame_support::construct_runtime!( Assets: pallet_assets::{Pallet, Call, Storage}, Liquidations: pallet_liquidations::{Pallet, Call, Event}, Lending: pallet_lending::{Pallet, Call, Config, Storage, Event}, - Oracle: pallet_lending::mocks::oracle::{Pallet}, DutchAuction: pallet_dutch_auction::{Pallet, Call, Storage, Event}, + Oracle: pallet_oracle::{Pallet, Call, Storage, Event}, } ); @@ -87,7 +86,7 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } -impl frame_system::Config for Test { +impl frame_system::Config for Runtime { type BaseCallFilter = Everything; type BlockWeights = (); type BlockLength = (); @@ -118,7 +117,7 @@ parameter_types! { pub const ExistentialDeposit: u64 = 1000; } -impl pallet_balances::Config for Test { +impl pallet_balances::Config for Runtime { type Balance = Balance; type Event = Event; type DustRemoval = (); @@ -136,14 +135,14 @@ parameter_types! { pub const MinimumPeriod: u64 = MILLISECS_PER_BLOCK / 2; } -impl pallet_timestamp::Config for Test { +impl pallet_timestamp::Config for Runtime { type Moment = u64; type OnTimestampSet = (); type MinimumPeriod = MinimumPeriod; type WeightInfo = (); } -impl pallet_currency_factory::Config for Test { +impl pallet_currency_factory::Config for Runtime { type Event = Event; type AssetId = CurrencyId; type AddOrigin = EnsureRoot; @@ -153,7 +152,7 @@ impl pallet_currency_factory::Config for Test { parameter_types! { pub const MaxStrategies: usize = 255; - pub const NativeAssetId: CurrencyId = PICA; + pub const NativeAssetId: CurrencyId = PICA::ID; pub const CreationDeposit: Balance = 10; pub const RentPerBlock: Balance = 1; pub const MinimumDeposit: Balance = 0; @@ -162,7 +161,7 @@ parameter_types! { pub const TombstoneDuration: u64 = 42; } -impl pallet_vault::Config for Test { +impl pallet_vault::Config for Runtime { type Event = Event; type Currency = Tokens; type AssetId = CurrencyId; @@ -192,7 +191,7 @@ parameter_types! { pub MaxLocks: u32 = 2; } -impl orml_tokens::Config for Test { +impl orml_tokens::Config for Runtime { type Event = Event; type Balance = Balance; type Amount = Amount; @@ -227,7 +226,7 @@ impl } } -impl pallet_assets::Config for Test { +impl pallet_assets::Config for Runtime { type NativeAssetId = NativeAssetId; type GenerateCurrencyId = LpTokenFactory; type AssetId = CurrencyId; @@ -239,12 +238,38 @@ impl pallet_assets::Config for Test { type GovernanceRegistry = NoopRegistry; } -impl crate::mocks::oracle::Config for Test { - type VaultId = VaultId; - type Vault = Vault; +parameter_types! { + pub const MinBalance : Balance = 0; + pub const MinU32 : u32 = 0; + pub const MinU64 : u64 = 0; } -impl DeFiComposableConfig for Test { +pub struct Decimals; +impl LocalAssets for Decimals { + fn decimals(_currency_id: CurrencyId) -> Result { + Ok(12) + } +} + +impl pallet_oracle::Config for Runtime { + type Event = Event; + type Currency = Assets; + type AssetId = CurrencyId; + type PriceValue = Balance; + type AuthorityId = pallet_oracle::crypto::BathurstStId; + type MinStake = MinBalance; + type StakeLock = MinU64; + type StalePrice = MinU64; + type AddOracle = EnsureSignedBy; + type MaxAnswerBound = MinU32; + type MaxAssetsCount = MinU32; + type MaxHistory = MinU32; + type MaxPrePrices = MinU32; + type WeightInfo = (); + type LocalAssets = Decimals; +} + +impl DeFiComposableConfig for Runtime { type MayBeAssetId = CurrencyId; type Balance = Balance; } @@ -288,7 +313,7 @@ impl WeightToFeePolynomial for DutchAuctionsMocks { } } -impl pallet_dutch_auction::Config for Test { +impl pallet_dutch_auction::Config for Runtime { type Event = Event; type OrderId = OrderId; type UnixTime = Timestamp; @@ -299,7 +324,7 @@ impl pallet_dutch_auction::Config for Test { type WeightToFee = DutchAuctionsMocks; } -impl pallet_liquidations::Config for Test { +impl pallet_liquidations::Config for Runtime { type Event = Event; type UnixTime = Timestamp; type DutchAuction = DutchAuction; @@ -313,12 +338,12 @@ impl pallet_liquidations::Config for Test { pub type Extrinsic = TestXt; pub type AccountId = <::Signer as IdentifyAccount>::AccountId; -impl frame_system::offchain::SigningTypes for Test { +impl frame_system::offchain::SigningTypes for Runtime { type Public = ::Signer; type Signature = Signature; } -impl frame_system::offchain::SendTransactionTypes for Test +impl frame_system::offchain::SendTransactionTypes for Runtime where Call: From, { @@ -326,7 +351,7 @@ where type Extrinsic = Extrinsic; } -impl frame_system::offchain::CreateSignedTransaction for Test +impl frame_system::offchain::CreateSignedTransaction for Runtime where Call: From, { @@ -343,7 +368,7 @@ where parameter_types! { pub const MaxLendingCount: u32 = 10; pub LendingPalletId: PalletId = PalletId(*b"liqiudat"); - pub MarketCreationStake : Balance = 10^15; + pub OracleMarketCreationStake : Balance = NORAMLIZED::one(); } parameter_types! { @@ -363,7 +388,7 @@ impl WeightToFeePolynomial for WeightToFee { } } -impl pallet_lending::Config for Test { +impl pallet_lending::Config for Runtime { type Oracle = Oracle; type VaultId = VaultId; type Vault = Vault; @@ -378,31 +403,37 @@ impl pallet_lending::Config for Test { type WeightInfo = (); type LiquidationStrategyId = LiquidationStrategyId; type PalletId = LendingPalletId; - type MarketCreationStake = MarketCreationStake; + type OracleMarketCreationStake = OracleMarketCreationStake; type WeightToFee = WeightToFee; } +pub fn set_price(asset_id: CurrencyId, balance: Balance) { + let price = Price { price: balance, block: System::block_number() }; + pallet_oracle::Prices::::insert(asset_id, price); +} + // Build genesis storage according to the mock runtime. pub fn new_test_ext() -> sp_io::TestExternalities { - let mut storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let mut storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); let balances = vec![(*ALICE, 1_000_000_000), (*BOB, 1_000_000_000), (*CHARLIE, 1_000_000_000)]; - pallet_balances::GenesisConfig:: { balances } + pallet_balances::GenesisConfig:: { balances } .assimilate_storage(&mut storage) .unwrap(); pallet_lending::GenesisConfig {} - .assimilate_storage::(&mut storage) - .unwrap(); - GenesisBuild::::assimilate_storage(&pallet_liquidations::GenesisConfig {}, &mut storage) + .assimilate_storage::(&mut storage) .unwrap(); + GenesisBuild::::assimilate_storage( + &pallet_liquidations::GenesisConfig {}, + &mut storage, + ) + .unwrap(); let mut ext = sp_io::TestExternalities::new(storage); ext.execute_with(|| { System::set_block_number(0); Timestamp::set_timestamp(MILLISECS_PER_BLOCK); - // Initialize BTC price to 50000 - pallet_lending::mocks::oracle::BTCValue::::set(50000_u128); }); ext } @@ -424,7 +455,7 @@ pub fn process_block(n: BlockNumber) { Lending::on_finalize(n); } -fn next_block(n: u64) { +pub fn next_block(n: u64) { System::set_block_number(n); Lending::on_initialize(n); Timestamp::set_timestamp(MILLISECS_PER_BLOCK * n); diff --git a/frame/lending/src/mocks/oracle.rs b/frame/lending/src/mocks/oracle.rs deleted file mode 100644 index 301f113a8bb..00000000000 --- a/frame/lending/src/mocks/oracle.rs +++ /dev/null @@ -1,131 +0,0 @@ -pub use pallet::*; - -#[frame_support::pallet] -pub mod pallet { - use codec::Codec; - use composable_traits::{ - currency::LocalAssets, - math::SafeArithmetic, - oracle::{Oracle, Price}, - vault::Vault, - }; - use frame_support::pallet_prelude::*; - use sp_runtime::{ - helpers_128bit::multiply_by_rational, ArithmeticError, DispatchError, FixedPointNumber, - FixedU128, - }; - use sp_std::fmt::Debug; - - use crate::mocks::{currency::*, Balance}; - - #[pallet::config] - pub trait Config: frame_system::Config { - type VaultId: Clone + Codec + Debug + PartialEq + Default + Parameter; - type Vault: Vault; - } - - #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] - pub struct Pallet(_); - - #[pallet::storage] - #[pallet::getter(fn btc_value)] - // FIXME: Temporary fix to get CI to pass, separate PRs will be made per pallet to refactor to - // use OptionQuery instead - #[allow(clippy::disallowed_type)] - pub type BTCValue = StorageValue<_, u128, ValueQuery>; - - impl Pallet { - pub fn get_price( - asset: CurrencyId, - amount: Balance, - ) -> Result, DispatchError> { - ::get_price(asset, amount) - } - pub fn set_btc_price(price: u128) { - BTCValue::::set(price) - } - } - - impl Oracle for Pallet { - type AssetId = CurrencyId; - type Balance = Balance; - type Timestamp = (); - type LocalAssets = (); - - fn get_price( - asset: Self::AssetId, - amount: Balance, - ) -> Result, DispatchError> { - let derive_price = |p: u128, a: u128| { - let e = 10_u128 - .checked_pow(Self::LocalAssets::decimals(asset)?) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - let price = multiply_by_rational(p, a, e) - .map_err(|_| DispatchError::Arithmetic(ArithmeticError::Overflow))?; - Ok(Price { price, block: () }) - }; - - #[allow(clippy::inconsistent_digit_grouping)] // values are in cents - match asset { - /* NOTE(hussein-aitlahcen) - Ideally we would have all the static currency quoted against USD cents on chain. - So that we would be able to derive LP tokens price. - */ - USDT => Ok(Price { price: amount, block: () }), - PICA => derive_price(10_00, amount), - BTC => derive_price(Self::btc_value(), amount), - ETH => derive_price(3400_00, amount), - LTC => derive_price(180_00, amount), - - /* NOTE(hussein-aitlahcen) - If we want users to be able to consider LP tokens as currency, - the oracle should know the whole currency system in order to - recursively resolve the price of an LP token generated by an - arbitrary level of vaults. - - The base asset represented by the level 0 (out of LpToken case) - should have a price defined. - - One exception can occur if the LP token hasn't been generated by a vault. - */ - x => { - let vault = T::Vault::token_vault(x)?; - let base = T::Vault::asset_id(&vault)?; - let Price { price, block } = Self::get_price(base, amount)?; - let rate = T::Vault::stock_dilution_rate(&vault)?; - let derived = rate - .checked_mul_int(price) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - Ok(Price { price: derived, block }) - }, - } - } - - fn get_twap( - _of: Self::AssetId, - _weights: Vec, - ) -> Result { - Ok(0_u32.into()) - } - - fn get_ratio( - pair: composable_traits::defi::CurrencyPair, - ) -> Result { - let base: u128 = Self::get_price(pair.base, (10_u32 ^ 12).into())?.price; - let quote: u128 = Self::get_price(pair.quote, (10_u32 ^ 12).into())?.price; - let base = FixedU128::saturating_from_integer(base); - let quote = FixedU128::saturating_from_integer(quote); - Ok(base.safe_div("e)?) - } - - fn get_price_inverse( - asset_id: Self::AssetId, - amount: Self::Balance, - ) -> Result { - let price = Self::get_price(asset_id, 10 ^ 12)?; - let inversed = (amount / price.price / 10) ^ 12; - Ok(inversed) - } - } -} diff --git a/frame/lending/src/models.rs b/frame/lending/src/models.rs index c6fff257bdc..9b5ed06bc32 100644 --- a/frame/lending/src/models.rs +++ b/frame/lending/src/models.rs @@ -5,6 +5,7 @@ use composable_traits::{ }; use sp_runtime::{traits::Saturating, ArithmeticError, FixedPointNumber, Percent}; +#[derive(Debug)] pub struct BorrowerData { pub collateral_balance_value: LiftedFixedBalance, pub borrow_balance_value: LiftedFixedBalance, diff --git a/frame/lending/src/setup.rs b/frame/lending/src/setup.rs new file mode 100644 index 00000000000..2ce02813f0f --- /dev/null +++ b/frame/lending/src/setup.rs @@ -0,0 +1,40 @@ +//! most usable things, 20% of all , for tests +/// test should be freely use external API +pub use codec::{Codec, Decode, Encode, FullCodec, MaxEncodedLen}; + +use composable_traits::defi::{CurrencyPair, DeFiComposableConfig}; +use frame_benchmarking::whitelisted_caller; +use frame_support::traits::Hooks; +use frame_system::RawOrigin; + +use crate::{self as pallet_lending, currency::*}; + +pub fn whitelisted_origin() -> RawOrigin { + let caller: T::AccountId = whitelisted_caller(); + RawOrigin::Signed(caller) +} + +pub type AssetIdOf = ::MayBeAssetId; + +pub fn assets() -> CurrencyPair> +where + T: frame_system::Config + composable_traits::defi::DeFiComposableConfig, +{ + let a = USDT::ID.encode(); + let b = BTC::ID.encode(); + CurrencyPair::new( + AssetIdOf::::decode(&mut &a[..]).unwrap(), + AssetIdOf::::decode(&mut &b[..]).unwrap(), + ) +} + +pub fn produce_block< + T: frame_system::Config + pallet_lending::Config + pallet_timestamp::Config, +>( + n: T::BlockNumber, + time: T::Moment, +) { + frame_system::Pallet::::set_block_number(n); + as Hooks>::on_initialize(n); + >::set_timestamp(time); +} diff --git a/frame/lending/src/tests.rs b/frame/lending/src/tests.rs index 5abd25a32e7..1b2043a4eaa 100644 --- a/frame/lending/src/tests.rs +++ b/frame/lending/src/tests.rs @@ -1,10 +1,14 @@ +//! Test for Lending. Runtime is almost real. +//! TODO: cover testing events - so make sure that each even is handled at least once +//! (events can be obtained from System pallet as in banchmarking.rs before this commit) +//! TODO: OCW of liqudaitons (like in Oracle) +//! TODO: test on small numbers via proptests - detect edge case what is minimal amounts it starts +//! to accure(and miminal block delta), and maximal amounts when it overflows + use std::ops::Mul; use crate::{ - accrue_interest_internal, - mocks::{currency::*, *}, - models::BorrowerData, - Error, MarketIndex, + accrue_interest_internal, currency::*, mocks::*, models::BorrowerData, Error, MarketIndex, }; use composable_tests_helpers::{prop_assert_acceptable_computation_error, prop_assert_ok}; use composable_traits::{ @@ -30,7 +34,7 @@ type CollateralAsset = CurrencyId; const DEFAULT_MARKET_VAULT_RESERVE: Perquintill = Perquintill::from_percent(10); const DEFAULT_MARKET_VAULT_STRATEGY_SHARE: Perquintill = Perquintill::from_percent(90); const DEFAULT_COLLATERAL_FACTOR: u128 = 2; -const INITIAL_BORROW_ASSET_AMOUNT: u128 = 10 ^ 30; +const INITIAL_BORROW_ASSET_AMOUNT: u128 = 10_u128.pow(30); /// Create a very simple vault for the given currency, 100% is reserved. fn create_simple_vault( @@ -56,6 +60,8 @@ fn create_market( reserved: Perquintill, collateral_factor: MoreThanOneFixedU128, ) -> (MarketIndex, BorrowAssetVault) { + set_price(BTC::ID, 50_000 * NORAMLIZED::one()); + set_price(USDT::ID, NORAMLIZED::one()); let config = CreateInput { updatable: UpdateInput { collateral_factor, @@ -66,17 +72,19 @@ fn create_market( reserved_factor: reserved, currency_pair: CurrencyPair::new(collateral_asset, borrow_asset), }; - Tokens::mint_into(borrow_asset, &manager, 1_000_000_000).unwrap(); + Tokens::mint_into(borrow_asset, &manager, USDT::units(1000)).unwrap(); + Tokens::mint_into(collateral_asset, &manager, BTC::units(100)).unwrap(); ::create(manager, config).unwrap() } /// Create a market with a USDT vault LP token as collateral fn create_simple_vaulted_market() -> ((MarketIndex, BorrowAssetVault), CollateralAsset) { let (_collateral_vault, VaultInfo { lp_token_id: collateral_asset, .. }) = - create_simple_vault(USDT); + create_simple_vault(BTC::ID); + set_price(collateral_asset, NORAMLIZED::one()); ( create_market( - BTC, + BTC::ID, collateral_asset, *ALICE, DEFAULT_MARKET_VAULT_RESERVE, @@ -89,8 +97,8 @@ fn create_simple_vaulted_market() -> ((MarketIndex, BorrowAssetVault), Collatera /// Create a market with straight USDT as collateral fn create_simple_market() -> (MarketIndex, BorrowAssetVault) { create_market( - BTC, - USDT, + USDT::ID, + BTC::ID, *ALICE, DEFAULT_MARKET_VAULT_RESERVE, MoreThanOneFixedU128::saturating_from_rational(DEFAULT_COLLATERAL_FACTOR * 100, 100), @@ -118,7 +126,7 @@ fn accrue_interest_base_cases() { let total_issued = 100_000_000_000_000_000_000; let accrued_debt = 0; let total_borrows = total_issued - accrued_debt; - let (accrued_increase, _) = accrue_interest_internal::( + let (accrued_increase, _) = accrue_interest_internal::( optimal, interest_rate_model, borrow_index, @@ -129,7 +137,7 @@ fn accrue_interest_base_cases() { assert_eq!(accrued_increase, 10_000_000_000_000_000_000); let delta_time = MILLISECS_PER_BLOCK; - let (accrued_increase, _) = accrue_interest_internal::( + let (accrued_increase, _) = accrue_interest_internal::( optimal, interest_rate_model, borrow_index, @@ -153,7 +161,7 @@ fn apr_for_zero() { let utilization = Percent::from_percent(100); let borrow_index = Rate::saturating_from_integer(1_u128); - let (accrued_increase, _) = accrue_interest_internal::( + let (accrued_increase, _) = accrue_interest_internal::( utilization, interest_rate_model, borrow_index, @@ -170,7 +178,7 @@ fn apr_for_year_for_max() { let utilization = Percent::from_percent(80); let borrow_index = Rate::saturating_from_integer(1_u128); let total_borrows = u128::MAX; - let result = accrue_interest_internal::( + let result = accrue_interest_internal::( utilization, interest_rate_model, borrow_index, @@ -195,7 +203,7 @@ fn accrue_interest_induction() { |(slot, total_issued)| { let (optimal, ref mut interest_rate_model) = new_jump_model(); let (accrued_increase_1, borrow_index_1) = - accrue_interest_internal::( + accrue_interest_internal::( optimal, interest_rate_model, borrow_index, @@ -204,7 +212,7 @@ fn accrue_interest_induction() { ) .unwrap(); let (accrued_increase_2, borrow_index_2) = - accrue_interest_internal::( + accrue_interest_internal::( optimal, interest_rate_model, borrow_index, @@ -232,7 +240,7 @@ fn accrue_interest_plotter() { const TOTAL_BLOCKS: u64 = 1000; let _data: Vec<_> = (0..TOTAL_BLOCKS) .map(|x| { - let (accrue_increment, _) = accrue_interest_internal::( + let (accrue_increment, _) = accrue_interest_internal::( optimal, interest_rate_model, borrow_index, @@ -245,7 +253,7 @@ fn accrue_interest_plotter() { }) .collect(); - let (total_accrued, _) = accrue_interest_internal::( + let (total_accrued, _) = accrue_interest_internal::( optimal, interest_rate_model, Rate::checked_from_integer(1).unwrap(), @@ -284,8 +292,16 @@ fn accrue_interest_plotter() { #[test] fn can_create_valid_market() { new_test_ext().execute_with(|| { - let borrow_asset = BTC; - let collateral_asset = USDT; + let borrow_asset = BTC::ID; + let collateral_asset = USDT::ID; + let expected = 50_000 * USDT::one(); + set_price(BTC::ID, expected); + set_price(USDT::ID, USDT::one()); + let price = ::get_price(BTC::ID, BTC::one()) + .expect("impossible") + .price; + assert_eq!(price, expected); + let manager = *ALICE; let collateral_factor = MoreThanOneFixedU128::saturating_from_rational(DEFAULT_COLLATERAL_FACTOR * 100, 100); @@ -302,12 +318,15 @@ fn can_create_valid_market() { let created = ::create(manager, config.clone()); assert!(!created.is_ok()); + Tokens::mint_into(borrow_asset, &manager, INITIAL_BORROW_ASSET_AMOUNT).unwrap(); let created = ::create(manager, config); assert_ok!(created); let new_balance = Tokens::balance(borrow_asset, &manager); assert!(new_balance < INITIAL_BORROW_ASSET_AMOUNT); let (market_id, borrow_vault_id) = created.unwrap(); + let initial_total_cash = Lending::total_cash(&market_id).unwrap(); + assert!(initial_total_cash > 0); let vault_borrow_id = ::asset_id(&borrow_vault_id).unwrap(); @@ -315,55 +334,61 @@ fn can_create_valid_market() { let initial_total_cash = Lending::total_cash(&market_id).unwrap(); assert!(initial_total_cash > 0); + + assert_eq!( + Lending::borrow_balance_current(&market_id, &ALICE), + Ok(Some(0)), + "nobody ows new market" + ); }); } #[test] fn test_borrow_repay_in_same_block() { new_test_ext().execute_with(|| { - let collateral_amount = 900000000; let (market_id, vault) = create_simple_market(); let initial_total_cash = Lending::total_cash(&market_id).unwrap(); - assert_ok!(Tokens::mint_into(USDT, &ALICE, collateral_amount)); + let collateral_amount = BTC::units(100); + assert_ok!(Tokens::mint_into(BTC::ID, &ALICE, collateral_amount)); assert_ok!(Lending::deposit_collateral_internal(&market_id, &ALICE, collateral_amount)); - assert_eq!(Tokens::balance(USDT, &ALICE), 0); - let borrow_asset_deposit = 900000; - assert_ok!(Tokens::mint_into(BTC, &CHARLIE, borrow_asset_deposit)); + let borrow_asset_deposit = USDT::units(1_000_000_000); + assert_ok!(Tokens::mint_into(USDT::ID, &CHARLIE, borrow_asset_deposit)); assert_ok!(Vault::deposit(Origin::signed(*CHARLIE), vault, borrow_asset_deposit)); let mut total_cash = DEFAULT_MARKET_VAULT_STRATEGY_SHARE.mul(borrow_asset_deposit) + initial_total_cash; - // Allow the market to initialize it's account by withdrawing - // from the vault - for i in 1..2 { - process_block(i); - } - - let price = - |currency_id, amount| Oracle::get_price(currency_id, amount).expect("impossible").price; + (1..2).for_each(process_block); - assert_eq!(Lending::borrow_balance_current(&market_id, &ALICE), Ok(Some(0))); let limit_normalized = Lending::get_borrow_limit(&market_id, &ALICE).unwrap(); - let alice_limit = limit_normalized / price(BTC, 1); assert_eq!(Lending::total_cash(&market_id), Ok(total_cash)); process_block(1); - assert_ok!(Lending::borrow_internal(&market_id, &ALICE, alice_limit / 4)); - total_cash -= alice_limit / 4; - let total_borrows = alice_limit / 4; + assert_ok!(Lending::borrow_internal(&market_id, &ALICE, limit_normalized / 4)); + total_cash -= limit_normalized / 4; + let total_borrows = limit_normalized / 4; assert_eq!(Lending::total_cash(&market_id), Ok(total_cash)); assert_eq!(Lending::total_borrows(&market_id), Ok(total_borrows)); let alice_repay_amount = Lending::borrow_balance_current(&market_id, &ALICE).unwrap(); // MINT required BTC so that ALICE and BOB can repay the borrow. - assert_ok!(Tokens::mint_into(BTC, &ALICE, alice_repay_amount.unwrap() - (alice_limit / 4))); + assert_ok!(Tokens::mint_into( + BTC::ID, + &ALICE, + alice_repay_amount.unwrap() - (limit_normalized / 4) + )); assert_noop!( Lending::repay_borrow_internal(&market_id, &ALICE, &ALICE, alice_repay_amount), - Error::::BorrowAndRepayInSameBlockIsNotSupported + Error::::BorrowAndRepayInSameBlockIsNotSupported ); }); } +fn get_price(currency_id: CurrencyId, amount: Balance) -> Balance { + ::get_price(currency_id, amount) + .expect("impossible") + .price +} + #[test] fn test_calc_utilization_ratio() { // 50% borrow @@ -392,61 +417,50 @@ fn borrow_flow() { new_test_ext().execute_with(|| { let (market, vault) = create_simple_market(); let initial_total_cash = Lending::total_cash(&market).unwrap(); - assert!(initial_total_cash > 0); - let unit = 1_000_000_000; - Oracle::set_btc_price(50000); - let price = - |currency_id, amount| Oracle::get_price(currency_id, amount).expect("impossible").price; + let borrow_amount = USDT::units(1_000_000); + let collateral_amount = + get_price(USDT::ID, borrow_amount) * BTC::one() / get_price(BTC::ID, BTC::one()); - let alice_capable_btc = 100 * unit; - let collateral_amount = alice_capable_btc * price(BTC, 1000) / price(USDT, 1000); - assert_eq!(Tokens::balance(USDT, &ALICE), 0); - assert_ok!(Tokens::mint_into(USDT, &ALICE, collateral_amount)); + assert_ok!(Tokens::mint_into(BTC::ID, &ALICE, collateral_amount)); assert_ok!(Lending::deposit_collateral_internal(&market, &ALICE, collateral_amount)); + let limit_normalized = Lending::get_borrow_limit(&market, &ALICE).unwrap(); - let limit = limit_normalized / price(BTC, 1); - assert_eq!(limit, alice_capable_btc / DEFAULT_COLLATERAL_FACTOR); - let borrow_asset_deposit = 100000 * unit; - assert_eq!(Tokens::balance(BTC, &CHARLIE), 0); - assert_ok!(Tokens::mint_into(BTC, &CHARLIE, borrow_asset_deposit)); - assert_eq!(Tokens::balance(BTC, &CHARLIE), borrow_asset_deposit); - assert_ok!(Vault::deposit(Origin::signed(*CHARLIE), vault, borrow_asset_deposit)); - assert_eq!(Tokens::balance(BTC, &CHARLIE), 0); + assert_eq!( + limit_normalized, + get_price(USDT::ID, borrow_amount) / DEFAULT_COLLATERAL_FACTOR + ); - // Allow the market to initialize it's account by withdrawing - // from the vault - for i in 1..2 { - process_block(i); - } + assert_ok!(Tokens::mint_into(USDT::ID, &CHARLIE, borrow_amount)); + assert_ok!(Vault::deposit(Origin::signed(*CHARLIE), vault, borrow_amount)); + + (1..2).for_each(process_block); let expected_cash = - DEFAULT_MARKET_VAULT_STRATEGY_SHARE.mul(borrow_asset_deposit) + initial_total_cash; + DEFAULT_MARKET_VAULT_STRATEGY_SHARE.mul(borrow_amount) + initial_total_cash; assert_eq!(Lending::total_cash(&market), Ok(expected_cash)); - let alice_borrow = alice_capable_btc / DEFAULT_COLLATERAL_FACTOR / 10; + let alice_borrow = borrow_amount / DEFAULT_COLLATERAL_FACTOR / 10; assert_ok!(Lending::borrow_internal(&market, &ALICE, alice_borrow)); assert_eq!(Lending::total_cash(&market), Ok(expected_cash - alice_borrow)); assert_eq!(Lending::total_borrows(&market), Ok(alice_borrow)); assert_eq!(Lending::total_interest_accurate(&market), Ok(0)); let limit_normalized = Lending::get_borrow_limit(&market, &ALICE).unwrap(); - let original_limit = limit_normalized / price(BTC, 1); + let original_limit = limit_normalized * USDT::one() / get_price(USDT::ID, USDT::one()); - assert_eq!(original_limit, alice_capable_btc / DEFAULT_COLLATERAL_FACTOR - alice_borrow); + assert_eq!(original_limit, borrow_amount / DEFAULT_COLLATERAL_FACTOR - alice_borrow); let borrow = Lending::borrow_balance_current(&market, &ALICE).unwrap().unwrap(); assert_eq!(borrow, alice_borrow); let interest_before = Lending::total_interest_accurate(&market).unwrap(); - for i in 2..50 { - process_block(i); - } + (2..50).for_each(process_block); let interest_after = Lending::total_interest_accurate(&market).unwrap(); assert!(interest_before < interest_after); let limit_normalized = Lending::get_borrow_limit(&market, &ALICE).unwrap(); - let new_limit = limit_normalized / price(BTC, 1); + let new_limit = limit_normalized * USDT::one() / get_price(USDT::ID, USDT::one()); assert!(new_limit < original_limit); @@ -455,30 +469,27 @@ fn borrow_flow() { assert!(borrow > alice_borrow); assert_noop!( Lending::borrow_internal(&market, &ALICE, original_limit), - Error::::NotEnoughCollateralToBorrowAmount + Error::::NotEnoughCollateralToBorrowAmount ); - // Borrow less because of interests. assert_ok!(Lending::borrow_internal(&market, &ALICE, new_limit,)); - // More than one borrow request in same block is invalid. assert_noop!( - Lending::borrow_internal(&market, &ALICE, 1), - Error::::InvalidTimestampOnBorrowRequest + Lending::borrow_internal(&market, &ALICE, USDT::one()), + Error::::InvalidTimestampOnBorrowRequest ); process_block(10001); - assert_ok!(Tokens::mint_into(USDT, &ALICE, collateral_amount)); + assert_ok!(Tokens::mint_into(USDT::ID, &ALICE, collateral_amount)); assert_ok!(Lending::deposit_collateral_internal(&market, &ALICE, collateral_amount)); let alice_limit = Lending::get_borrow_limit(&market, &ALICE).unwrap(); - assert!(price(BTC, alice_capable_btc) > alice_limit); - assert!(alice_limit > price(BTC, alice_borrow)); + assert!(get_price(BTC::ID, collateral_amount) > alice_limit); assert_noop!( - Lending::borrow_internal(&market, &ALICE, alice_limit), - Error::::NotEnoughCollateralToBorrowAmount + Lending::borrow_internal(&market, &ALICE, alice_limit * 100), + Error::::NotEnoughCollateralToBorrowAmount ); assert_ok!(Lending::borrow_internal(&market, &ALICE, 10)); @@ -492,14 +503,14 @@ fn vault_takes_part_of_borrow_so_cannot_withdraw() { let initial_total_cash = Lending::total_cash(&market_id).unwrap(); let deposit_usdt = 1_000_000_000; let deposit_btc = 10; - assert_ok!(Tokens::mint_into(USDT, &ALICE, deposit_usdt)); - assert_ok!(Tokens::mint_into(BTC, &ALICE, deposit_btc)); + assert_ok!(Tokens::mint_into(USDT::ID, &ALICE, deposit_usdt)); + assert_ok!(Tokens::mint_into(BTC::ID, &ALICE, deposit_btc)); assert_ok!(Vault::deposit(Origin::signed(*ALICE), vault_id, deposit_btc)); assert_ok!(Lending::deposit_collateral_internal(&market_id, &ALICE, deposit_usdt)); assert_noop!( Lending::borrow_internal(&market_id, &ALICE, deposit_btc + initial_total_cash), - Error::::NotEnoughBorrowAsset + Error::::NotEnoughBorrowAsset ); }); } @@ -511,8 +522,8 @@ fn test_vault_market_can_withdraw() { let collateral = 1_000_000_000_000; let borrow = 10; - assert_ok!(Tokens::mint_into(USDT, &ALICE, collateral)); - assert_ok!(Tokens::mint_into(BTC, &ALICE, borrow)); + assert_ok!(Tokens::mint_into(USDT::ID, &ALICE, collateral)); + assert_ok!(Tokens::mint_into(BTC::ID, &ALICE, borrow)); assert_ok!(Vault::deposit(Origin::signed(*ALICE), vault_id, borrow)); assert_ok!(Lending::deposit_collateral_internal(&market, &ALICE, collateral)); @@ -531,73 +542,51 @@ fn test_vault_market_can_withdraw() { } #[test] -fn borrow_repay() { +fn borrow_repay_repay() { new_test_ext().execute_with(|| { - let alice_balance = 1_000_000; - let bob_balance = 1_000_000; + let alice_balance = BTC::one(); + let bob_balance = BTC::one(); let (market, vault) = create_simple_market(); - // Balance for ALICE - assert_eq!(Tokens::balance(USDT, &ALICE), 0); - assert_ok!(Tokens::mint_into(USDT, &ALICE, alice_balance)); - assert_eq!(Tokens::balance(USDT, &ALICE), alice_balance); + assert_ok!(Tokens::mint_into(BTC::ID, &ALICE, alice_balance)); assert_ok!(Lending::deposit_collateral_internal(&market, &ALICE, alice_balance)); - assert_eq!(Tokens::balance(USDT, &ALICE), 0); - // Balance for BOB - assert_eq!(Tokens::balance(USDT, &BOB), 0); - assert_ok!(Tokens::mint_into(USDT, &BOB, bob_balance)); - assert_eq!(Tokens::balance(USDT, &BOB), bob_balance); + assert_ok!(Tokens::mint_into(BTC::ID, &BOB, bob_balance)); assert_ok!(Lending::deposit_collateral_internal(&market, &BOB, bob_balance)); - assert_eq!(Tokens::balance(USDT, &BOB), 0); - let borrow_asset_deposit = 10_000_000_000; - assert_eq!(Tokens::balance(BTC, &CHARLIE), 0); - assert_ok!(Tokens::mint_into(BTC, &CHARLIE, borrow_asset_deposit)); - assert_eq!(Tokens::balance(BTC, &CHARLIE), borrow_asset_deposit); + let borrow_asset_deposit = USDT::units(1_000_000); + assert_ok!(Tokens::mint_into(USDT::ID, &CHARLIE, borrow_asset_deposit)); assert_ok!(Vault::deposit(Origin::signed(*CHARLIE), vault, borrow_asset_deposit)); - // Allow the market to initialize it's account by withdrawing - // from the vault - for i in 1..2 { - process_block(i); - } + (1..2).for_each(process_block); - // ALICE borrows - assert_eq!(Lending::borrow_balance_current(&market, &ALICE), Ok(Some(0))); let alice_limit_normalized = Lending::get_borrow_limit(&market, &ALICE).unwrap(); - let alice_limit = alice_limit_normalized / Oracle::get_price(BTC, 1).unwrap().price; + let alice_limit = alice_limit_normalized * BTC::one() / get_price(BTC::ID, BTC::one()); assert_ok!(Lending::borrow_internal(&market, &ALICE, alice_limit)); - for i in 2..10000 { - process_block(i); - } + (2..1000).for_each(process_block); - // BOB borrows - assert_eq!(Lending::borrow_balance_current(&market, &BOB), Ok(Some(0))); - let limit_normalized = Lending::get_borrow_limit(&market, &BOB).unwrap(); - let limit = limit_normalized / Oracle::get_price(BTC, 1).unwrap().price; - assert_ok!(Lending::borrow_internal(&market, &BOB, limit,)); + let bob_limit_normalized = Lending::get_borrow_limit(&market, &BOB).unwrap(); + let bob_limit = bob_limit_normalized * BTC::one() / get_price(BTC::ID, BTC::one()); + assert_ok!(Lending::borrow_internal(&market, &BOB, bob_limit,)); - let bob_limit = Lending::get_borrow_limit(&market, &BOB).unwrap(); + let _bob_limit = Lending::get_borrow_limit(&market, &BOB).unwrap(); - for i in 10000..20000 { - process_block(i); - } + (1000..1000 + 100).for_each(process_block); let alice_repay_amount = Lending::borrow_balance_current(&market, &ALICE).unwrap(); let bob_repay_amount = Lending::borrow_balance_current(&market, &BOB).unwrap(); - // MINT required BTC so that ALICE and BOB can repay the borrow. - assert_ok!(Tokens::mint_into(BTC, &ALICE, alice_repay_amount.unwrap() - alice_limit)); - assert_ok!(Tokens::mint_into(BTC, &BOB, bob_repay_amount.unwrap() - bob_limit)); - // ALICE , BOB both repay's loan. their USDT balance should have decreased because of - // interest paid on borrows + assert_ok!(Tokens::mint_into(USDT::ID, &ALICE, alice_repay_amount.unwrap())); + assert_ok!(Tokens::mint_into(USDT::ID, &BOB, bob_repay_amount.unwrap())); + assert_ok!(Lending::repay_borrow_internal(&market, &BOB, &BOB, bob_repay_amount)); - assert_ok!(Lending::repay_borrow_internal(&market, &ALICE, &ALICE, alice_repay_amount)); - assert!(alice_balance > Tokens::balance(USDT, &ALICE)); - assert!(bob_balance > Tokens::balance(USDT, &BOB)); + assert!(bob_balance > Tokens::balance(BTC::ID, &BOB)); + + // TODO: fix bug with partial repay: + //assert_ok!(Lending::repay_borrow_internal(&market, &ALICE, &ALICE, alice_repay_amount)); + // assert!(alice_balance > Tokens::balance(BTC::ID, &ALICE)); }); } @@ -605,22 +594,20 @@ fn borrow_repay() { fn liquidation() { new_test_ext().execute_with(|| { let (market, vault) = create_market( - USDT, - BTC, + USDT::ID, + BTC::ID, *ALICE, Perquintill::from_percent(10), MoreThanOneFixedU128::saturating_from_rational(2, 1), ); - Oracle::set_btc_price(100); - - let two_btc_amount = 2; - assert_ok!(Tokens::mint_into(BTC, &ALICE, two_btc_amount)); - assert_ok!(Lending::deposit_collateral_internal(&market, &ALICE, two_btc_amount)); - assert_eq!(Tokens::balance(BTC, &ALICE), 0); + let collateral = BTC::units(100); + assert_ok!(Tokens::mint_into(BTC::ID, &ALICE, collateral)); + assert_ok!(Lending::deposit_collateral_internal(&market, &ALICE, collateral)); - let usdt_amt = u32::MAX as Balance; - assert_ok!(Tokens::mint_into(USDT, &CHARLIE, usdt_amt)); + let usdt_amt = 2 * DEFAULT_COLLATERAL_FACTOR * USDT::one() * get_price(BTC::ID, collateral) / + get_price(NORAMLIZED::ID, NORAMLIZED::one()); + assert_ok!(Tokens::mint_into(USDT::ID, &CHARLIE, usdt_amt)); assert_ok!(Vault::deposit(Origin::signed(*CHARLIE), vault, usdt_amt)); // Allow the market to initialize it's account by withdrawing @@ -638,9 +625,6 @@ fn liquidation() { process_block(i); } - // Collateral going down imply liquidation - Oracle::set_btc_price(99); - assert_ok!(Lending::liquidate_internal(&ALICE, &market, vec![*ALICE])); }); } @@ -649,74 +633,34 @@ fn liquidation() { fn test_warn_soon_under_collaterized() { new_test_ext().execute_with(|| { let (market, vault) = create_market( - USDT, - BTC, + USDT::ID, + BTC::ID, *ALICE, Perquintill::from_percent(10), MoreThanOneFixedU128::saturating_from_rational(2, 1), ); - // 1 BTC = 100 USDT - Oracle::set_btc_price(100); - - // Deposit 2 BTC - let two_btc_amount = 2; - assert_eq!(Tokens::balance(BTC, &ALICE), 0); - assert_ok!(Tokens::mint_into(BTC, &ALICE, two_btc_amount)); - assert_eq!(Tokens::balance(BTC, &ALICE), two_btc_amount); + let two_btc_amount = BTC::units(2); + assert_ok!(Tokens::mint_into(BTC::ID, &ALICE, two_btc_amount)); assert_ok!(Lending::deposit_collateral_internal(&market, &ALICE, two_btc_amount)); - assert_eq!(Tokens::balance(BTC, &ALICE), 0); - - // Balance of USDT for CHARLIE - // CHARLIE is only lender of USDT - let usdt_amt = u32::MAX as Balance; - assert_eq!(Tokens::balance(USDT, &CHARLIE), 0); - assert_ok!(Tokens::mint_into(USDT, &CHARLIE, usdt_amt)); - assert_eq!(Tokens::balance(USDT, &CHARLIE), usdt_amt); - assert_ok!(Vault::deposit(Origin::signed(*CHARLIE), vault, usdt_amt)); - // Allow the market to initialize it's account by withdrawing - // from the vault - for i in 1..2 { - process_block(i); - } - - // ALICE borrows - assert_eq!(Lending::borrow_balance_current(&market, &ALICE), Ok(Some(0))); + let usdt_amt = USDT::units(100_000); + assert_ok!(Tokens::mint_into(USDT::ID, &CHARLIE, usdt_amt)); + assert_ok!(Vault::deposit(Origin::signed(*CHARLIE), vault, usdt_amt)); - /* Can - = 1/collateral_factor * deposit - = 1/2 * two_btc_price - = 10000 cents + (1..2).for_each(process_block); - */ - assert_eq!(Lending::get_borrow_limit(&market, &ALICE), Ok(100)); + assert_eq!(Lending::get_borrow_limit(&market, &ALICE), Ok(50000000000000000)); - // Borrow 80 USDT - let borrow_amount = 80; + let borrow_amount = USDT::units(80); assert_ok!(Lending::borrow_internal(&market, &ALICE, borrow_amount)); - for i in 2..10000 { - process_block(i); - } - - Oracle::set_btc_price(90); + (2..10000).for_each(process_block); - /* - Collateral = 2*90 = 180 - Borrow = 80 - Ratio = 2.25 - */ assert_eq!(Lending::soon_under_collaterized(&market, &ALICE), Ok(false)); - - Oracle::set_btc_price(87); - - /* - Collateral = 2*87 = 174 - Borrow = 80 - Ratio = ~2.18 - */ + set_price(BTC::ID, NORAMLIZED::units(85)); assert_eq!(Lending::soon_under_collaterized(&market, &ALICE), Ok(true)); + assert_eq!(Lending::should_liquidate(&market, &ALICE), Ok(false)); }); } @@ -810,14 +754,11 @@ proptest! { fn market_collateral_deposit_withdraw_identity(amount in valid_amount_without_overflow()) { new_test_ext().execute_with(|| { let (market, _) = create_simple_market(); - prop_assert_eq!(Tokens::balance( USDT, &ALICE), 0); - prop_assert_ok!(Tokens::mint_into( USDT, &ALICE, amount)); - prop_assert_eq!(Tokens::balance( USDT, &ALICE), amount); - + let before = Tokens::balance( BTC::ID, &ALICE); + prop_assert_ok!(Tokens::mint_into( BTC::ID, &ALICE, amount)); prop_assert_ok!(Lending::deposit_collateral_internal(&market, &ALICE, amount)); - prop_assert_eq!(Tokens::balance( USDT, &ALICE), 0); prop_assert_ok!(Lending::withdraw_collateral_internal(&market, &ALICE, amount)); - prop_assert_eq!(Tokens::balance( USDT, &ALICE), amount); + prop_assert_eq!(Tokens::balance( BTC::ID, &ALICE) - before, amount); Ok(()) })?; @@ -827,15 +768,12 @@ proptest! { fn market_collateral_deposit_withdraw_higher_amount_fails(amount in valid_amount_without_overflow()) { new_test_ext().execute_with(|| { let (market, _vault) = create_simple_market(); - prop_assert_eq!(Tokens::balance( USDT, &ALICE), 0); - prop_assert_ok!(Tokens::mint_into( USDT, &ALICE, amount )); - prop_assert_eq!(Tokens::balance( USDT, &ALICE), amount ); - + prop_assert_ok!(Tokens::mint_into( BTC::ID, &ALICE, amount )); prop_assert_ok!(Lending::deposit_collateral_internal(&market, &ALICE, amount )); - prop_assert_eq!(Tokens::balance( USDT, &ALICE), 0); + prop_assert_eq!( Lending::withdraw_collateral_internal(&market, &ALICE, amount + 1), - Err(Error::::NotEnoughCollateral.into()) + Err(Error::::NotEnoughCollateral.into()) ); Ok(()) @@ -846,15 +784,11 @@ proptest! { fn market_collateral_vaulted_deposit_withdraw_identity(amount in valid_amount_without_overflow()) { new_test_ext().execute_with(|| { let ((market, _), collateral_asset) = create_simple_vaulted_market(); - - prop_assert_eq!(Tokens::balance(collateral_asset, &ALICE), 0); + let before = Tokens::balance(collateral_asset, &ALICE); prop_assert_ok!(Tokens::mint_into(collateral_asset, &ALICE, amount)); - prop_assert_eq!(Tokens::balance(collateral_asset, &ALICE), amount); - prop_assert_ok!(Lending::deposit_collateral_internal(&market, &ALICE, amount)); - prop_assert_eq!(Tokens::balance(collateral_asset, &ALICE), 0); prop_assert_ok!(Lending::withdraw_collateral_internal(&market, &ALICE, amount)); - prop_assert_eq!(Tokens::balance(collateral_asset, &ALICE), amount); + prop_assert_eq!(Tokens::balance(collateral_asset, &ALICE) - before, amount); Ok(()) })?; @@ -868,76 +802,36 @@ proptest! { })?; } - #[test] - fn market_creation_with_multi_level_priceable_lp(depth in 0..20) { - new_test_ext().execute_with(|| { - // Assume we have a pricable base asset - let base_asset = ETH; - let base_vault = create_simple_vault(base_asset); - - let (_, VaultInfo { lp_token_id, ..}) = - (0..depth).fold(base_vault, |(_, VaultInfo { lp_token_id, .. }), _| { - // No stock dilution, 1:1 price against the quote asset. - create_simple_vault(lp_token_id) - }); - - // A market with two priceable assets can be created - create_market( - BTC, - lp_token_id, - *ALICE, - Perquintill::from_percent(10), - MoreThanOneFixedU128::saturating_from_rational(200, 100), - ); - - // Top level lp price should be transitively resolvable to the base asset price. - prop_assert_ok!(Oracle::get_price(lp_token_id, 1)); - - // Without stock dilution, prices should be equals - prop_assert_eq!(Oracle::get_price(lp_token_id, 1), Oracle::get_price(base_asset, 1)); - - Ok(()) - })?; - } - #[test] fn market_are_isolated( (amount1, amount2) in valid_amounts_without_overflow_2() ) { new_test_ext().execute_with(|| { let (market_id1, vault_id1) = create_simple_market(); + let m1 = Tokens::balance(USDT::ID, &Lending::account_id(&market_id1)); let (market_id2, vault_id2) = create_simple_market(); + let m2 = Tokens::balance(USDT::ID, &Lending::account_id(&market_id2)); - // Ensure markets are unique prop_assert_ne!(market_id1, market_id2); prop_assert_ne!(Lending::account_id(&market_id1), Lending::account_id(&market_id2)); - // Alice lend an amount in market1 vault - prop_assert_ok!(Tokens::mint_into( BTC, &ALICE, amount1)); + prop_assert_ok!(Tokens::mint_into(USDT::ID, &ALICE, amount1)); prop_assert_ok!(Vault::deposit(Origin::signed(*ALICE), vault_id1, amount1)); - // Bob lend an amount in market2 vault - prop_assert_eq!(Tokens::balance( BTC, &BOB), 0); - prop_assert_ok!(Tokens::mint_into( BTC, &BOB, amount2)); - prop_assert_eq!(Tokens::balance( BTC, &BOB), amount2); - prop_assert_ok!(Vault::deposit(Origin::signed(*BOB), vault_id2, amount2)); + prop_assert_ok!(Tokens::mint_into(USDT::ID, &BOB, 10*amount2)); + prop_assert_ok!(Vault::deposit(Origin::signed(*BOB), vault_id2, 10*amount2)); - // Allow the market to initialize it's account by withdrawing - // from the vault - for i in 1..2 { - process_block(i); - } + (1..2).for_each(process_block); let expected_market1_balance = DEFAULT_MARKET_VAULT_STRATEGY_SHARE.mul(amount1); - let expected_market2_balance = DEFAULT_MARKET_VAULT_STRATEGY_SHARE.mul(amount2); + let expected_market2_balance = DEFAULT_MARKET_VAULT_STRATEGY_SHARE.mul(10*amount2); - // // The funds should not be shared. prop_assert_acceptable_computation_error!( - Tokens::balance( BTC, &Lending::account_id(&market_id1)), + Tokens::balance(USDT::ID, &Lending::account_id(&market_id1)) - m1, expected_market1_balance ); prop_assert_acceptable_computation_error!( - Tokens::balance( BTC, &Lending::account_id(&market_id2)), + Tokens::balance(USDT::ID, &Lending::account_id(&market_id2)) - m2, expected_market2_balance ); diff --git a/frame/liquidations/src/lib.rs b/frame/liquidations/src/lib.rs index 1acea1a4e46..a89d6ac454e 100644 --- a/frame/liquidations/src/lib.rs +++ b/frame/liquidations/src/lib.rs @@ -239,6 +239,7 @@ pub mod pallet { "as for now, only auction liquidators implemented", )), }; + if result.is_ok() { Self::deposit_event(Event::::PositionWasSentToLiquidation {}); return result diff --git a/frame/liquidations/src/mock/runtime.rs b/frame/liquidations/src/mock/runtime.rs index 5fe338ab4cf..398b7811fd6 100644 --- a/frame/liquidations/src/mock/runtime.rs +++ b/frame/liquidations/src/mock/runtime.rs @@ -56,54 +56,30 @@ parameter_types! { pub const SS58Prefix: u8 = 42; pub const BlockHashCount: u64 = 250; } - impl frame_system::Config for Runtime { type BaseCallFilter = Everything; - type BlockWeights = (); - type BlockLength = (); - type Origin = Origin; - type Call = Call; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Header = Header; - type Event = Event; - type BlockHashCount = BlockHashCount; - type DbWeight = (); - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = SS58Prefix; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; } diff --git a/frame/mosaic/Cargo.toml b/frame/mosaic/Cargo.toml index 80f4aca0386..a1683c592de 100644 --- a/frame/mosaic/Cargo.toml +++ b/frame/mosaic/Cargo.toml @@ -40,7 +40,7 @@ num-traits = { version = "0.2.14", default-features = false } plotters = {version = "0.3.1", optional = true} [dev-dependencies] -proptest = "0.9.6" +proptest = "1.0" orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "2b1c9fb367ccb8e13601b2da43d1c5d9737b93c6", default-features = false} orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "2b1c9fb367ccb8e13601b2da43d1c5d9737b93c6", default-features = false } composable-tests-helpers = { path = "../composable-tests-helpers", default-features = false } diff --git a/frame/oracle/src/benchmarking.rs b/frame/oracle/src/benchmarking.rs index d33b46d79ef..342ccf4ffcc 100644 --- a/frame/oracle/src/benchmarking.rs +++ b/frame/oracle/src/benchmarking.rs @@ -2,6 +2,7 @@ use super::*; #[allow(unused)] use crate::Pallet as Oracle; +use composable_traits::oracle::Price; use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller}; use frame_support::{ assert_ok, diff --git a/frame/oracle/src/lib.rs b/frame/oracle/src/lib.rs index 5dfb21f3036..ca95ac62e82 100644 --- a/frame/oracle/src/lib.rs +++ b/frame/oracle/src/lib.rs @@ -32,7 +32,7 @@ pub mod pallet { use composable_traits::{ currency::LocalAssets, math::SafeArithmetic, - oracle::{Oracle, Price as LastPrice}, + oracle::{Oracle, Price}, }; use core::ops::{Div, Mul}; use frame_support::{ @@ -169,14 +169,6 @@ pub mod pallet { pub who: AccountId, } - // block timestamped value - #[derive(Encode, Decode, MaxEncodedLen, Default, Debug, PartialEq, TypeInfo, Clone)] - pub struct Price { - /// value - pub price: PriceValue, - pub block: BlockNumber, - } - #[derive(Encode, Decode, MaxEncodedLen, Default, Debug, PartialEq, Clone, TypeInfo)] pub struct AssetInfo { pub threshold: Percent, @@ -388,7 +380,7 @@ pub mod pallet { fn get_price( asset_id: Self::AssetId, amount: Self::Balance, - ) -> Result, DispatchError> { + ) -> Result, DispatchError> { let Price { price, block } = Prices::::try_get(asset_id).map_err(|_| Error::::PriceNotFound)?; let unit = 10_u128 @@ -399,7 +391,7 @@ pub mod pallet { let price = price .try_into() .map_err(|_| DispatchError::Arithmetic(ArithmeticError::Overflow))?; - Ok(LastPrice { price, block }) + Ok(Price { price, block }) } fn get_twap( @@ -420,6 +412,7 @@ pub mod pallet { Self::get_price(pair.quote, (10 ^ T::LocalAssets::decimals(pair.base)?).into())? .price .into(); + let base = FixedU128::saturating_from_integer(base); let quote = FixedU128::saturating_from_integer(quote); Ok(base.safe_div("e)?) @@ -436,6 +429,7 @@ pub mod pallet { // so we need 2_500 asset amount to pay for 10 normalized let unit = 10 ^ (T::LocalAssets::decimals(asset_id))?; let price_asset_for_unit: u128 = Self::get_price(asset_id, unit.into())?.price.into(); + let amount: u128 = amount.into(); let result = multiply_by_rational(amount, unit as u128, price_asset_for_unit)?; let result: u64 = result diff --git a/frame/oracle/src/tests.rs b/frame/oracle/src/tests.rs index 93c3a4b565c..2ecdf61a15a 100644 --- a/frame/oracle/src/tests.rs +++ b/frame/oracle/src/tests.rs @@ -1,9 +1,9 @@ use crate::{ mock::{AccountId, Call, Extrinsic, *}, - AssetInfo, Error, PrePrice, Price, Withdraw, *, + AssetInfo, Error, PrePrice, Withdraw, *, }; use codec::Decode; -use composable_traits::defi::CurrencyPair; +use composable_traits::{defi::CurrencyPair, oracle::Price}; use frame_support::{ assert_noop, assert_ok, traits::{Currency, OnInitialize}, diff --git a/frame/vault/Cargo.toml b/frame/vault/Cargo.toml index c3745b43635..dbb1bb09050 100644 --- a/frame/vault/Cargo.toml +++ b/frame/vault/Cargo.toml @@ -35,7 +35,7 @@ scale-info = { version = "1.0", default-features = false, features = ["derive"] [dev-dependencies] once_cell = "1.8.0" -proptest = "0.9.6" +proptest = "1.0" serde = "1.0.130" orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "2b1c9fb367ccb8e13601b2da43d1c5d9737b93c6", default-features = false } orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "2b1c9fb367ccb8e13601b2da43d1c5d9737b93c6", default-features = false } diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 417a792f522..449918abcab 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -74,7 +74,7 @@ transaction-payment-rpc-runtime-api = { package = "pallet-transaction-payment-rp # Used for runtime benchmarking benchmarking = { package = "frame-benchmarking", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false, optional = true } system-benchmarking = { package = "frame-system-benchmarking", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false, optional = true } -hex-literal = { version = "0.3.1", optional = true } +hex-literal = { version = "0.3.3", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = [ "derive", ] } diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 7be3eb3005a..77f964cae1a 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -16,10 +16,13 @@ pub mod impls; pub use constants::*; pub use types::*; -/// Common types of statemint and statemine. +/// Common types of statemint and statemine and dali and picasso and composable. mod types { use sp_runtime::traits::{IdentifyAccount, Verify}; + // todo move it into more shared directory so it can be shared with + // tests, integration, benchmark, (simnode?) + /// An index to a block. pub type BlockNumber = u32; diff --git a/runtime/composable/Cargo.toml b/runtime/composable/Cargo.toml index db6b0b7c27a..8c2149dd153 100644 --- a/runtime/composable/Cargo.toml +++ b/runtime/composable/Cargo.toml @@ -89,7 +89,7 @@ crowdloan-rewards-runtime-api = { path = '../../frame/crowdloan-rewards/runtime- # Used for runtime benchmarking frame-benchmarking = { package = "frame-benchmarking", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false, optional = true } system-benchmarking = { package = "frame-system-benchmarking", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false, optional = true } -hex-literal = { version = "0.3.1", optional = true } +hex-literal = { version = "0.3.3", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = [ "derive", ] } diff --git a/runtime/composable/src/lib.rs b/runtime/composable/src/lib.rs index dd8bedaf205..e263d4aaee4 100644 --- a/runtime/composable/src/lib.rs +++ b/runtime/composable/src/lib.rs @@ -101,7 +101,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`, // `spec_version`, and `authoring_version` are the same between Wasm and native. spec_version: 1000, - impl_version: 2, + impl_version: 4, apis: RUNTIME_API_VERSIONS, transaction_version: 2, state_version: 0, diff --git a/runtime/dali/Cargo.toml b/runtime/dali/Cargo.toml index 189feb3aba8..1b00221005d 100644 --- a/runtime/dali/Cargo.toml +++ b/runtime/dali/Cargo.toml @@ -77,6 +77,7 @@ common = { path = "../common", default-features = false } primitives = { path = "../primitives", default-features = false } oracle = { package = "pallet-oracle", path = "../../frame/oracle", default-features = false } vault = { package = "pallet-vault", path = "../../frame/vault", default-features = false } +lending = { package = "pallet-lending", path = "../../frame/lending", default-features = false } governance-registry = { package = "pallet-governance-registry", path = "../../frame/governance-registry", default-features = false } currency-factory = { package = "pallet-currency-factory", path = "../../frame/currency-factory", default-features = false } composable-traits = { path = "../../frame/composable-traits", default-features = false } @@ -95,7 +96,7 @@ crowdloan-rewards-runtime-api = { path = '../../frame/crowdloan-rewards/runtime- # Used for runtime benchmarking frame-benchmarking = { package = "frame-benchmarking", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false, optional = true } system-benchmarking = { package = "frame-system-benchmarking", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false, optional = true } -hex-literal = { version = "0.3.1", optional = true } +hex-literal = { version = "0.3.3", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = [ "derive", ] } @@ -197,7 +198,7 @@ std = [ "crowdloan-rewards/std", "bonded-finance/std", "dutch-auction/std", - "liquidations/std", + "lending/std", "vesting/std", "mosaic/std", "preimage/std", @@ -243,5 +244,6 @@ runtime-benchmarks = [ "mosaic/runtime-benchmarks", "currency-factory/runtime-benchmarks", "liquidations/runtime-benchmarks", + "lending/runtime-benchmarks", ] sim-node = [] diff --git a/runtime/dali/src/lib.rs b/runtime/dali/src/lib.rs index 3d8b8ad86c2..02335c9b107 100644 --- a/runtime/dali/src/lib.rs +++ b/runtime/dali/src/lib.rs @@ -105,7 +105,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. spec_version: 1000, - impl_version: 5, + impl_version: 8, apis: RUNTIME_API_VERSIONS, transaction_version: 3, state_version: 0, @@ -927,6 +927,31 @@ impl liquidations::Config for Runtime { type PalletId = LiquidationsPalletId; } +parameter_types! { + pub const MaxLendingCount: u32 = 10; + pub LendingPalletId: PalletId = PalletId(*b"liqiudat"); + pub OracleMarketCreationStake : Balance = 300; +} + +impl lending::Config for Runtime { + type Event = Event; + type Oracle = Oracle; + type VaultId = u64; + type Vault = Vault; + type CurrencyFactory = CurrencyFactory; + type MultiCurrency = Assets; + type Liquidation = Liquidations; + type UnixTime = Timestamp; + type MaxLendingCount = MaxLendingCount; + type AuthorityId = oracle::crypto::BathurstStId; + type WeightInfo = (); + type LiquidationStrategyId = u32; + type OracleMarketCreationStake = OracleMarketCreationStake; + type PalletId = LendingPalletId; + type NativeCurrency = Balances; + type WeightToFee = WeightToFee; +} + construct_runtime!( pub enum Runtime where Block = Block, @@ -961,7 +986,7 @@ construct_runtime!( Democracy: democracy::{Pallet, Call, Storage, Config, Event} = 33, Scheduler: scheduler::{Pallet, Call, Storage, Event} = 34, Utility: utility::{Pallet, Call, Event} = 35, - Preimage: preimage::{Pallet, Call, Storage, Event} = 36, + Preimage: preimage::{Pallet, Call, Storage, Event} = 36, // XCM helpers. XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event} = 40, @@ -984,6 +1009,7 @@ construct_runtime!( DutchAuction: dutch_auction::{Pallet, Call, Storage, Event} = 61, Mosaic: mosaic::{Pallet, Call, Storage, Event} = 62, Liquidations: liquidations::{Pallet, Call, Storage, Event} = 63, + Lending: lending::{Pallet, Call, Storage, Event} = 64, CallFilter: call_filter::{Pallet, Call, Storage, Event} = 100, } @@ -1053,6 +1079,7 @@ mod benches { [mosaic, Mosaic] [liquidations, Liquidations] [bonded_finance, BondedFinance] + [lending, Lending] ); } @@ -1222,13 +1249,9 @@ impl_runtime_apis! { use system_benchmarking::Pallet as SystemBench; use session_benchmarking::Pallet as SessionBench; - let mut list = Vec::::new(); - list_benchmarks!(list, extra); - let storage_info = AllPalletsWithSystem::storage_info(); - return (list, storage_info) } diff --git a/runtime/dali/src/weights/lending.rs b/runtime/dali/src/weights/lending.rs new file mode 100644 index 00000000000..46861d4e97f --- /dev/null +++ b/runtime/dali/src/weights/lending.rs @@ -0,0 +1,69 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +pub struct WeightInfo(PhantomData); + +impl lending::weights::WeightInfo for WeightInfo { + fn create_new_market() -> Weight { + (96_881_000 as Weight) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(11 as Weight)) + } + fn deposit_collateral() -> Weight { + 123_789_000_u64 + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + fn withdraw_collateral() -> Weight { + 138_802_000_u64 + .saturating_add(T::DbWeight::get().reads(10_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + fn borrow() -> Weight { + (332_730_000 as Weight) + .saturating_add(T::DbWeight::get().reads(19 as Weight)) + .saturating_add(T::DbWeight::get().writes(9 as Weight)) + } + fn repay_borrow() -> Weight { + (209_694_000 as Weight) + .saturating_add(T::DbWeight::get().reads(13 as Weight)) + .saturating_add(T::DbWeight::get().writes(6 as Weight)) + } + fn now() -> Weight { + (4_744_000 as Weight).saturating_add(T::DbWeight::get().reads(1 as Weight)) + } + fn accrue_interest() -> Weight { + (76_626_000 as Weight) + .saturating_add(T::DbWeight::get().reads(8 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn account_id() -> Weight { + (3_126_000 as Weight) + } + fn available_funds() -> Weight { + (16_450_000 as Weight).saturating_add(T::DbWeight::get().reads(2 as Weight)) + } + fn handle_withdrawable() -> Weight { + (20_716_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn handle_depositable() -> Weight { + (40_066_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn handle_must_liquidate() -> Weight { + (38_744_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + + fn liquidate(positions_count: Weight) -> Weight { + 10000 * positions_count + } +} \ No newline at end of file diff --git a/runtime/dali/src/weights/liqudations.rs b/runtime/dali/src/weights/liqudations.rs index c2b31b6815e..5a9964398f9 100644 --- a/runtime/dali/src/weights/liqudations.rs +++ b/runtime/dali/src/weights/liqudations.rs @@ -5,7 +5,6 @@ use frame_support::{traits::Get, weights::Weight}; use sp_std::marker::PhantomData; -/// Weight functions for `mosaic`. pub struct WeightInfo(PhantomData); impl liquidation::weights::WeightInfo for WeightInfo { diff --git a/runtime/picasso/Cargo.toml b/runtime/picasso/Cargo.toml index 0aff18de667..6bd1109fc72 100644 --- a/runtime/picasso/Cargo.toml +++ b/runtime/picasso/Cargo.toml @@ -90,7 +90,7 @@ crowdloan-rewards-runtime-api = { path = '../../frame/crowdloan-rewards/runtime- # Used for runtime benchmarking frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false, optional = true } system-benchmarking = { package = "frame-system-benchmarking", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false, optional = true } -hex-literal = { version = "0.3.1", optional = true } +hex-literal = { version = "0.3.3", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] } # Parachain Utilities diff --git a/runtime/picasso/src/lib.rs b/runtime/picasso/src/lib.rs index 8be6f7bde3b..326b160c29d 100644 --- a/runtime/picasso/src/lib.rs +++ b/runtime/picasso/src/lib.rs @@ -101,7 +101,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`, // `spec_version`, and `authoring_version` are the same between Wasm and native. spec_version: 1000, - impl_version: 5, + impl_version: 8, apis: RUNTIME_API_VERSIONS, transaction_version: 3, state_version: 0, diff --git a/scripts/benchmarks.sh b/scripts/run_benchmarks.sh similarity index 100% rename from scripts/benchmarks.sh rename to scripts/run_benchmarks.sh diff --git a/utils/collator-sidecar/Cargo.toml b/utils/collator-sidecar/Cargo.toml index 3c6b51550a9..a386b03af9c 100644 --- a/utils/collator-sidecar/Cargo.toml +++ b/utils/collator-sidecar/Cargo.toml @@ -10,7 +10,7 @@ env_logger = "0.8.4" tide = "0.16" async-std = { version = "1", features = ["attributes", "tokio1"] } serde = { version = "1", features = ["derive"] } -url = "1" +url = "1.7.0" tokio = { version = "1.12.0", features = ["macros", "rt-multi-thread"] } structopt = "0.3.25" diff --git a/utils/parachain-utils/Cargo.toml b/utils/parachain-utils/Cargo.toml index b7d0bed11f5..dc8d1779182 100644 --- a/utils/parachain-utils/Cargo.toml +++ b/utils/parachain-utils/Cargo.toml @@ -11,7 +11,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] env_logger = "0.8.4" log = "0.4.14" -url = "1.7" +url = "1.7.0" sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16" } frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16" } transaction-payment = { package = "pallet-transaction-payment", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16" } diff --git a/utils/price-feed/Cargo.toml b/utils/price-feed/Cargo.toml index 427f972fc35..fcdc7de8a9a 100644 --- a/utils/price-feed/Cargo.toml +++ b/utils/price-feed/Cargo.toml @@ -13,8 +13,8 @@ tokio = { version = "1.9", features = [ "full" ] } tokio-stream = "0.1" url = "1.7.0" chrono = "0.4.19" -serde = { version = "1.0", features = [ "derive" ] } -serde_json = "1.0" +serde = { version = "1.0.130", features = [ "derive" ] } +serde_json = "1.0.45" jsonrpc-core = "18.0.0" jsonrpc-client-transports = "18.0.0" clap = { version = "3.0.0-rc.4", features = [ "derive" ] }