diff --git a/Cargo.lock b/Cargo.lock index 658be220d..0d8c469dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1194,8 +1194,12 @@ dependencies = [ "frame-system-rpc-runtime-api", "frame-try-runtime", "hex-literal 0.4.1", + "lend-market", + "lend-market-rpc-runtime-api", + "leverage-staking", "log", "merkle-distributor", + "orml-oracle", "orml-tokens", "orml-traits", "orml-unknown-tokens", @@ -1215,6 +1219,7 @@ dependencies = [ "pallet-membership", "pallet-multisig", "pallet-preimage", + "pallet-prices", "pallet-proxy", "pallet-ranked-collective", "pallet-referenda", @@ -1355,8 +1360,12 @@ dependencies = [ "frame-system-rpc-runtime-api", "frame-try-runtime", "hex-literal 0.4.1", + "lend-market", + "lend-market-rpc-runtime-api", + "leverage-staking", "log", "merkle-distributor", + "orml-oracle", "orml-tokens", "orml-traits", "orml-unknown-tokens", @@ -1377,6 +1386,7 @@ dependencies = [ "pallet-membership", "pallet-multisig", "pallet-preimage", + "pallet-prices", "pallet-proxy", "pallet-ranked-collective", "pallet-referenda", @@ -1427,6 +1437,7 @@ version = "0.8.0" dependencies = [ "bstringify", "frame-support", + "orml-oracle", "parity-scale-codec", "scale-info", "serde", @@ -1453,6 +1464,8 @@ dependencies = [ "bifrost-ve-minting-rpc", "bifrost-ve-minting-rpc-runtime-api", "jsonrpsee", + "lend-market-rpc", + "lend-market-rpc-runtime-api", "pallet-transaction-payment-rpc", "sc-client-api", "sc-rpc", @@ -6048,6 +6061,100 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "lend-market" +version = "0.8.0" +dependencies = [ + "bifrost-asset-registry", + "bifrost-currencies", + "bifrost-primitives", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "num-traits", + "orml-oracle", + "orml-tokens", + "orml-traits", + "pallet-assets", + "pallet-balances", + "pallet-prices", + "pallet-timestamp", + "pallet-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm-builder", +] + +[[package]] +name = "lend-market-rpc" +version = "0.8.0" +dependencies = [ + "bifrost-primitives", + "jsonrpsee", + "lend-market-rpc-runtime-api", + "parity-scale-codec", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-rpc", + "sp-runtime", +] + +[[package]] +name = "lend-market-rpc-runtime-api" +version = "0.8.0" +dependencies = [ + "bifrost-primitives", + "parity-scale-codec", + "sp-api", + "sp-runtime", +] + +[[package]] +name = "leverage-staking" +version = "0.8.0" +dependencies = [ + "bifrost-asset-registry", + "bifrost-currencies", + "bifrost-primitives", + "bifrost-runtime-common", + "bifrost-stable-asset", + "bifrost-stable-pool", + "bifrost-vtoken-minting", + "env_logger 0.10.0", + "frame-benchmarking", + "frame-support", + "frame-system", + "lend-market", + "log", + "orml-oracle", + "orml-tokens", + "orml-traits", + "orml-xtokens", + "pallet-balances", + "pallet-collective", + "pallet-prices", + "pallet-timestamp", + "pallet-traits", + "pallet-xcm", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + [[package]] name = "libc" version = "0.2.149" @@ -7418,6 +7525,24 @@ dependencies = [ "num-traits", ] +[[package]] +name = "orml-oracle" +version = "0.4.1-dev" +source = "git+https://github.com/open-web3-stack/open-runtime-module-library?rev=b3694e631df7f1ca16b1973122937753fcdee9d4#b3694e631df7f1ca16b1973122937753fcdee9d4" +dependencies = [ + "frame-support", + "frame-system", + "orml-traits", + "orml-utilities", + "parity-scale-codec", + "scale-info", + "serde", + "sp-application-crypto", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "orml-tokens" version = "0.4.1-dev" @@ -8197,6 +8322,33 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-prices" +version = "0.8.0" +dependencies = [ + "bifrost-asset-registry", + "bifrost-currencies", + "bifrost-primitives", + "frame-support", + "frame-system", + "log", + "num-traits", + "orml-oracle", + "orml-tokens", + "orml-traits", + "pallet-assets", + "pallet-balances", + "pallet-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", +] + [[package]] name = "pallet-proxy" version = "4.0.0-dev" @@ -8461,6 +8613,29 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-traits" +version = "0.8.0" +dependencies = [ + "bifrost-primitives", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "num-bigint", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 6fb7a57bd..8557d610e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,9 @@ members = [ "pallets/parachain-staking", "pallets/ve-minting", "pallets/stable-pool", + "pallets/lend-market", + "pallets/prices", + "pallets/leverage-staking", "runtime/bifrost-kusama", "runtime/bifrost-polkadot", @@ -46,6 +49,7 @@ bifrost-rpc = { path = "node/rpc" } bifrost-salp-rpc = { path = "pallets/salp/rpc" } bifrost-stable-pool-rpc = { path = "pallets/stable-pool/rpc" } bifrost-ve-minting-rpc = { path = "pallets/ve-minting/rpc" } +lend-market-rpc = { path = "pallets/lend-market/rpc" } # Bifrost Runtime bifrost-kusama-runtime = { path = "runtime/bifrost-kusama" } @@ -83,6 +87,11 @@ bifrost-vstoken-conversion = { path = "pallets/vstoken-conversion", de bifrost-vtoken-minting = { path = "pallets/vtoken-minting", default-features = false } bifrost-vtoken-voting = { path = "pallets/vtoken-voting", default-features = false } bifrost-xcm-interface = { path = "pallets/xcm-interface", default-features = false } +lend-market = { path = "pallets/lend-market", default-features = false } +lend-market-rpc-runtime-api = { path = "pallets/lend-market/rpc/runtime-api", default-features = false } +pallet-prices = { path = "pallets/prices", default-features = false } +pallet-traits = { path = "pallets/traits", default-features = false } +leverage-staking = { path = "pallets/leverage-staking", default-features = false } # Zenlink merkle-distributor = { git = "https://github.com/bifrost-finance/merkle-distributor", branch = "release-polkadot-v1.1.0", default-features = false } @@ -102,6 +111,7 @@ orml-utilities = { git = "https://github.com/open-web3-stack/open-runtime-m orml-xcm = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "b3694e631df7f1ca16b1973122937753fcdee9d4", default-features = false } orml-xcm-support = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "b3694e631df7f1ca16b1973122937753fcdee9d4", default-features = false } orml-xtokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "b3694e631df7f1ca16b1973122937753fcdee9d4", default-features = false } +orml-oracle = { git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "b3694e631df7f1ca16b1973122937753fcdee9d4", default-features = false } # Substrate Client frame-benchmarking-cli = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } @@ -283,18 +293,21 @@ scale-info = { version = "2.10.0", default-features = false } serde_json = { version = "1.0.105", default-features = false } serde = { version = "1.0.188", default-features = false } strum = { version = "0.24.1", default-features = false, features = ["derive"] } +num-traits = { version = "0.2.15", default-features = false } +num-bigint = { version = "0.4.3", default-features = false } +impl-trait-for-tuples = { version = "0.2.2" } [profile.dev] split-debuginfo = "unpacked" [profile.release] -panic = "unwind" opt-level = 3 +panic = "unwind" [profile.production] +codegen-units = 1 inherits = "release" lto = true -codegen-units = 1 # The list of dependencies below (which can be both direct and indirect dependencies) are crates # that are suspected to be CPU-intensive, and that are unlikely to require debugging (as some of diff --git a/node/rpc/Cargo.toml b/node/rpc/Cargo.toml index 5985c7083..e4ed12119 100644 --- a/node/rpc/Cargo.toml +++ b/node/rpc/Cargo.toml @@ -30,6 +30,8 @@ bifrost-ve-minting-rpc = { workspace = true } bifrost-ve-minting-rpc-runtime-api = { workspace = true } bifrost-stable-pool-rpc = { workspace = true } bifrost-stable-pool-rpc-runtime-api = { workspace = true } +lend-market-rpc = { workspace = true } +lend-market-rpc-runtime-api = { workspace = true } zenlink-protocol = { workspace = true } zenlink-protocol-rpc = { workspace = true } zenlink-protocol-runtime-api = { workspace = true } diff --git a/node/rpc/src/lib.rs b/node/rpc/src/lib.rs index 79f8defbd..c80f9bfe7 100644 --- a/node/rpc/src/lib.rs +++ b/node/rpc/src/lib.rs @@ -44,6 +44,8 @@ use bifrost_stable_pool_rpc::{StablePoolRpc, StablePoolRpcApiServer}; use bifrost_stable_pool_rpc_runtime_api::StablePoolRuntimeApi; use bifrost_ve_minting_rpc::{VeMintingRpc, VeMintingRpcApiServer}; use bifrost_ve_minting_rpc_runtime_api::VeMintingRuntimeApi; +use lend_market_rpc::{LendMarket, LendMarketApiServer}; +use lend_market_rpc_runtime_api::LendMarketApi; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; use sc_rpc_api::DenyUnsafe; use sc_transaction_pool_api::TransactionPool; @@ -86,6 +88,7 @@ where C::Api: FeeRuntimeApi, C::Api: SalpRuntimeApi, C::Api: StablePoolRuntimeApi, + C::Api: LendMarketApi, C::Api: ZenlinkProtocolRuntimeApi, C::Api: zenlink_stable_amm_runtime_api::StableAmmApi, @@ -103,7 +106,8 @@ where module.merge(SalpRpc::new(client.clone()).into_rpc())?; module.merge(ZenlinkProtocol::new(client.clone()).into_rpc())?; module.merge(StableAmm::new(client.clone()).into_rpc())?; - module.merge(StablePoolRpc::new(client).into_rpc())?; + module.merge(StablePoolRpc::new(client.clone()).into_rpc())?; + module.merge(LendMarket::new(client).into_rpc())?; Ok(module) } @@ -126,6 +130,7 @@ where C::Api: FeeRuntimeApi, C::Api: SalpRuntimeApi, C::Api: VeMintingRuntimeApi, + C::Api: LendMarketApi, C::Api: ZenlinkProtocolRuntimeApi, C::Api: StablePoolRuntimeApi, C::Api: BlockBuilder, @@ -142,7 +147,8 @@ where module.merge(SalpRpc::new(client.clone()).into_rpc())?; module.merge(VeMintingRpc::new(client.clone()).into_rpc())?; module.merge(ZenlinkProtocol::new(client.clone()).into_rpc())?; - module.merge(StablePoolRpc::new(client).into_rpc())?; + module.merge(StablePoolRpc::new(client.clone()).into_rpc())?; + module.merge(LendMarket::new(client).into_rpc())?; Ok(module) } diff --git a/node/service/src/chain_spec/bifrost_kusama.rs b/node/service/src/chain_spec/bifrost_kusama.rs index 95e042d34..634493ae2 100644 --- a/node/service/src/chain_spec/bifrost_kusama.rs +++ b/node/service/src/chain_spec/bifrost_kusama.rs @@ -24,9 +24,10 @@ use std::{ use bifrost_kusama_runtime::{ constants::currency::DOLLARS, AccountId, AssetRegistryConfig, Balance, BalancesConfig, BlockNumber, CouncilConfig, CouncilMembershipConfig, DefaultBlocksPerRound, DemocracyConfig, - IndicesConfig, InflationInfo, ParachainInfoConfig, ParachainStakingConfig, PolkadotXcmConfig, - Range, RuntimeGenesisConfig, SS58Prefix, SalpConfig, SessionConfig, SystemConfig, - TechnicalCommitteeConfig, TechnicalMembershipConfig, TokensConfig, VestingConfig, WASM_BINARY, + IndicesConfig, InflationInfo, OracleMembershipConfig, ParachainInfoConfig, + ParachainStakingConfig, PolkadotXcmConfig, Range, RuntimeGenesisConfig, SS58Prefix, SalpConfig, + SessionConfig, SystemConfig, TechnicalCommitteeConfig, TechnicalMembershipConfig, TokensConfig, + VestingConfig, WASM_BINARY, }; use bifrost_primitives::{CurrencyId, CurrencyId::*, TokenInfo, TokenSymbol, TokenSymbol::*}; use bifrost_runtime_common::AuraId; @@ -130,6 +131,7 @@ pub fn bifrost_genesis( Vec, Vec<(CurrencyId, u32, u32, u32)>, ), + oracle_membership: Vec, ) -> RuntimeGenesisConfig { RuntimeGenesisConfig { system: SystemConfig { @@ -147,6 +149,10 @@ pub fn bifrost_genesis( members: technical_committee_membership.try_into().expect("convert error!"), phantom: Default::default(), }, + oracle_membership: OracleMembershipConfig { + members: oracle_membership.try_into().expect("convert error!"), + phantom: Default::default(), + }, council: CouncilConfig { members: vec![], phantom: Default::default() }, technical_committee: TechnicalCommitteeConfig { members: vec![], @@ -222,6 +228,7 @@ fn development_config_genesis(id: ParaId) -> RuntimeGenesisConfig { let council_membership = vec![get_account_id_from_seed::("Alice")]; let technical_committee_membership = vec![get_account_id_from_seed::("Alice")]; + let oracle_membership = vec![get_account_id_from_seed::("Alice")]; let salp_multisig: AccountId = hex!["49daa32c7287890f38b7e1a8cd2961723d36d20baa0bf3b82e0c4bdda93b1c0a"].into(); @@ -241,6 +248,7 @@ fn development_config_genesis(id: ParaId) -> RuntimeGenesisConfig { technical_committee_membership, salp_multisig, (vec![], vec![], vec![]), + oracle_membership, ) } @@ -310,6 +318,7 @@ fn local_config_genesis(id: ParaId) -> RuntimeGenesisConfig { let council_membership = vec![get_account_id_from_seed::("Alice")]; let technical_committee_membership = vec![get_account_id_from_seed::("Alice")]; + let oracle_membership = vec![get_account_id_from_seed::("Alice")]; let salp_multisig: AccountId = hex!["49daa32c7287890f38b7e1a8cd2961723d36d20baa0bf3b82e0c4bdda93b1c0a"].into(); @@ -376,6 +385,7 @@ fn local_config_genesis(id: ParaId) -> RuntimeGenesisConfig { technical_committee_membership, salp_multisig, (currency, vcurrency, vsbond), + oracle_membership, ) } @@ -451,6 +461,10 @@ fn rococo_testnet_config_genesis(id: ParaId) -> RuntimeGenesisConfig { // dDWnEWnx3GUgfugXh9mZtgj4CvJdmd8naYkWYCZGxjfb1Cz hex!["420398e0150cd9d417fb8fd4027b75bd42717262e6eac97c55f2f8f84e8ffb3f"].into(), ]; + let oracle_membership = vec![ + // dDWnEWnx3GUgfugXh9mZtgj4CvJdmd8naYkWYCZGxjfb1Cz + hex!["420398e0150cd9d417fb8fd4027b75bd42717262e6eac97c55f2f8f84e8ffb3f"].into(), + ]; bifrost_genesis( invulnerables, @@ -470,6 +484,7 @@ fn rococo_testnet_config_genesis(id: ParaId) -> RuntimeGenesisConfig { vec![], vec![], ), + oracle_membership, ) } @@ -509,6 +524,7 @@ fn rococo_local_config_genesis(id: ParaId) -> RuntimeGenesisConfig { let council_membership = vec![get_account_id_from_seed::("Alice")]; let technical_committee_membership = vec![get_account_id_from_seed::("Alice")]; + let oracle_membership = vec![get_account_id_from_seed::("Alice")]; bifrost_genesis( vec![ @@ -532,6 +548,7 @@ fn rococo_local_config_genesis(id: ParaId) -> RuntimeGenesisConfig { technical_committee_membership, salp_multisig, (vec![(Token(DOT), 100_000_000, None), (Token(KSM), 10_000_000, None)], vec![], vec![]), + oracle_membership, ) } @@ -648,6 +665,7 @@ fn bifrost_config_genesis(id: ParaId) -> RuntimeGenesisConfig { vec![], // technical committee membership salp_multisig, (vec![], vec![], vec![]), + vec![], ) } diff --git a/node/service/src/chain_spec/bifrost_polkadot.rs b/node/service/src/chain_spec/bifrost_polkadot.rs index 9d7a76d44..8625c1e57 100644 --- a/node/service/src/chain_spec/bifrost_polkadot.rs +++ b/node/service/src/chain_spec/bifrost_polkadot.rs @@ -19,9 +19,9 @@ use bifrost_polkadot_runtime::{ constants::currency::DOLLARS, AccountId, AssetRegistryConfig, Balance, BalancesConfig, BlockNumber, CollatorSelectionConfig, CouncilMembershipConfig, IndicesConfig, - ParachainInfoConfig, PolkadotXcmConfig, RuntimeGenesisConfig, SS58Prefix, SalpConfig, - SessionConfig, SystemConfig, TechnicalMembershipConfig, TokensConfig, VestingConfig, - WASM_BINARY, + OracleMembershipConfig, ParachainInfoConfig, PolkadotXcmConfig, RuntimeGenesisConfig, + SS58Prefix, SalpConfig, SessionConfig, SystemConfig, TechnicalMembershipConfig, TokensConfig, + VestingConfig, WASM_BINARY, }; use bifrost_primitives::{CurrencyId, CurrencyId::*, TokenInfo, TokenSymbol, DOT_TOKEN_ID}; use bifrost_runtime_common::AuraId; @@ -84,6 +84,7 @@ pub fn bifrost_polkadot_genesis( Vec, Vec<(CurrencyId, u32, u32, u32)>, ), + oracle_membership: Vec, ) -> RuntimeGenesisConfig { RuntimeGenesisConfig { system: SystemConfig { @@ -101,6 +102,10 @@ pub fn bifrost_polkadot_genesis( members: technical_committee_membership.try_into().expect("convert error!"), phantom: Default::default(), }, + oracle_membership: OracleMembershipConfig { + members: oracle_membership.try_into().expect("convert error!"), + phantom: Default::default(), + }, council: Default::default(), technical_committee: Default::default(), treasury: Default::default(), @@ -161,6 +166,7 @@ fn development_config_genesis(id: ParaId) -> RuntimeGenesisConfig { let council_membership = vec![get_account_id_from_seed::("Alice")]; let technical_committee_membership = vec![get_account_id_from_seed::("Alice")]; + let oracle_membership = vec![get_account_id_from_seed::("Alice")]; let salp_multisig: AccountId = hex!["49daa32c7287890f38b7e1a8cd2961723d36d20baa0bf3b82e0c4bdda93b1c0a"].into(); @@ -177,6 +183,7 @@ fn development_config_genesis(id: ParaId) -> RuntimeGenesisConfig { technical_committee_membership, salp_multisig, (vec![], vec![], vec![]), + oracle_membership, ) } @@ -224,6 +231,7 @@ fn local_config_genesis(id: ParaId) -> RuntimeGenesisConfig { .collect(); let council_membership = vec![get_account_id_from_seed::("Alice")]; let technical_committee_membership = vec![get_account_id_from_seed::("Alice")]; + let oracle_membership = vec![get_account_id_from_seed::("Alice")]; let salp_multisig: AccountId = hex!["49daa32c7287890f38b7e1a8cd2961723d36d20baa0bf3b82e0c4bdda93b1c0a"].into(); let currency = vec![ @@ -252,6 +260,7 @@ fn local_config_genesis(id: ParaId) -> RuntimeGenesisConfig { technical_committee_membership, salp_multisig, (currency, vcurrency, vec![]), + oracle_membership, ) } @@ -326,5 +335,6 @@ fn bifrost_polkadot_config_genesis(id: ParaId) -> RuntimeGenesisConfig { vec![], salp_multisig, (vec![], vec![], vec![]), + vec![], ) } diff --git a/pallets/lend-market/Cargo.toml b/pallets/lend-market/Cargo.toml index d7af2ceb4..e530d0f87 100644 --- a/pallets/lend-market/Cargo.toml +++ b/pallets/lend-market/Cargo.toml @@ -8,43 +8,42 @@ version = "0.8.0" targets = ['x86_64-unknown-linux-gnu'] [dependencies] -log = { version = "0.4.17", default-features = false } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false, optional = true } -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -num-traits = { version = '0.2.15', default-features = false } -orml-traits = { version = "0.4.1-dev", default-features = false } -pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -pallet-prices = { path = "../prices", default-features = false } -bifrost-primitives = { path = "../../primitives", default-features = false } -pallet-timestamp = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -pallet-traits = { path = "../traits", default-features = false } -scale-info = { version = "2.9.0", default-features = false, features = [ - "derive", -] } -serde = { version = "1.0.160", features = ['derive'], optional = true } -sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -xcm-builder = { package = "staging-xcm-builder", git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false} +bifrost-primitives = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +log = { workspace = true } +num-traits = { workspace = true } +orml-traits = { workspace = true } +pallet-assets = { workspace = true } +pallet-balances = { workspace = true } +pallet-prices = { workspace = true } +pallet-timestamp = { workspace = true } +pallet-traits = { workspace = true } +parity-scale-codec = { workspace = true, features = ["derive"] } +scale-info = { workspace = true, features = ["derive"] } +serde = { workspace = true, features = ['derive'], optional = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } +xcm-builder = { workspace = true } [dev-dependencies] -orml-oracle = "0.4.1-dev" -orml-tokens = "0.4.1-dev" -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -bifrost-asset-registry = { path = "../asset-registry", default-features = false } -bifrost-currencies = { path = "../currencies", default-features = false } +bifrost-asset-registry = { workspace = true } +bifrost-currencies = { workspace = true } +orml-oracle = { workspace = true } +orml-tokens = { workspace = true } +sp-core = { workspace = true } [features] default = ['std'] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", ] std = [ 'log/std', - 'codec/std', + 'parity-scale-codec/std', 'frame-support/std', 'frame-system/std', 'frame-benchmarking/std', @@ -54,6 +53,7 @@ std = [ 'sp-std/std', 'sp-io/std', 'pallet-prices/std', + 'pallet-assets/std', 'pallet-balances/std', 'pallet-timestamp/std', 'serde', diff --git a/pallets/lend-market/rpc/Cargo.toml b/pallets/lend-market/rpc/Cargo.toml index c59064090..bf9d9e42d 100644 --- a/pallets/lend-market/rpc/Cargo.toml +++ b/pallets/lend-market/rpc/Cargo.toml @@ -5,25 +5,17 @@ name = 'lend-market-rpc' version = "0.8.0" [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ - "derive", -] } -jsonrpsee = { version = "0.16.2", features = ["server", "macros"] } -bifrost-primitives = { path = "../../../primitives", default-features = false } -serde = { version = "1.0.160", features = ["derive"] } -sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", features = [ - 'std', -] } -sp-blockchain = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", features = [ - 'std', -] } -sp-rpc = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", features = [ - 'std', -] } +bifrost-primitives = { workspace = true } +parity-scale-codec = { workspace = true, features = ["derive"] } +jsonrpsee = { workspace = true, features = ["server", "macros"] } +serde = { workspace = true, features = ["derive"] } +sp-api = { workspace = true } +sp-blockchain = { workspace = true } +sp-core = { workspace = true, features = ['std'] } +sp-rpc = { workspace = true } +sp-runtime = { workspace = true } -lend-market-rpc-runtime-api = { path = 'runtime-api', default-features = false } +lend-market-rpc-runtime-api = { workspace = true } [lib] doctest = false diff --git a/pallets/lend-market/rpc/runtime-api/Cargo.toml b/pallets/lend-market/rpc/runtime-api/Cargo.toml index 270aa3a5a..381688bc5 100644 --- a/pallets/lend-market/rpc/runtime-api/Cargo.toml +++ b/pallets/lend-market/rpc/runtime-api/Cargo.toml @@ -5,16 +5,14 @@ name = 'lend-market-rpc-runtime-api' version = "0.8.0" [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ - "derive", -] } -bifrost-primitives = { path = "../../../../primitives", default-features = false } -sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +bifrost-primitives = { workspace = true } +parity-scale-codec = { workspace = true, features = ["derive"] } +sp-api = { workspace = true } +sp-runtime = { workspace = true } [features] default = ['std'] -std = ['codec/std', 'sp-api/std', 'sp-runtime/std'] +std = ['parity-scale-codec/std', 'sp-api/std', 'sp-runtime/std'] [lib] doctest = false diff --git a/pallets/lend-market/rpc/runtime-api/src/lib.rs b/pallets/lend-market/rpc/runtime-api/src/lib.rs index 54aaf99d6..3410ec519 100644 --- a/pallets/lend-market/rpc/runtime-api/src/lib.rs +++ b/pallets/lend-market/rpc/runtime-api/src/lib.rs @@ -15,7 +15,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use bifrost_primitives::{CurrencyId, Liquidity, Rate, Ratio, Shortfall}; -use codec::Codec; +use parity_scale_codec::Codec; use sp_runtime::{DispatchError, FixedU128}; sp_api::decl_runtime_apis! { diff --git a/pallets/lend-market/rpc/src/lib.rs b/pallets/lend-market/rpc/src/lib.rs index 71cf64f8e..6ea27754b 100644 --- a/pallets/lend-market/rpc/src/lib.rs +++ b/pallets/lend-market/rpc/src/lib.rs @@ -17,12 +17,12 @@ use std::sync::Arc; pub use lend_market_rpc_runtime_api::LendMarketApi as LendMarketRuntimeApi; use bifrost_primitives::{CurrencyId, Liquidity, Rate, Ratio, Shortfall}; -use codec::Codec; use jsonrpsee::{ core::{async_trait, Error as JsonRpseeError, RpcResult}, proc_macros::rpc, types::error::{CallError, ErrorCode, ErrorObject}, }; +use parity_scale_codec::Codec; use sp_api::ProvideRuntimeApi; use sp_blockchain::HeaderBackend; use sp_rpc::number::NumberOrHex; diff --git a/pallets/lend-market/src/lib.rs b/pallets/lend-market/src/lib.rs index d9297af37..58c3b0d03 100644 --- a/pallets/lend-market/src/lib.rs +++ b/pallets/lend-market/src/lib.rs @@ -81,10 +81,10 @@ pub const MIN_INTEREST_CALCULATING_INTERVAL: u64 = 100; // 100 seconds pub const MAX_EXCHANGE_RATE: u128 = 1_000_000_000_000_000_000; // 1 pub const MIN_EXCHANGE_RATE: u128 = 20_000_000_000_000_000; // 0.02 -type AccountIdOf = ::AccountId; -type AssetIdOf = +pub type AccountIdOf = ::AccountId; +pub type AssetIdOf = <::Assets as Inspect<::AccountId>>::AssetId; -type BalanceOf = +pub type BalanceOf = <::Assets as Inspect<::AccountId>>::Balance; /// Utility type for managing upgrades/migrations. @@ -1875,6 +1875,7 @@ impl Pallet { if !lf_enable && total_liquidity >= lf_liquidity + reduce_amount { return Ok(()); } + Err(Error::::InsufficientLiquidity.into()) } diff --git a/pallets/lend-market/src/tests.rs b/pallets/lend-market/src/tests.rs index 35dbaa864..b4d871049 100644 --- a/pallets/lend-market/src/tests.rs +++ b/pallets/lend-market/src/tests.rs @@ -18,14 +18,13 @@ mod lend_tokens; mod liquidate_borrow; mod market; +use crate::mock::*; use frame_support::{assert_err, assert_noop, assert_ok}; use sp_runtime::{ traits::{CheckedDiv, One, Saturating}, FixedU128, Permill, }; -use crate::mock::*; - #[test] fn init_minting_ok() { new_test_ext().execute_with(|| { diff --git a/pallets/lend-market/src/types.rs b/pallets/lend-market/src/types.rs index b35083e77..b4b51f1f2 100644 --- a/pallets/lend-market/src/types.rs +++ b/pallets/lend-market/src/types.rs @@ -33,7 +33,16 @@ pub struct Deposits { /// The current state of a market. For more information, see [Market]. #[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] -#[derive(Clone, Copy, PartialEq, Eq, codec::Decode, codec::Encode, RuntimeDebug, TypeInfo)] +#[derive( + Clone, + Copy, + PartialEq, + Eq, + parity_scale_codec::Decode, + parity_scale_codec::Encode, + RuntimeDebug, + TypeInfo, +)] pub enum MarketState { Active, Pending, @@ -44,7 +53,15 @@ pub enum MarketState { /// /// A large pool of liquidity where accounts can lend and borrow. #[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] -#[derive(Clone, PartialEq, Eq, codec::Decode, codec::Encode, RuntimeDebug, TypeInfo)] +#[derive( + Clone, + PartialEq, + Eq, + parity_scale_codec::Decode, + parity_scale_codec::Encode, + RuntimeDebug, + TypeInfo, +)] pub struct Market { /// The collateral utilization ratio pub collateral_factor: Ratio, diff --git a/pallets/leverage-staking/Cargo.toml b/pallets/leverage-staking/Cargo.toml new file mode 100644 index 000000000..29b3d0165 --- /dev/null +++ b/pallets/leverage-staking/Cargo.toml @@ -0,0 +1,79 @@ +[package] +authors = ["yooml "] +edition = "2021" +name = "leverage-staking" +version = "0.8.0" + +[package.metadata.docs.rs] +targets = ['x86_64-unknown-linux-gnu'] + +[dependencies] +bifrost-primitives = { workspace = true } +bifrost-stable-asset = { workspace = true } +bifrost-stable-pool = { workspace = true } +bifrost-vtoken-minting = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +lend-market = { workspace = true } +log = { workspace = true } +orml-traits = { workspace = true } +pallet-balances = { workspace = true } +pallet-collective = { workspace = true } +pallet-prices = { workspace = true } +pallet-timestamp = { workspace = true } +pallet-traits = { workspace = true } +parity-scale-codec = { workspace = true, features = ["derive"] } +scale-info = { workspace = true, features = ["derive"] } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +[dev-dependencies] +bifrost-asset-registry = { workspace = true } +bifrost-currencies = { workspace = true } +bifrost-runtime-common = { workspace = true } +env_logger = { workspace = true } +orml-oracle = { workspace = true } +orml-tokens = { workspace = true } +orml-xtokens = { workspace = true } +pallet-balances = { workspace = true } +pallet-xcm = { workspace = true } +sp-io = { workspace = true } +xcm = { workspace = true } +xcm-builder = { workspace = true } +xcm-executor = { workspace = true } + +[features] +default = ['std'] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "pallet-collective/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", +] +std = [ + "parity-scale-codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "pallet-balances/std", + "pallet-timestamp/std", + "bifrost-stable-asset/std", + "orml-traits/std", + "bifrost-primitives/std", + "pallet-traits/std", + "bifrost-asset-registry/std", + "bifrost-runtime-common/std", + "bifrost-asset-registry/std", + "xcm-builder/std", + "lend-market/std", + "bifrost-stable-pool/std", + "pallet-prices/std", + "log/std", +] +try-runtime = ['frame-support/try-runtime'] + +[lib] +doctest = false diff --git a/pallets/leverage-staking/src/benchmarking.rs b/pallets/leverage-staking/src/benchmarking.rs new file mode 100644 index 000000000..6888b8dc9 --- /dev/null +++ b/pallets/leverage-staking/src/benchmarking.rs @@ -0,0 +1,191 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +use crate::{Pallet, *}; +pub use bifrost_primitives::{ + AccountId, Balance, CurrencyId, CurrencyIdMapping, SlpOperator, SlpxOperator, TokenSymbol, BNC, + GLMR, KSM, VKSM, *, +}; +use frame_benchmarking::{account, v2::*}; +use frame_support::assert_ok; +use frame_system::RawOrigin as SystemOrigin; +use lend_market::{self, InterestRateModel, JumpModel, Market, MarketState}; +use orml_traits::MultiCurrency; +use sp_runtime::{ + traits::{StaticLookup, UniqueSaturatedFrom}, + FixedPointNumber, +}; + +pub fn unit(d: u128) -> u128 { + d.saturating_mul(10_u128.pow(12)) +} + +pub const fn market_mock(lend_token_id: CurrencyId) -> Market { + Market { + close_factor: Ratio::from_percent(50), + collateral_factor: Ratio::from_percent(50), + liquidation_threshold: Ratio::from_percent(55), + liquidate_incentive: Rate::from_inner(Rate::DIV / 100 * 110), + liquidate_incentive_reserved_factor: Ratio::from_percent(3), + state: MarketState::Pending, + rate_model: InterestRateModel::Jump(JumpModel { + base_rate: Rate::from_inner(Rate::DIV / 100 * 2), + jump_rate: Rate::from_inner(Rate::DIV / 100 * 10), + full_rate: Rate::from_inner(Rate::DIV / 100 * 32), + jump_utilization: Ratio::from_percent(80), + }), + reserve_factor: Ratio::from_percent(15), + supply_cap: 1_000_000_000_000_000_000_000u128, // set to 1B + borrow_cap: 1_000_000_000_000_000_000_000u128, // set to 1B + lend_token_id, + } +} + +fn init< + T: Config + + bifrost_stable_pool::Config + + bifrost_vtoken_minting::Config + + pallet_prices::Config + + pallet_balances::Config, +>() -> Result<(), BenchmarkError> { + let caller: AccountIdOf = account("caller", 1, SEED); + let account_id = T::Lookup::unlookup(caller.clone()); + pallet_balances::Pallet::::force_set_balance( + SystemOrigin::Root.into(), + account_id, + 10_000_000_000_000_u128, + ) + .unwrap(); + pallet_prices::Pallet::::set_price(SystemOrigin::Root.into(), KSM, 1.into()).unwrap(); + pallet_prices::Pallet::::set_price(SystemOrigin::Root.into(), VKSM, 1.into()).unwrap(); + ::MultiCurrency::deposit( + KSM.into(), + &caller, + ::Balance::from(unit(1_000_000).into()), + )?; + ::MultiCurrency::deposit( + VKSM.into(), + &caller, + ::Balance::from(unit(1_000_000).into()), + )?; + let fee_account: AccountIdOf = account("caller", 2, 2); + pallet_balances::Pallet::::force_set_balance( + SystemOrigin::Root.into(), + T::Lookup::unlookup(caller.clone()), + 10_000_000_000_000_u128, + ) + .unwrap(); + pallet_balances::Pallet::::force_set_balance( + SystemOrigin::Root.into(), + T::Lookup::unlookup(fee_account.clone()), + 10_000_000_000_000_u128, + ) + .unwrap(); + + assert_ok!(lend_market::Pallet::::add_market( + SystemOrigin::Root.into(), + KSM, + market_mock(VKSM) + )); + assert_ok!(lend_market::Pallet::::activate_market(SystemOrigin::Root.into(), KSM)); + assert_ok!(lend_market::Pallet::::add_market( + SystemOrigin::Root.into(), + VKSM, + market_mock(VBNC) + )); + assert_ok!(lend_market::Pallet::::activate_market(SystemOrigin::Root.into(), VKSM)); + assert_ok!(lend_market::Pallet::::mint( + SystemOrigin::Signed(caller.clone()).into(), + KSM, + unit(100) + )); + + let coin0 = KSM; + let coin1 = VKSM; + let amounts = vec![ + ::Balance::from(unit(100u128).into()), + ::Balance::from(unit(100u128).into()), + ]; + assert_ok!(bifrost_stable_pool::Pallet::::create_pool( + SystemOrigin::Root.into(), + vec![coin0.into(), coin1.into()], + vec![1u128.into(), 1u128.into()], + 0u128.into(), + 0u128.into(), + 0u128.into(), + 220u128.into(), + fee_account.clone(), + fee_account.clone(), + 1000000000000u128.into() + )); + assert_ok!(bifrost_stable_pool::Pallet::::edit_token_rate( + SystemOrigin::Root.into(), + 0, + vec![ + (KSM.into(), (1u128.into(), 1u128.into())), + (VKSM.into(), (90_000_000u128.into(), 100_000_000u128.into())) + ] + )); + assert_ok!(bifrost_stable_pool::Pallet::::add_liquidity( + SystemOrigin::Signed(caller.clone()).into(), + 0, + amounts, + ::Balance::zero() + )); + + assert_ok!(bifrost_vtoken_minting::Pallet::::mint( + SystemOrigin::Signed(caller.clone()).into(), + KSM, + bifrost_vtoken_minting::BalanceOf::::unique_saturated_from(unit(100u128)), + BoundedVec::default() + )); + + Ok(()) +} + +const SEED: u32 = 1; + +#[benchmarks(where T: Config + bifrost_stable_pool::Config + bifrost_vtoken_minting::Config + bifrost_stable_asset::pallet::Config + pallet_prices::Config + pallet_balances::Config )] +mod benchmarks { + use lend_market::AccountIdOf; + + use super::*; + + #[benchmark] + fn flash_loan_deposit() -> Result<(), BenchmarkError> { + init::()?; + let caller: AccountIdOf = account("caller", 1, SEED); + let coin0 = KSM; + let rate = FixedU128::from_inner(unit(990_000)); + + #[extrinsic_call] + Pallet::::flash_loan_deposit( + SystemOrigin::Signed(caller.clone()), + coin0.into(), + rate, + Some(unit(1).into()), + ); + + Ok(()) + } + + impl_benchmark_test_suite!( + Pallet, + crate::mock::ExtBuilder::default().new_test_ext().build(), + crate::mock::Test + ); +} diff --git a/pallets/leverage-staking/src/lib.rs b/pallets/leverage-staking/src/lib.rs new file mode 100644 index 000000000..551eafa2c --- /dev/null +++ b/pallets/leverage-staking/src/lib.rs @@ -0,0 +1,323 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +#![cfg_attr(not(feature = "std"), no_std)] +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; + +use bifrost_primitives::{CurrencyIdConversion, Rate, VtokenMintingInterface}; +use frame_support::{ + pallet_prelude::*, + traits::{ + fungibles::{Inspect, Mutate}, + tokens::{Fortitude, Precision}, + }, + BoundedVec, +}; +use frame_system::{ensure_signed, pallet_prelude::*}; +pub use pallet_traits::{ + ConvertToBigUint, LendMarket as LendMarketTrait, LendMarketMarketDataProvider, + LendMarketPositionDataProvider, MarketInfo, MarketStatus, PriceFeeder, +}; +pub use parity_scale_codec::{Decode, Encode}; +use sp_runtime::{ + traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Zero}, + ArithmeticError, FixedU128, Permill, RuntimeDebug, +}; +use sp_std::marker::PhantomData; +pub use weights::WeightInfo; + +use bifrost_stable_pool::traits::StablePoolHandler; +use lend_market::{AccountIdOf, AssetIdOf, BalanceOf, Markets}; +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config + lend_market::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + type WeightInfo: WeightInfo; + + type ControlOrigin: EnsureOrigin; + + type VtokenMinting: VtokenMintingInterface< + AccountIdOf, + AssetIdOf, + BalanceOf, + >; + + type LendMarket: LendMarketTrait, AccountIdOf, BalanceOf>; + + type StablePoolHandler: StablePoolHandler< + Balance = BalanceOf, + AccountId = AccountIdOf, + CurrencyId = AssetIdOf, + >; + + type CurrencyIdConversion: CurrencyIdConversion>; + } + + #[pallet::error] + pub enum Error { + ArgumentsError, + NotSupportTokenType, + InsufficientBalance, + } + + #[pallet::event] + #[pallet::generate_deposit(pub (crate) fn deposit_event)] + pub enum Event { + FlashLoanDeposited { + who: AccountIdOf, + asset_id: AssetIdOf, + amount: BalanceOf, + rate: Rate, + }, + FlashLoanRepaid { + who: AccountIdOf, + asset_id: AssetIdOf, + rate: Rate, + }, + } + + #[pallet::storage] + pub type AccountFlashLoans = StorageDoubleMap< + _, + Blake2_128Concat, + AssetIdOf, + Blake2_128Concat, + T::AccountId, + AccountFlashLoanInfo>, + >; + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(::WeightInfo::flash_loan_deposit())] + pub fn flash_loan_deposit( + origin: OriginFor, + asset_id: AssetIdOf, + rate: Rate, + maybe_input_value: Option>, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + let input_value = + if let Some(flash_loan_info) = AccountFlashLoans::::get(asset_id, &who) { + Self::do_repay(&who, asset_id, None)?; + let value = maybe_input_value.unwrap_or(flash_loan_info.amount); + value + } else { + let value = maybe_input_value.ok_or(Error::::ArgumentsError)?; + value + }; + + if rate.is_zero() { + return Ok(().into()); + } + + let free_balance = ::Assets::balance(asset_id, &who); + ensure!(free_balance >= input_value, Error::::InsufficientBalance); + + let mut token_total_value = FixedU128::from_inner(input_value) + .checked_mul(&rate) + .map(|r| r.into_inner()) + .ok_or(ArithmeticError::Underflow)?; + + let vtoken_id = T::CurrencyIdConversion::convert_to_vtoken(asset_id) + .map_err(|_| Error::::NotSupportTokenType)?; + + let mut vtoken_total_amount: BalanceOf = Zero::zero(); + if let Some(market) = Markets::::get(asset_id) { + let mut token_value = input_value; + while token_total_value > Zero::zero() { + let vtoken_value = T::VtokenMinting::mint( + who.clone(), + asset_id, + token_value, + BoundedVec::default(), + )?; + T::LendMarket::do_mint(&who, vtoken_id, vtoken_value)?; + let deposits = lend_market::Pallet::::account_deposits(vtoken_id, &who); + if !deposits.is_collateral { + T::LendMarket::do_collateral_asset(&who, vtoken_id, true)?; + } + + token_value = market.collateral_factor * token_value; + token_value = match token_total_value < token_value { + true => { + vtoken_total_amount = vtoken_total_amount + .checked_add(vtoken_value) + .ok_or(ArithmeticError::Overflow)?; + T::LendMarket::do_borrow(&who, asset_id, token_total_value)?; + let vtoken_value = T::VtokenMinting::mint( + who.clone(), + asset_id, + token_total_value, + BoundedVec::default(), + )?; + T::LendMarket::do_mint(&who, vtoken_id, vtoken_value)?; + let deposits = + lend_market::Pallet::::account_deposits(vtoken_id, &who); + if !deposits.is_collateral { + T::LendMarket::do_collateral_asset(&who, vtoken_id, true)?; + } + vtoken_total_amount = vtoken_total_amount + .checked_add(vtoken_value) + .ok_or(ArithmeticError::Overflow)?; + token_total_value = Zero::zero(); + token_total_value + }, + false => { + vtoken_total_amount = vtoken_total_amount + .checked_add(vtoken_value) + .ok_or(ArithmeticError::Overflow)?; + T::LendMarket::do_borrow(&who, asset_id, token_value)?; + token_total_value = token_total_value.saturating_sub(token_value); + token_value + }, + }; + } + AccountFlashLoans::::insert( + asset_id, + &who, + AccountFlashLoanInfo { + amount: input_value, + leverage_rate: rate, + vtoken_amount: vtoken_total_amount, + collateral_factor: market.collateral_factor, + }, + ); + } + + Self::deposit_event(Event::::FlashLoanDeposited { + who, + asset_id, + rate, + amount: input_value, + }); + Ok(().into()) + } + } +} + +impl Pallet { + pub fn do_repay( + who: &T::AccountId, + asset_id: AssetIdOf, + maybe_rate: Option, + ) -> DispatchResult { + let vtoken_id = T::CurrencyIdConversion::convert_to_vtoken(asset_id) + .map_err(|_| Error::::NotSupportTokenType)?; + + AccountFlashLoans::::try_mutate_exists( + asset_id, + &who, + |maybe_flash_loan_info| -> DispatchResult { + let flash_loan_info = + maybe_flash_loan_info.as_mut().ok_or(Error::::ArgumentsError)?; + let rate = match maybe_rate { + Some(r) => { + ensure!(flash_loan_info.leverage_rate >= r, Error::::ArgumentsError); + r + }, + None => flash_loan_info.leverage_rate, + }; + + let token_value = FixedU128::from_inner(flash_loan_info.amount) + .checked_mul(&rate) + .map(|r| r.into_inner()) + .ok_or(ArithmeticError::Underflow)?; + let (pool_id, currency_id_in, currency_id_out) = + T::StablePoolHandler::get_pool_id(&vtoken_id, &asset_id) + .ok_or(Error::::ArgumentsError)?; + + ::Assets::mint_into(asset_id, &who, token_value)?; + let total_rate = flash_loan_info + .leverage_rate + .checked_add(&FixedU128::from_inner(10_u128.pow(18))) + .ok_or(ArithmeticError::Underflow)?; + let vtoken_value = FixedU128::from_inner(flash_loan_info.vtoken_amount) + .checked_mul(&rate) + .and_then(|r: FixedU128| r.checked_div(&total_rate)) + .map(|r: FixedU128| r.into_inner()) + .ok_or(ArithmeticError::Underflow)?; + + T::LendMarket::do_repay_borrow(&who, asset_id, token_value)?; + T::LendMarket::do_redeem(&who, vtoken_id, vtoken_value)?; + + T::StablePoolHandler::swap( + &who, + pool_id, + currency_id_in, + currency_id_out, + vtoken_value, + vtoken_value, + )?; + ::Assets::burn_from( + asset_id, + &who, + token_value, + Precision::Exact, + Fortitude::Force, + )?; + flash_loan_info.leverage_rate = flash_loan_info + .leverage_rate + .checked_sub(&rate) + .ok_or(ArithmeticError::Underflow)?; + + flash_loan_info.vtoken_amount = flash_loan_info + .vtoken_amount + .checked_sub(vtoken_value) + .ok_or(ArithmeticError::Underflow)?; + + if flash_loan_info.leverage_rate.is_zero() { + *maybe_flash_loan_info = None; + } + + Self::deposit_event(Event::::FlashLoanRepaid { + who: who.clone(), + asset_id, + rate, + }); + Ok(()) + }, + ) + } +} + +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct AccountFlashLoanInfo { + amount: Balance, + vtoken_amount: Balance, + leverage_rate: Rate, + collateral_factor: Permill, +} diff --git a/pallets/leverage-staking/src/mock.rs b/pallets/leverage-staking/src/mock.rs new file mode 100644 index 000000000..ec7b5b7d8 --- /dev/null +++ b/pallets/leverage-staking/src/mock.rs @@ -0,0 +1,595 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +pub use super::*; + +use crate as leverage_staking; +use bifrost_asset_registry::AssetIdMaps; +pub use bifrost_primitives::{ + AccountId, Balance, CurrencyId, CurrencyIdMapping, SlpOperator, SlpxOperator, TokenSymbol, + ASTR, BNC, DOT, DOT_TOKEN_ID, GLMR, VBNC, VDOT, *, +}; +use bifrost_runtime_common::milli; +use frame_support::{ + ord_parameter_types, parameter_types, + traits::{ConstU128, ConstU16, ConstU32, ConstU64, Everything, Nothing}, + PalletId, +}; +use frame_system::{EnsureRoot, EnsureSignedBy}; +use lend_market::{InterestRateModel, JumpModel, Market, MarketState}; +use orml_traits::{ + location::RelativeReserveProvider, parameter_type_with_key, DataFeeder, DataProvider, + DataProviderExtended, +}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, FixedPointNumber, +}; +use std::{ + cell::RefCell, + collections::HashMap, + hash::{Hash, Hasher}, +}; +use xcm::{ + prelude::*, + v3::{MultiLocation, Weight}, +}; +use xcm_builder::FixedWeightBounds; +use xcm_executor::XcmExecutor; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test{ + System: frame_system, + Tokens: orml_tokens, + Currencies: bifrost_currencies::{Pallet, Call}, + Balances: pallet_balances, + XTokens: orml_xtokens::{Pallet, Call, Event}, + PolkadotXcm: pallet_xcm, + AssetRegistry: bifrost_asset_registry, + StableAsset: bifrost_stable_asset::{Pallet, Storage, Event}, + StablePool: bifrost_stable_pool, + VtokenMinting: bifrost_vtoken_minting::{Pallet, Call, Storage, Event}, + LendMarket: lend_market::{Pallet, Storage, Call, Event}, + TimestampPallet: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + LeverageStaking: leverage_staking::{Pallet, Storage, Call, Event}, + Prices: pallet_prices::{Pallet, Storage, Call, Event}, + // PolkadotXcm: pallet_xcm, + } +); + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u32; + type Block = Block; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u128; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const NativeCurrencyId: CurrencyId = CurrencyId::Native(TokenSymbol::BNC); +} + +orml_traits::parameter_type_with_key! { + pub ExistentialDeposits: |currency_id: CurrencyId| -> Balance { + env_logger::try_init().unwrap_or(()); + + match currency_id { + &CurrencyId::Native(TokenSymbol::BNC) => 10 * milli::(NativeCurrencyId::get()), // 0.01 BNC + &CurrencyId::Token(TokenSymbol::KSM) => 0, + &CurrencyId::VToken(TokenSymbol::KSM) => 0, + &DOT => 0, + &VDOT => 0, + &VBNC => 0, + &CurrencyId::BLP(_) => 0, + _ => bifrost_asset_registry::AssetIdMaps::::get_currency_metadata(*currency_id) + .map_or(Balance::max_value(), |metatata| metatata.minimal_balance) + } + }; +} +impl orml_tokens::Config for Test { + type Amount = i128; + type Balance = Balance; + type CurrencyId = CurrencyId; + type DustRemovalWhitelist = Nothing; + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposits = ExistentialDeposits; + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); + type CurrencyHooks = (); +} + +parameter_types! { + pub const GetNativeCurrencyId: CurrencyId = BNC; +} + +pub type BlockNumber = u64; +pub type Amount = i128; +pub type AdaptedBasicCurrency = + bifrost_currencies::BasicCurrencyAdapter; + +impl bifrost_currencies::Config for Test { + type GetNativeCurrencyId = GetNativeCurrencyId; + type MultiCurrency = Tokens; + type NativeCurrency = AdaptedBasicCurrency; + type WeightInfo = (); +} + +parameter_types! { + // One XCM operation is 200_000_000 XcmWeight, cross-chain transfer ~= 2x of transfer = 3_000_000_000 + pub UnitWeightCost: Weight = Weight::from_parts(200_000_000, 0); + pub const MaxInstructions: u32 = 100; + pub UniversalLocation: InteriorMultiLocation = X1(Parachain(2001)); +} + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type AssetClaims = PolkadotXcm; + type AssetTransactor = (); + type AssetTrap = PolkadotXcm; + type Barrier = (); + type RuntimeCall = RuntimeCall; + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type OriginConverter = (); + type ResponseHandler = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type Trader = (); + type Weigher = FixedWeightBounds; + type XcmSender = (); + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = ConstU32<64>; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type AssetLocker = (); + type AssetExchanger = (); + type Aliasers = Nothing; +} + +parameter_type_with_key! { + pub ParachainMinFee: |_location: MultiLocation| -> Option { + Some(u128::MAX) + }; +} + +parameter_types! { + pub SelfRelativeLocation: MultiLocation = MultiLocation::here(); + // pub const BaseXcmWeight: Weight = Weight::from_ref_time(1000_000_000u64); + pub const MaxAssetsForTransfer: usize = 2; + // pub UniversalLocation: InteriorMultiLocation = X1(Parachain(2001)); +} + +impl orml_xtokens::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type CurrencyId = CurrencyId; + type CurrencyIdConvert = (); + type AccountIdToMultiLocation = (); + type UniversalLocation = UniversalLocation; + type SelfLocation = SelfRelativeLocation; + type XcmExecutor = XcmExecutor; + type Weigher = FixedWeightBounds; + type BaseXcmWeight = (); + type MaxAssetsForTransfer = MaxAssetsForTransfer; + type MinXcmFee = ParachainMinFee; + type MultiLocationsFilter = Everything; + type ReserveProvider = RelativeReserveProvider; +} + +parameter_types! { + pub const ExistentialDeposit: Balance = 1; +} + +impl pallet_balances::Config for Test { + type AccountStore = frame_system::Pallet; + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ExistentialDeposit; + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; + type RuntimeHoldReason = (); +} + +ord_parameter_types! { + pub const One: u128 = 1; +} +impl bifrost_asset_registry::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type RegisterOrigin = EnsureSignedBy; + type WeightInfo = (); +} + +pub struct EnsurePoolAssetId; +impl bifrost_stable_asset::traits::ValidateAssetId for EnsurePoolAssetId { + fn validate(_: CurrencyId) -> bool { + true + } +} +parameter_types! { + pub const StableAssetPalletId: PalletId = PalletId(*b"nuts/sta"); +} + +impl bifrost_stable_asset::Config for Test { + type RuntimeEvent = RuntimeEvent; + type AssetId = CurrencyId; + type Balance = Balance; + type Assets = Currencies; + type PalletId = StableAssetPalletId; + type AtLeast64BitUnsigned = u128; + type FeePrecision = ConstU128<10_000_000_000>; + type APrecision = ConstU128<100>; + type PoolAssetLimit = ConstU32<5>; + type SwapExactOverAmount = ConstU128<100>; + type WeightInfo = (); + type ListingOrigin = EnsureSignedBy; + type EnsurePoolAssetId = EnsurePoolAssetId; +} + +impl bifrost_stable_pool::Config for Test { + type WeightInfo = (); + type ControlOrigin = EnsureRoot; + type CurrencyId = CurrencyId; + type MultiCurrency = Currencies; + type StableAsset = StableAsset; + type VtokenMinting = VtokenMinting; + type CurrencyIdConversion = AssetIdMaps; + type CurrencyIdRegister = AssetIdMaps; +} + +impl leverage_staking::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type ControlOrigin = EnsureRoot; + type VtokenMinting = VtokenMinting; + type LendMarket = LendMarket; + type StablePoolHandler = StablePool; + type CurrencyIdConversion = AssetIdMaps; +} + +parameter_types! { + pub const MaximumUnlockIdOfUser: u32 = 1_000; + pub const MaximumUnlockIdOfTimeUnit: u32 = 1_000; + pub BifrostEntranceAccount: PalletId = PalletId(*b"bf/vtkin"); + pub BifrostExitAccount: PalletId = PalletId(*b"bf/vtout"); + // pub BifrostFeeAccount: AccountId = 1.into(); +} + +pub struct SlpxInterface; +impl SlpxOperator for SlpxInterface { + fn get_moonbeam_transfer_to_fee() -> Balance { + Default::default() + } +} + +ord_parameter_types! { + pub const RelayCurrencyId: CurrencyId = CurrencyId::Token(TokenSymbol::KSM); +} + +impl bifrost_vtoken_minting::Config for Test { + type RuntimeEvent = RuntimeEvent; + type MultiCurrency = Tokens; + type ControlOrigin = EnsureSignedBy; + type MaximumUnlockIdOfUser = MaximumUnlockIdOfUser; + type MaximumUnlockIdOfTimeUnit = MaximumUnlockIdOfTimeUnit; + type EntranceAccount = BifrostEntranceAccount; + type ExitAccount = BifrostExitAccount; + type FeeAccount = One; + type BifrostSlp = Slp; + type RelayChainToken = RelayCurrencyId; + type CurrencyIdConversion = AssetIdMaps; + type CurrencyIdRegister = AssetIdMaps; + type WeightInfo = (); + type OnRedeemSuccess = (); + type XcmTransfer = XTokens; + type AstarParachainId = ConstU32<2007>; + type MoonbeamParachainId = ConstU32<2023>; + type BifrostSlpx = SlpxInterface; + type HydradxParachainId = ConstU32<2034>; + type InterlayParachainId = ConstU32<2032>; +} + +pub struct Slp; +// Functions to be called by other pallets. +impl SlpOperator for Slp { + fn all_delegation_requests_occupied(_currency_id: CurrencyId) -> bool { + true + } +} + +parameter_types! { + pub const MinimumPeriod: u64 = 5; +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +pub struct MockPriceFeeder; +#[derive(Encode, Decode, Clone, Copy, RuntimeDebug)] +pub struct CurrencyIdWrap(CurrencyId); + +impl Hash for CurrencyIdWrap { + fn hash(&self, state: &mut H) { + state.write_u8(1); + } +} + +impl PartialEq for CurrencyIdWrap { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for CurrencyIdWrap {} + +// pallet-price is using for benchmark compilation +pub type TimeStampedPrice = orml_oracle::TimestampedValue; +pub struct MockDataProvider; +impl DataProvider for MockDataProvider { + fn get(_asset_id: &CurrencyId) -> Option { + Some(TimeStampedPrice { value: Price::saturating_from_integer(100), timestamp: 0 }) + } +} + +impl DataProviderExtended for MockDataProvider { + fn get_no_op(_key: &CurrencyId) -> Option { + None + } + + fn get_all_values() -> Vec<(CurrencyId, Option)> { + vec![] + } +} + +impl DataFeeder for MockDataProvider { + fn feed_value( + _: Option, + _: CurrencyId, + _: TimeStampedPrice, + ) -> sp_runtime::DispatchResult { + Ok(()) + } +} + +impl MockPriceFeeder { + thread_local! { + pub static PRICES: RefCell>> = { + RefCell::new( + vec![BNC, DOT, KSM, DOT_U, VKSM, VDOT] + .iter() + .map(|&x| (CurrencyIdWrap(x), Some((Price::saturating_from_integer(1), 1)))) + .collect() + ) + }; + } + + pub fn set_price(asset_id: CurrencyId, price: Price) { + Self::PRICES.with(|prices| { + prices.borrow_mut().insert(CurrencyIdWrap(asset_id), Some((price, 1u64))); + }); + } + + pub fn reset() { + Self::PRICES.with(|prices| { + for (_, val) in prices.borrow_mut().iter_mut() { + *val = Some((Price::saturating_from_integer(1), 1u64)); + } + }) + } +} + +impl PriceFeeder for MockPriceFeeder { + fn get_price(asset_id: &CurrencyId) -> Option { + Self::PRICES.with(|prices| *prices.borrow().get(&CurrencyIdWrap(*asset_id)).unwrap()) + } +} + +parameter_types! { + pub const LendMarketPalletId: PalletId = PalletId(*b"bf/ldmkt"); + pub const RewardAssetId: CurrencyId = BNC; + pub const LiquidationFreeAssetId: CurrencyId = DOT; +} + +impl lend_market::Config for Test { + type RuntimeEvent = RuntimeEvent; + type PriceFeeder = MockPriceFeeder; + type PalletId = LendMarketPalletId; + type ReserveOrigin = EnsureRoot; + type UpdateOrigin = EnsureRoot; + type WeightInfo = (); + type UnixTime = TimestampPallet; + type Assets = Currencies; + type RewardAssetId = RewardAssetId; + type LiquidationFreeAssetId = LiquidationFreeAssetId; +} + +impl pallet_prices::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Source = MockDataProvider; + type FeederOrigin = EnsureRoot; + type UpdateOrigin = EnsureRoot; + type RelayCurrency = RelayCurrencyId; + type Assets = Currencies; + type CurrencyIdConvert = AssetIdMaps; + type WeightInfo = (); +} + +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + +impl pallet_xcm::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type UniversalLocation = UniversalLocation; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type Weigher = FixedWeightBounds; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmReserveTransferFilter = Everything; + type XcmRouter = (); + type XcmTeleportFilter = Nothing; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = ConstU32<2>; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = (); + type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; // TODO: config after polkadot impl WeightInfo for () + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; + type AdminOrigin = EnsureSignedBy; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); +} + +pub struct ExtBuilder { + endowed_accounts: Vec<(u128, CurrencyId, Balance)>, +} + +impl Default for ExtBuilder { + fn default() -> Self { + Self { endowed_accounts: vec![] } + } +} + +impl ExtBuilder { + pub fn balances(mut self, endowed_accounts: Vec<(u128, CurrencyId, Balance)>) -> Self { + self.endowed_accounts = endowed_accounts; + self + } + + pub fn new_test_ext(self) -> Self { + self.balances(vec![ + (0, DOT, unit(1_000_000_000_000)), + (1, BNC, unit(1)), + (1, DOT, unit(1000)), + (3, DOT, unit(1000)), + ]) + } + + // Build genesis storage according to the mock runtime. + pub fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap().into(); + + bifrost_asset_registry::GenesisConfig:: { + currency: vec![ + (CurrencyId::Token(TokenSymbol::KSM), 10_000_000, None), + (CurrencyId::Native(TokenSymbol::BNC), 10_000_000, None), + (DOT, 1_000_000, None), + (ASTR, 10_000_000, None), + (GLMR, 10_000_000, None), + ], + vcurrency: vec![VDOT], + vsbond: vec![], + phantom: Default::default(), + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: self + .endowed_accounts + .clone() + .into_iter() + .filter(|(_, currency_id, _)| *currency_id == BNC) + .map(|(account_id, _, initial_balance)| (account_id, initial_balance)) + .collect::>(), + } + .assimilate_storage(&mut t) + .unwrap(); + + orml_tokens::GenesisConfig:: { + balances: self + .endowed_accounts + .clone() + .into_iter() + .filter(|(_, currency_id, _)| *currency_id != BNC) + .collect::>(), + } + .assimilate_storage(&mut t) + .unwrap(); + + t.into() + } +} + +pub fn unit(d: u128) -> u128 { + d.saturating_mul(10_u128.pow(12)) +} + +pub const fn market_mock(lend_token_id: CurrencyId) -> Market { + Market { + close_factor: Ratio::from_percent(50), + collateral_factor: Ratio::from_percent(50), + liquidation_threshold: Ratio::from_percent(55), + liquidate_incentive: Rate::from_inner(Rate::DIV / 100 * 110), + liquidate_incentive_reserved_factor: Ratio::from_percent(3), + state: MarketState::Pending, + rate_model: InterestRateModel::Jump(JumpModel { + base_rate: Rate::from_inner(Rate::DIV / 100 * 2), + jump_rate: Rate::from_inner(Rate::DIV / 100 * 10), + full_rate: Rate::from_inner(Rate::DIV / 100 * 32), + jump_utilization: Ratio::from_percent(80), + }), + reserve_factor: Ratio::from_percent(15), + supply_cap: 1_000_000_000_000_000_000_000u128, // set to 1B + borrow_cap: 1_000_000_000_000_000_000_000u128, // set to 1B + lend_token_id, + } +} diff --git a/pallets/leverage-staking/src/tests.rs b/pallets/leverage-staking/src/tests.rs new file mode 100644 index 000000000..e500fe86c --- /dev/null +++ b/pallets/leverage-staking/src/tests.rs @@ -0,0 +1,153 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::mock::*; +use frame_support::{assert_noop, assert_ok, BoundedVec}; +use lend_market::{AccountBorrows, BorrowSnapshot}; + +fn init() { + assert_ok!(LendMarket::add_market(RuntimeOrigin::root(), DOT, market_mock(VKSM))); + assert_ok!(LendMarket::activate_market(RuntimeOrigin::root(), DOT)); + assert_ok!(LendMarket::add_market(RuntimeOrigin::root(), VDOT, market_mock(VBNC))); + assert_ok!(LendMarket::activate_market(RuntimeOrigin::root(), VDOT)); + TimestampPallet::set_timestamp(6000); + + assert_ok!(StablePool::create_pool( + RuntimeOrigin::root(), + vec![DOT, VDOT], + vec![1u128, 1u128], + 10000000u128, + 20000000u128, + 50000000u128, + 10000u128, + 2, + 1, + unit(1), + )); + assert_ok!(StablePool::edit_token_rate( + RuntimeOrigin::root(), + 0, + vec![(DOT, (1, 1)), (VDOT, (90_000_000, 100_000_000))] + )); + assert_ok!(VtokenMinting::set_minimum_mint(RuntimeOrigin::signed(1), DOT, 0)); + assert_ok!(VtokenMinting::mint(Some(0).into(), DOT, unit(100), BoundedVec::default())); + let amounts = vec![unit(100), unit(100)]; + assert_ok!(StablePool::add_liquidity(RuntimeOrigin::signed(0), 0, amounts, 0)); +} + +#[test] +fn flash_loan_deposit() { + ExtBuilder::default().new_test_ext().build().execute_with(|| { + init(); + assert_ok!(LendMarket::mint(RuntimeOrigin::signed(3), DOT, unit(1000))); + assert_noop!( + LeverageStaking::flash_loan_deposit( + RuntimeOrigin::signed(3), + DOT, + FixedU128::from_inner(unit(1_000_000)), + Some(100_000) + ), + Error::::InsufficientBalance + ); + assert_ok!(LendMarket::mint(RuntimeOrigin::signed(1), DOT, unit(100))); + assert_noop!( + LeverageStaking::flash_loan_deposit( + RuntimeOrigin::signed(1), + DOT, + FixedU128::from_inner(unit(1_000_000)), + Some(100_000) + ), + lend_market::Error::::InvalidAmount + ); + assert_ok!(LeverageStaking::flash_loan_deposit( + RuntimeOrigin::signed(1), + DOT, + FixedU128::from_inner(unit(800_000)), + Some(100_000) + )); + assert_ok!(LeverageStaking::flash_loan_deposit( + RuntimeOrigin::signed(1), + DOT, + FixedU128::from_inner(unit(900_000)), + Some(100_000) + )); + assert_eq!( + AccountBorrows::::get(DOT, 1), + BorrowSnapshot { principal: 90_000, borrow_index: 1.into() }, + ); + assert_eq!( + AccountFlashLoans::::get(DOT, 1).unwrap(), + AccountFlashLoanInfo { + amount: 100_000, + vtoken_amount: 190_000, + leverage_rate: FixedU128::from_inner(unit(900_000)), + collateral_factor: Permill::from_percent(50) + }, + ); + }); +} + +#[test] +fn flash_loan_repay() { + ExtBuilder::default().new_test_ext().build().execute_with(|| { + init(); + assert_ok!(VtokenMinting::set_minimum_mint(RuntimeOrigin::signed(1), DOT, 0)); + assert_ok!(VtokenMinting::mint(Some(3).into(), DOT, 100_000_000, BoundedVec::default())); + assert_ok!(LendMarket::mint(RuntimeOrigin::signed(1), DOT, unit(100))); + assert_ok!(LeverageStaking::flash_loan_deposit( + RuntimeOrigin::signed(1), + DOT, + FixedU128::from_inner(unit(900_000)), + Some(100_000) + )); + assert_eq!( + AccountBorrows::::get(DOT, 1), + BorrowSnapshot { principal: 90_000, borrow_index: 1.into() }, + ); + assert_ok!(LeverageStaking::flash_loan_deposit( + RuntimeOrigin::signed(1), + DOT, + FixedU128::from_inner(unit(100_000)), + None + )); + assert_eq!( + AccountBorrows::::get(DOT, 1), + BorrowSnapshot { principal: 10_000, borrow_index: 1.into() }, + ); + assert_eq!( + AccountFlashLoans::::get(DOT, 1).unwrap(), + AccountFlashLoanInfo { + amount: 100_000, + vtoken_amount: 110_000, + leverage_rate: FixedU128::from_inner(unit(100_000)), + collateral_factor: Permill::from_percent(50) + }, + ); + assert_ok!(LeverageStaking::flash_loan_deposit( + RuntimeOrigin::signed(1), + DOT, + FixedU128::from_inner(0), + Some(100_000) + )); + assert_eq!( + AccountBorrows::::get(DOT, 1), + BorrowSnapshot { principal: 0, borrow_index: 1.into() }, + ); + assert_eq!(AccountFlashLoans::::get(DOT, 1), None); + }); +} diff --git a/pallets/leverage-staking/src/weights.rs b/pallets/leverage-staking/src/weights.rs new file mode 100644 index 000000000..b37293727 --- /dev/null +++ b/pallets/leverage-staking/src/weights.rs @@ -0,0 +1,191 @@ +// This file is part of Bifrost. + +// Copyright (C) 2019-2022 Liebi Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Autogenerated weights for `leverage_staking` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-10-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `yml`, CPU: `AMD Ryzen 9 3950X 16-Core Processor` +//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("bifrost-kusama-local"), DB CACHE: 1024 + +// Executed Command: +// target/release/bifrost +// benchmark +// pallet +// --chain=bifrost-kusama-local +// --pallet=leverage-staking +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --wasm-execution=compiled +// --heap-pages=4096 +// --header=./HEADER-GPL3 +// --output=./bifrost_leverage_staking.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use core::marker::PhantomData; +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; + +pub trait WeightInfo { + fn flash_loan_deposit() -> Weight; +} + +/// Weight functions for `leverage_staking`. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: LeverageStaking AccountFlashLoans (r:1 w:1) + /// Proof Skipped: LeverageStaking AccountFlashLoans (max_values: None, max_size: None, mode: Measured) + /// Storage: Tokens Accounts (r:5 w:5) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: LendMarket Markets (r:3 w:0) + /// Proof Skipped: LendMarket Markets (max_values: None, max_size: None, mode: Measured) + /// Storage: VtokenMinting MinimumMint (r:1 w:0) + /// Proof: VtokenMinting MinimumMint (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: VtokenMinting TokenPool (r:1 w:1) + /// Proof: VtokenMinting TokenPool (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: Tokens TotalIssuance (r:1 w:1) + /// Proof: Tokens TotalIssuance (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: VtokenMinting Fees (r:1 w:0) + /// Proof: VtokenMinting Fees (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: AssetRegistry CurrencyMetadatas (r:2 w:0) + /// Proof Skipped: AssetRegistry CurrencyMetadatas (max_values: None, max_size: None, mode: Measured) + /// Storage: System Account (r:2 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: LendMarket LastAccruedInterestTime (r:2 w:2) + /// Proof Skipped: LendMarket LastAccruedInterestTime (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket RewardSupplyState (r:1 w:1) + /// Proof Skipped: LendMarket RewardSupplyState (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket RewardSupplySpeed (r:1 w:0) + /// Proof Skipped: LendMarket RewardSupplySpeed (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket RewardSupplierIndex (r:1 w:1) + /// Proof Skipped: LendMarket RewardSupplierIndex (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket RewardAccured (r:1 w:1) + /// Proof Skipped: LendMarket RewardAccured (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket AccountDeposits (r:2 w:1) + /// Proof Skipped: LendMarket AccountDeposits (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket TotalSupply (r:1 w:1) + /// Proof Skipped: LendMarket TotalSupply (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket TotalBorrows (r:2 w:1) + /// Proof Skipped: LendMarket TotalBorrows (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket TotalReserves (r:2 w:0) + /// Proof Skipped: LendMarket TotalReserves (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket AccountEarned (r:1 w:1) + /// Proof Skipped: LendMarket AccountEarned (max_values: None, max_size: None, mode: Measured) + /// Storage: Prices EmergencyPrice (r:2 w:0) + /// Proof Skipped: Prices EmergencyPrice (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket AccountBorrows (r:2 w:1) + /// Proof Skipped: LendMarket AccountBorrows (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket LiquidationFreeCollaterals (r:1 w:0) + /// Proof Skipped: LendMarket LiquidationFreeCollaterals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: LendMarket RewardBorrowState (r:1 w:1) + /// Proof Skipped: LendMarket RewardBorrowState (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket RewardBorrowSpeed (r:1 w:0) + /// Proof Skipped: LendMarket RewardBorrowSpeed (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket RewardBorrowerIndex (r:1 w:1) + /// Proof Skipped: LendMarket RewardBorrowerIndex (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket BorrowIndex (r:1 w:0) + /// Proof Skipped: LendMarket BorrowIndex (max_values: None, max_size: None, mode: Measured) + fn flash_loan_deposit() -> Weight { + // Proof Size summary in bytes: + // Measured: `3847` + // Estimated: `13955` + // Minimum execution time: 265_916_000 picoseconds. + Weight::from_parts(267_741_000, 0) + .saturating_add(Weight::from_parts(0, 13955)) + .saturating_add(T::DbWeight::get().reads(41)) + .saturating_add(T::DbWeight::get().writes(21)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: LeverageStaking AccountFlashLoans (r:1 w:1) + /// Proof Skipped: LeverageStaking AccountFlashLoans (max_values: None, max_size: None, mode: Measured) + /// Storage: Tokens Accounts (r:5 w:5) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: LendMarket Markets (r:3 w:0) + /// Proof Skipped: LendMarket Markets (max_values: None, max_size: None, mode: Measured) + /// Storage: VtokenMinting MinimumMint (r:1 w:0) + /// Proof: VtokenMinting MinimumMint (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: VtokenMinting TokenPool (r:1 w:1) + /// Proof: VtokenMinting TokenPool (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: Tokens TotalIssuance (r:1 w:1) + /// Proof: Tokens TotalIssuance (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) + /// Storage: VtokenMinting Fees (r:1 w:0) + /// Proof: VtokenMinting Fees (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: AssetRegistry CurrencyMetadatas (r:2 w:0) + /// Proof Skipped: AssetRegistry CurrencyMetadatas (max_values: None, max_size: None, mode: Measured) + /// Storage: System Account (r:2 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: LendMarket LastAccruedInterestTime (r:2 w:2) + /// Proof Skipped: LendMarket LastAccruedInterestTime (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket RewardSupplyState (r:1 w:1) + /// Proof Skipped: LendMarket RewardSupplyState (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket RewardSupplySpeed (r:1 w:0) + /// Proof Skipped: LendMarket RewardSupplySpeed (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket RewardSupplierIndex (r:1 w:1) + /// Proof Skipped: LendMarket RewardSupplierIndex (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket RewardAccured (r:1 w:1) + /// Proof Skipped: LendMarket RewardAccured (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket AccountDeposits (r:2 w:1) + /// Proof Skipped: LendMarket AccountDeposits (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket TotalSupply (r:1 w:1) + /// Proof Skipped: LendMarket TotalSupply (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket TotalBorrows (r:2 w:1) + /// Proof Skipped: LendMarket TotalBorrows (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket TotalReserves (r:2 w:0) + /// Proof Skipped: LendMarket TotalReserves (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket AccountEarned (r:1 w:1) + /// Proof Skipped: LendMarket AccountEarned (max_values: None, max_size: None, mode: Measured) + /// Storage: Prices EmergencyPrice (r:2 w:0) + /// Proof Skipped: Prices EmergencyPrice (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket AccountBorrows (r:2 w:1) + /// Proof Skipped: LendMarket AccountBorrows (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket LiquidationFreeCollaterals (r:1 w:0) + /// Proof Skipped: LendMarket LiquidationFreeCollaterals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: LendMarket RewardBorrowState (r:1 w:1) + /// Proof Skipped: LendMarket RewardBorrowState (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket RewardBorrowSpeed (r:1 w:0) + /// Proof Skipped: LendMarket RewardBorrowSpeed (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket RewardBorrowerIndex (r:1 w:1) + /// Proof Skipped: LendMarket RewardBorrowerIndex (max_values: None, max_size: None, mode: Measured) + /// Storage: LendMarket BorrowIndex (r:1 w:0) + /// Proof Skipped: LendMarket BorrowIndex (max_values: None, max_size: None, mode: Measured) + fn flash_loan_deposit() -> Weight { + // Proof Size summary in bytes: + // Measured: `3847` + // Estimated: `13955` + // Minimum execution time: 265_916_000 picoseconds. + Weight::from_parts(267_741_000, 0) + .saturating_add(Weight::from_parts(0, 13955)) + .saturating_add(RocksDbWeight::get().reads(41)) + .saturating_add(RocksDbWeight::get().writes(21)) + } +} \ No newline at end of file diff --git a/pallets/prices/Cargo.toml b/pallets/prices/Cargo.toml index 42d937833..d40578310 100644 --- a/pallets/prices/Cargo.toml +++ b/pallets/prices/Cargo.toml @@ -5,37 +5,35 @@ name = 'pallet-prices' version = "0.8.0" [dependencies] -log = { version = "0.4.17", default-features = false } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -num-traits = { version = '0.2.15', default-features = false } -orml-oracle = { version = "0.4.1-dev", default-features = false } -orml-traits = { version = "0.4.1-dev", default-features = false } -xcm = { package = "staging-xcm", git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -pallet-assets = { git = 'https://github.com/paritytech/polkadot-sdk.git', branch = 'release-polkadot-v1.1.0', default-features = false } -pallet-traits = { path = '../traits', default-features = false } -bifrost-primitives = { path = "../../primitives", default-features = false } -scale-info = { version = "2.9.0", default-features = false, features = [ - "derive", -] } -serde = { version = "1.0.160", optional = true, features = ["derive"] } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -bifrost-asset-registry = { path = "../asset-registry", default-features = false } +log = { workspace = true } +parity-scale-codec = { workspace = true, features = ["derive"] } +frame-support = { workspace = true } +frame-system = { workspace = true } +num-traits = { workspace = true } +orml-oracle = { workspace = true } +orml-traits = { workspace = true } +xcm = { workspace = true } +pallet-assets = { workspace = true } +pallet-traits = { workspace = true } +bifrost-primitives = { workspace = true } +scale-info = { workspace = true, features = ["derive"] } +serde = { workspace = true, optional = true, features = ["derive"] } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } +bifrost-asset-registry = { workspace = true } [dev-dependencies] -bifrost-currencies = { path = "../currencies", default-features = false } -pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -orml-tokens = { version = "0.4.1-dev", default-features = false } +bifrost-currencies = { workspace = true } +pallet-balances = { workspace = true } +sp-io = { workspace = true } +orml-tokens = { workspace = true } [features] default = ['std'] std = [ 'serde', - 'codec/std', + 'parity-scale-codec/std', 'sp-runtime/std', 'frame-support/std', 'frame-system/std', diff --git a/pallets/slp/src/agents/parachain_staking_agent/types/manta_types.rs b/pallets/slp/src/agents/parachain_staking_agent/types/manta_types.rs index 9482613fa..651097e3e 100644 --- a/pallets/slp/src/agents/parachain_staking_agent/types/manta_types.rs +++ b/pallets/slp/src/agents/parachain_staking_agent/types/manta_types.rs @@ -1,6 +1,6 @@ // This file is part of Bifrost. -// Copyright (C) 2019-2022 Liebi Technologies (UK) Ltd. +// Copyright (C) Liebi Technologies PTE. LTD. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/pallets/slp/src/migrations/v3.rs b/pallets/slp/src/migrations/v3.rs index dfc1fece8..6ded7281b 100644 --- a/pallets/slp/src/migrations/v3.rs +++ b/pallets/slp/src/migrations/v3.rs @@ -1,6 +1,6 @@ // This file is part of Bifrost. -// Copyright (C) 2019-2022 Liebi Technologies (UK) Ltd. +// Copyright (C) Liebi Technologies PTE. LTD. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/pallets/slpx/src/lib.rs b/pallets/slpx/src/lib.rs index 2ee21033b..9cf27983a 100644 --- a/pallets/slpx/src/lib.rs +++ b/pallets/slpx/src/lib.rs @@ -131,6 +131,7 @@ pub mod pallet { type StablePoolHandler: StablePoolHandler< Balance = BalanceOf, AccountId = AccountIdOf, + CurrencyId = CurrencyIdOf, >; /// xtokens xcm transfer interface diff --git a/pallets/stable-pool/src/lib.rs b/pallets/stable-pool/src/lib.rs index 0075b105c..6691be609 100644 --- a/pallets/stable-pool/src/lib.rs +++ b/pallets/stable-pool/src/lib.rs @@ -36,7 +36,7 @@ use bifrost_primitives::{ }; pub use bifrost_stable_asset::{ MintResult, PoolCount, PoolTokenIndex, Pools, RedeemMultiResult, RedeemProportionResult, - RedeemSingleResult, StableAsset, StableAssetPoolId, SwapResult, + RedeemSingleResult, StableAsset, StableAssetPoolId, StableAssetPoolInfo, SwapResult, }; use frame_support::{self, pallet_prelude::*, sp_runtime::traits::Zero, transactional}; use frame_system::pallet_prelude::*; @@ -814,6 +814,42 @@ impl Pallet { Ok(downscale_out) } + pub fn get_swap_input( + pool_id: StableAssetPoolId, + currency_id_in: PoolTokenIndex, + currency_id_out: PoolTokenIndex, + amount: T::Balance, + ) -> Result { + let pool_info = + T::StableAsset::pool(pool_id).ok_or(bifrost_stable_asset::Error::::PoolNotFound)?; + let dy = Self::upscale( + amount, + pool_id, + *pool_info + .assets + .get(currency_id_out as usize) + .ok_or(bifrost_stable_asset::Error::::ArgumentsMismatch)?, + )?; + let SwapResult { dx, dy: _, .. } = + bifrost_stable_asset::Pallet::::get_swap_amount_exact( + &pool_info, + currency_id_in, + currency_id_out, + dy, + ) + .ok_or(bifrost_stable_asset::Error::::Math)?; + let downscale_out = Self::downscale( + dx, + pool_id, + *pool_info + .assets + .get(currency_id_in as usize) + .ok_or(bifrost_stable_asset::Error::::ArgumentsMismatch)?, + )?; + + Ok(downscale_out) + } + pub fn add_liquidity_amount( pool_id: StableAssetPoolId, mut amounts: Vec, @@ -836,4 +872,23 @@ impl Pallet { Ok(mint_amount) } + + fn get_pool_id( + currency_id_in: &AssetIdOf, + currency_id_out: &AssetIdOf, + ) -> Option<(StableAssetPoolId, PoolTokenIndex, PoolTokenIndex)> { + Pools::::iter().find_map(|(pool_id, pool_info)| { + if pool_info.assets.get(0) == Some(currency_id_in) && + pool_info.assets.get(1) == Some(currency_id_out) + { + Some((pool_id, 0, 1)) + } else if pool_info.assets.get(0) == Some(currency_id_out) && + pool_info.assets.get(1) == Some(currency_id_in) + { + Some((pool_id, 1, 0)) + } else { + None + } + }) + } } diff --git a/pallets/stable-pool/src/traits.rs b/pallets/stable-pool/src/traits.rs index 25d1d537f..5b69979ba 100644 --- a/pallets/stable-pool/src/traits.rs +++ b/pallets/stable-pool/src/traits.rs @@ -22,6 +22,7 @@ use crate::*; pub trait StablePoolHandler { type Balance; type AccountId; + type CurrencyId; fn add_liquidity( who: Self::AccountId, @@ -66,11 +67,31 @@ pub trait StablePoolHandler { pool_id: StableAssetPoolId, currency_id: CurrencyId, ) -> Option; + + fn get_swap_output( + pool_id: StableAssetPoolId, + currency_id_in: PoolTokenIndex, + currency_id_out: PoolTokenIndex, + amount: Self::Balance, + ) -> Result; + + fn get_swap_input( + pool_id: StableAssetPoolId, + currency_id_in: PoolTokenIndex, + currency_id_out: PoolTokenIndex, + amount: Self::Balance, + ) -> Result; + + fn get_pool_id( + currency_id_in: &Self::CurrencyId, + currency_id_out: &Self::CurrencyId, + ) -> Option<(StableAssetPoolId, PoolTokenIndex, PoolTokenIndex)>; } impl StablePoolHandler for Pallet { type Balance = T::Balance; type AccountId = T::AccountId; + type CurrencyId = T::CurrencyId; fn add_liquidity( who: Self::AccountId, @@ -132,11 +153,37 @@ impl StablePoolHandler for Pallet { .position(|&x| x == currency_id.into()) .map(|value| value as u32) } + + fn get_swap_output( + pool_id: StableAssetPoolId, + currency_id_in: PoolTokenIndex, + currency_id_out: PoolTokenIndex, + amount: Self::Balance, + ) -> Result { + Self::get_swap_output(pool_id, currency_id_in, currency_id_out, amount) + } + + fn get_swap_input( + pool_id: StableAssetPoolId, + currency_id_in: PoolTokenIndex, + currency_id_out: PoolTokenIndex, + amount: Self::Balance, + ) -> Result { + Self::get_swap_input(pool_id, currency_id_in, currency_id_out, amount) + } + + fn get_pool_id( + currency_id_in: &Self::CurrencyId, + currency_id_out: &Self::CurrencyId, + ) -> Option<(StableAssetPoolId, PoolTokenIndex, PoolTokenIndex)> { + Self::get_pool_id(currency_id_in, currency_id_out) + } } impl StablePoolHandler for () { type Balance = u128; type AccountId = sp_runtime::AccountId32; + type CurrencyId = CurrencyId; fn add_liquidity( _who: Self::AccountId, @@ -193,4 +240,29 @@ impl StablePoolHandler for () { ) -> Option { None } + + fn get_swap_output( + _pool_id: StableAssetPoolId, + _currency_id_in: PoolTokenIndex, + _currency_id_out: PoolTokenIndex, + _amount: Self::Balance, + ) -> Result { + Ok(0) + } + + fn get_swap_input( + _pool_id: StableAssetPoolId, + _currency_id_in: PoolTokenIndex, + _currency_id_out: PoolTokenIndex, + _amount: Self::Balance, + ) -> Result { + Ok(0) + } + + fn get_pool_id( + _currency_id_in: &Self::CurrencyId, + _currency_id_out: &Self::CurrencyId, + ) -> Option<(StableAssetPoolId, PoolTokenIndex, PoolTokenIndex)> { + None + } } diff --git a/pallets/traits/Cargo.toml b/pallets/traits/Cargo.toml index 32ca11f51..517f3b127 100644 --- a/pallets/traits/Cargo.toml +++ b/pallets/traits/Cargo.toml @@ -8,38 +8,32 @@ version = "0.8.0" targets = ['x86_64-unknown-linux-gnu'] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ - "derive", -] } -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -log = { version = "0.4.17", default-features = false } -num-bigint = { version = '0.4.3', default-features = false } -num-traits = { version = '0.2.15', default-features = false } -impl-trait-for-tuples = "0.2.2" -bifrost-primitives = { path = "../../primitives", default-features = false } -scale-info = { version = "2.9.0", default-features = false, features = [ - "derive", -] } -serde = { version = "1.0.160", features = ['derive'], optional = true } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -xcm = { package = "staging-xcm", git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.1.0" } -xcm-builder = { package = "staging-xcm-builder", git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.1.0" } -xcm-executor = { package = "staging-xcm-executor", git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.1.0" } +parity-scale-codec = { workspace = true, features = ["derive"] } +frame-support = { workspace = true } +frame-system = { workspace = true } +log = { workspace = true } +num-bigint = { workspace = true } +num-traits = { workspace = true } +impl-trait-for-tuples = { workspace = true } +bifrost-primitives = { workspace = true } +scale-info = { workspace = true, features = ["derive"] } +serde = { workspace = true, features = ['derive'], optional = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } +xcm = { workspace = true } +xcm-builder = { workspace = true } +xcm-executor = { workspace = true } [dev-dependencies] -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false, features = [ - 'std', -] } +sp-core = { workspace = true } [features] default = ['std'] std = [ 'serde', - 'codec/std', + 'parity-scale-codec/std', 'frame-support/std', 'frame-system/std', 'sp-runtime/std', diff --git a/pallets/traits/src/lend_market.rs b/pallets/traits/src/lend_market.rs index dc7b9d459..88fab107d 100644 --- a/pallets/traits/src/lend_market.rs +++ b/pallets/traits/src/lend_market.rs @@ -13,7 +13,7 @@ // limitations under the License. use bifrost_primitives::{Rate, Ratio}; -use codec::{Decode, Encode}; +use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_runtime::{DispatchError, FixedU128, RuntimeDebug}; use sp_std::prelude::*; diff --git a/pallets/vtoken-minting/src/lib.rs b/pallets/vtoken-minting/src/lib.rs index d426bc50d..f94c2d144 100644 --- a/pallets/vtoken-minting/src/lib.rs +++ b/pallets/vtoken-minting/src/lib.rs @@ -353,10 +353,10 @@ pub mod pallet { token_id: CurrencyIdOf, token_amount: BalanceOf, remark: BoundedVec>, - ) -> DispatchResultWithPostInfo { + ) -> DispatchResult { // Check origin let exchanger = ensure_signed(origin)?; - Self::mint_inner(exchanger, token_id, token_amount, remark) + Self::mint_inner(exchanger, token_id, token_amount, remark).map(|_| ()) } #[pallet::call_index(1)] @@ -1227,7 +1227,7 @@ pub mod pallet { token_id: CurrencyIdOf, token_amount: BalanceOf, remark: BoundedVec>, - ) -> DispatchResultWithPostInfo { + ) -> Result, DispatchError> { ensure!(token_amount >= MinimumMint::::get(token_id), Error::::BelowMinimumMint); let vtoken_id = T::CurrencyIdConversion::convert_to_vtoken(token_id) @@ -1250,7 +1250,7 @@ pub mod pallet { fee, remark, }); - Ok(().into()) + Ok(vtoken_amount.into()) } #[transactional] @@ -1619,7 +1619,7 @@ impl VtokenMintingInterface, CurrencyIdOf, BalanceO token_id: CurrencyIdOf, token_amount: BalanceOf, remark: BoundedVec>, - ) -> DispatchResultWithPostInfo { + ) -> Result, DispatchError> { Self::mint_inner(exchanger, token_id, token_amount, remark) } diff --git a/pallets/vtoken-voting/src/migration.rs b/pallets/vtoken-voting/src/migration.rs index 8c0f6499c..44ff9268b 100644 --- a/pallets/vtoken-voting/src/migration.rs +++ b/pallets/vtoken-voting/src/migration.rs @@ -1,6 +1,6 @@ // This file is part of Bifrost. -// Copyright (C) 2019-2022 Liebi Technologies (UK) Ltd. +// Copyright (C) Liebi Technologies PTE. LTD. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 1e70e1248..c26f14a49 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -17,6 +17,7 @@ sp-std = { workspace = true } xcm = { workspace = true } zenlink-protocol = { workspace = true } +orml-oracle = { workspace = true } [features] default = ["std"] @@ -33,6 +34,7 @@ std = [ "xcm/std", "zenlink-protocol/std", + "orml-oracle/std", ] with-bifrost-runtime = [ diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index be2ebc08c..2fe618d20 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -152,6 +152,8 @@ pub const SECONDS_PER_YEAR: Timestamp = 365 * 24 * 60 * 60; pub type DerivativeIndex = u16; +pub type TimeStampedPrice = orml_oracle::TimestampedValue; + #[derive( Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord, scale_info::TypeInfo, )] diff --git a/primitives/src/traits.rs b/primitives/src/traits.rs index f5c2e0262..bf4a73953 100644 --- a/primitives/src/traits.rs +++ b/primitives/src/traits.rs @@ -318,7 +318,7 @@ pub trait VtokenMintingInterface { token_id: CurrencyId, token_amount: Balance, remark: BoundedVec>, - ) -> DispatchResultWithPostInfo; + ) -> Result; fn redeem( exchanger: AccountId, vtoken_id: CurrencyId, @@ -357,8 +357,8 @@ impl VtokenMintingInterface>, - ) -> DispatchResultWithPostInfo { - Ok(().into()) + ) -> Result { + Ok(Zero::zero()) } fn redeem( diff --git a/runtime/bifrost-kusama/Cargo.toml b/runtime/bifrost-kusama/Cargo.toml index 5fc4dd6e5..2047d6f47 100644 --- a/runtime/bifrost-kusama/Cargo.toml +++ b/runtime/bifrost-kusama/Cargo.toml @@ -91,6 +91,7 @@ orml-unknown-tokens = { workspace = true } orml-xcm = { workspace = true } orml-xcm-support = { workspace = true } orml-xtokens = { workspace = true } +orml-oracle = { workspace = true } # zenlink merkle-distributor = { workspace = true } @@ -131,6 +132,10 @@ bifrost-vtoken-minting = { workspace = true } bifrost-vtoken-voting = { workspace = true, features = [ "kusama" ] } bifrost-xcm-interface = { workspace = true } bifrost-parachain-staking = { workspace = true } +lend-market = { workspace = true } +lend-market-rpc-runtime-api = { workspace = true } +pallet-prices = { workspace = true } +leverage-staking = { workspace = true } [build-dependencies] substrate-wasm-builder = { workspace = true, optional = true } @@ -211,6 +216,7 @@ std = [ "orml-traits/std", "orml-unknown-tokens/std", "orml-xcm-support/std", + "orml-oracle/std", "orml-xcm/std", "orml-xtokens/std", @@ -246,6 +252,10 @@ std = [ "bifrost-vstoken-conversion/std", "bifrost-vtoken-minting/std", "bifrost-vtoken-voting/std", + "lend-market/std", + "lend-market-rpc-runtime-api/std", + "pallet-prices/std", + "leverage-staking/std", "bifrost-stable-asset/std", "bifrost-parachain-staking/std", "bifrost-xcm-interface/std", @@ -296,6 +306,8 @@ runtime-benchmarks = [ "bifrost-slpx/runtime-benchmarks", "bifrost-stable-pool/runtime-benchmarks", "bifrost-vtoken-voting/runtime-benchmarks", + "lend-market/runtime-benchmarks", + "leverage-staking/runtime-benchmarks", ] try-runtime = [ @@ -341,6 +353,7 @@ try-runtime = [ "bifrost-currencies/try-runtime", "orml-unknown-tokens/try-runtime", "orml-xcm/try-runtime", + "orml-oracle/try-runtime", "zenlink-protocol/try-runtime", "merkle-distributor/try-runtime", "zenlink-stable-amm/try-runtime", @@ -363,7 +376,10 @@ try-runtime = [ "bifrost-slpx/try-runtime", "bifrost-stable-asset/try-runtime", "bifrost-stable-pool/try-runtime", + "lend-market/try-runtime", + "pallet-prices/try-runtime", "bifrost-vtoken-voting/try-runtime", + "leverage-staking/try-runtime", ] # When enabled, the runtime API will not be build. diff --git a/runtime/bifrost-kusama/src/lib.rs b/runtime/bifrost-kusama/src/lib.rs index 910435988..8c819d0ab 100644 --- a/runtime/bifrost-kusama/src/lib.rs +++ b/runtime/bifrost-kusama/src/lib.rs @@ -93,6 +93,7 @@ use frame_support::{ }; use frame_system::{EnsureRoot, EnsureSigned, EnsureWithSuccess}; use hex_literal::hex; +use orml_oracle::{DataFeeder, DataProvider, DataProviderExtended}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; // zenlink imports @@ -1671,6 +1672,104 @@ impl bifrost_stable_pool::Config for Runtime { type CurrencyIdRegister = AssetIdMaps; } +parameter_types! { + pub const MinimumCount: u32 = 3; + pub const ExpiresIn: Moment = 1000 * 60 * 60; // 60 mins + pub const MaxHasDispatchedSize: u32 = 100; + pub OracleRootOperatorAccountId: AccountId = OraclePalletId::get().into_account_truncating(); +} + +type BifrostDataProvider = orml_oracle::Instance1; +impl orml_oracle::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnNewData = (); + type CombineData = + orml_oracle::DefaultCombineData; + type Time = Timestamp; + type OracleKey = CurrencyId; + type OracleValue = Price; + type RootOperatorAccountId = OracleRootOperatorAccountId; + type MaxHasDispatchedSize = MaxHasDispatchedSize; + type WeightInfo = weights::orml_oracle::WeightInfo; + type Members = OracleMembership; + type MaxFeedValues = ConstU32<100>; +} + +pub type TimeStampedPrice = orml_oracle::TimestampedValue; +pub struct AggregatedDataProvider; +impl DataProvider for AggregatedDataProvider { + fn get(key: &CurrencyId) -> Option { + Oracle::get(key) + } +} + +impl DataProviderExtended for AggregatedDataProvider { + fn get_no_op(key: &CurrencyId) -> Option { + Oracle::get_no_op(key) + } + + fn get_all_values() -> Vec<(CurrencyId, Option)> { + Oracle::get_all_values() + } +} + +impl DataFeeder for AggregatedDataProvider { + fn feed_value(_: Option, _: CurrencyId, _: TimeStampedPrice) -> DispatchResult { + Err("Not supported".into()) + } +} + +impl pallet_prices::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Source = AggregatedDataProvider; + type FeederOrigin = EitherOfDiverse; + type UpdateOrigin = EitherOfDiverse; + type RelayCurrency = RelayCurrencyId; + type CurrencyIdConvert = AssetIdMaps; + type Assets = Currencies; + type WeightInfo = pallet_prices::weights::SubstrateWeight; +} + +impl lend_market::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type PalletId = LendMarketPalletId; + type PriceFeeder = Prices; + type ReserveOrigin = EitherOfDiverse; + type UpdateOrigin = EitherOfDiverse; + type WeightInfo = lend_market::weights::BifrostWeight; + type UnixTime = Timestamp; + type Assets = Currencies; + type RewardAssetId = NativeCurrencyId; + type LiquidationFreeAssetId = RelayCurrencyId; +} + +parameter_types! { + pub const OracleMaxMembers: u32 = 100; +} + +impl pallet_membership::Config for Runtime { + type AddOrigin = MoreThanHalfCouncil; + type RuntimeEvent = RuntimeEvent; + type MaxMembers = OracleMaxMembers; + type MembershipInitialized = (); + type MembershipChanged = (); + type PrimeOrigin = MoreThanHalfCouncil; + type RemoveOrigin = MoreThanHalfCouncil; + type ResetOrigin = MoreThanHalfCouncil; + type SwapOrigin = MoreThanHalfCouncil; + type WeightInfo = pallet_membership::weights::SubstrateWeight; +} + +impl leverage_staking::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = leverage_staking::weights::SubstrateWeight; + type ControlOrigin = EnsureRoot; + type VtokenMinting = VtokenMinting; + type LendMarket = LendMarket; + type StablePoolHandler = StablePool; + type CurrencyIdConversion = AssetIdMaps; +} + // Below is the implementation of tokens manipulation functions other than native token. pub struct LocalAssetAdaptor(PhantomData); @@ -1852,6 +1951,11 @@ construct_runtime! { StableAsset: bifrost_stable_asset::{Pallet, Storage, Event} = 128, StablePool: bifrost_stable_pool = 129, VtokenVoting: bifrost_vtoken_voting = 130, + LendMarket: lend_market = 131, + Prices: pallet_prices = 132, + Oracle: orml_oracle:: = 133, + OracleMembership: pallet_membership::::{Pallet, Call, Storage, Event, Config} = 134, + LeverageStaking: leverage_staking = 135, } } @@ -1941,6 +2045,8 @@ mod benches { [bifrost_vstoken_conversion, VstokenConversion] [bifrost_vtoken_minting, VtokenMinting] [bifrost_vtoken_voting, VtokenVoting] + [lend_market, LendMarket] + [leverage_staking, LeverageStaking] ); } @@ -2235,6 +2341,20 @@ impl_runtime_apis! { } } + impl lend_market_rpc_runtime_api::LendMarketApi for Runtime { + fn get_account_liquidity(account: AccountId) -> Result<(Liquidity, Shortfall, Liquidity, Shortfall), DispatchError> { + LendMarket::get_account_liquidity(&account) + } + + fn get_market_status(asset_id: CurrencyId) -> Result<(Rate, Rate, Rate, Ratio, Balance, Balance, sp_runtime::FixedU128), DispatchError> { + LendMarket::get_market_status(asset_id) + } + + fn get_liquidation_threshold_liquidity(account: AccountId) -> Result<(Liquidity, Shortfall, Liquidity, Shortfall), DispatchError> { + LendMarket::get_account_liquidation_threshold_liquidity(&account) + } + } + #[cfg(feature = "runtime-benchmarks")] impl frame_benchmarking::Benchmark for Runtime { fn benchmark_metadata(extra: bool) -> ( diff --git a/runtime/bifrost-kusama/src/weights/mod.rs b/runtime/bifrost-kusama/src/weights/mod.rs index 3a6d87883..c760cfe11 100644 --- a/runtime/bifrost-kusama/src/weights/mod.rs +++ b/runtime/bifrost-kusama/src/weights/mod.rs @@ -38,5 +38,6 @@ pub mod bifrost_vsbond_auction; pub mod bifrost_vstoken_conversion; pub mod bifrost_vtoken_minting; pub mod bifrost_vtoken_voting; +pub mod orml_oracle; pub mod orml_tokens; pub mod pallet_xcm; diff --git a/runtime/bifrost-kusama/src/weights/orml_oracle.rs b/runtime/bifrost-kusama/src/weights/orml_oracle.rs new file mode 100644 index 000000000..dffcbeaf8 --- /dev/null +++ b/runtime/bifrost-kusama/src/weights/orml_oracle.rs @@ -0,0 +1,45 @@ +//! Autogenerated weights for orml_oracle +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 +//! DATE: 2021-05-04, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 + +// Executed Command: +// target/release/bifrost +// benchmark +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=orml_oracle +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./oracle/src/weights.rs +// --template +// ../templates/orml-weight-template.hbs + + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(clippy::unnecessary_cast)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +pub struct WeightInfo(PhantomData); +impl orml_oracle::WeightInfo for WeightInfo { + fn feed_values(c: u32, ) -> Weight { + Weight::from_parts(16_800_000, 0) + // Standard Error: 84_000 + .saturating_add(Weight::from_parts(3_600_000, 0).saturating_mul(c as u64)) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + .saturating_add(RocksDbWeight::get().writes((2 as u64).saturating_mul(c as u64))) + } + fn on_finalize() -> Weight { + Weight::from_parts(3_000_000, 0) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } +} \ No newline at end of file diff --git a/runtime/bifrost-polkadot/Cargo.toml b/runtime/bifrost-polkadot/Cargo.toml index f2cba5709..0eab8c459 100644 --- a/runtime/bifrost-polkadot/Cargo.toml +++ b/runtime/bifrost-polkadot/Cargo.toml @@ -91,6 +91,7 @@ orml-xtokens = { workspace = true } orml-unknown-tokens = { workspace = true } orml-xcm = { workspace = true } orml-xcm-support = { workspace = true } +orml-oracle = { workspace = true } # zenlink merkle-distributor = { workspace = true } @@ -127,6 +128,10 @@ bifrost-vstoken-conversion = { workspace = true } bifrost-vtoken-minting = { workspace = true } bifrost-vtoken-voting = { workspace = true, features = [ "polkadot" ] } bifrost-xcm-interface = { workspace = true } +lend-market = { workspace = true } +lend-market-rpc-runtime-api = { workspace = true } +pallet-prices = { workspace = true } +leverage-staking = { workspace = true } [build-dependencies] substrate-wasm-builder = { workspace = true, optional = true } @@ -201,6 +206,7 @@ std = [ "orml-traits/std", "orml-unknown-tokens/std", "orml-xcm-support/std", + "orml-oracle/std", "orml-xcm/std", "orml-xtokens/std", @@ -231,6 +237,10 @@ std = [ "bifrost-vesting/std", "bifrost-vtoken-minting/std", "bifrost-vtoken-voting/std", + "lend-market/std", + "lend-market-rpc-runtime-api/std", + "pallet-prices/std", + "leverage-staking/std", "bifrost-xcm-interface/std", "substrate-wasm-builder" @@ -267,6 +277,7 @@ runtime-benchmarks = [ "bifrost-stable-pool/runtime-benchmarks", "bifrost-vtoken-voting/runtime-benchmarks", "sp-api/disable-logging", + "lend-market/runtime-benchmarks", ] try-runtime = [ @@ -312,6 +323,7 @@ try-runtime = [ "bifrost-currencies/try-runtime", "orml-unknown-tokens/try-runtime", "orml-xcm/try-runtime", + "orml-oracle/try-runtime", "zenlink-protocol/try-runtime", "merkle-distributor/try-runtime", "bifrost-flexible-fee/try-runtime", @@ -332,6 +344,9 @@ try-runtime = [ "bifrost-stable-asset/try-runtime", "bifrost-stable-pool/try-runtime", "bifrost-vtoken-voting/try-runtime", + "lend-market/try-runtime", + "pallet-prices/try-runtime", + "leverage-staking/try-runtime", ] # When enabled, the runtime API will not be build. diff --git a/runtime/bifrost-polkadot/src/lib.rs b/runtime/bifrost-polkadot/src/lib.rs index e914c26e4..9007852f9 100644 --- a/runtime/bifrost-polkadot/src/lib.rs +++ b/runtime/bifrost-polkadot/src/lib.rs @@ -46,6 +46,7 @@ pub use frame_support::{ PalletId, StorageValue, }; use frame_system::limits::{BlockLength, BlockWeights}; +use orml_oracle::{DataFeeder, DataProvider, DataProviderExtended}; pub use pallet_balances::Call as BalancesCall; pub use pallet_timestamp::Call as TimestampCall; use sp_api::impl_runtime_apis; @@ -1477,6 +1478,104 @@ impl bifrost_ve_minting::Config for Runtime { type VoteWeightMultiplier = VoteWeightMultiplier; } +parameter_types! { + pub const MinimumCount: u32 = 3; + pub const ExpiresIn: Moment = 1000 * 60 * 60; // 60 mins + pub const MaxHasDispatchedSize: u32 = 100; + pub OracleRootOperatorAccountId: AccountId = OraclePalletId::get().into_account_truncating(); +} + +type BifrostDataProvider = orml_oracle::Instance1; +impl orml_oracle::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnNewData = (); + type CombineData = + orml_oracle::DefaultCombineData; + type Time = Timestamp; + type OracleKey = CurrencyId; + type OracleValue = Price; + type RootOperatorAccountId = OracleRootOperatorAccountId; + type MaxHasDispatchedSize = MaxHasDispatchedSize; + type WeightInfo = weights::orml_oracle::WeightInfo; + type Members = OracleMembership; + type MaxFeedValues = ConstU32<100>; +} + +pub type TimeStampedPrice = orml_oracle::TimestampedValue; +pub struct AggregatedDataProvider; +impl DataProvider for AggregatedDataProvider { + fn get(key: &CurrencyId) -> Option { + Oracle::get(key) + } +} + +impl DataProviderExtended for AggregatedDataProvider { + fn get_no_op(key: &CurrencyId) -> Option { + Oracle::get_no_op(key) + } + + fn get_all_values() -> Vec<(CurrencyId, Option)> { + Oracle::get_all_values() + } +} + +impl DataFeeder for AggregatedDataProvider { + fn feed_value(_: Option, _: CurrencyId, _: TimeStampedPrice) -> DispatchResult { + Err("Not supported".into()) + } +} + +impl pallet_prices::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Source = AggregatedDataProvider; + type FeederOrigin = EitherOfDiverse; + type UpdateOrigin = EitherOfDiverse; + type RelayCurrency = RelayCurrencyId; + type CurrencyIdConvert = AssetIdMaps; + type Assets = Currencies; + type WeightInfo = pallet_prices::weights::SubstrateWeight; +} + +impl lend_market::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type PalletId = LendMarketPalletId; + type PriceFeeder = Prices; + type ReserveOrigin = EitherOfDiverse; + type UpdateOrigin = EitherOfDiverse; + type WeightInfo = lend_market::weights::BifrostWeight; + type UnixTime = Timestamp; + type Assets = Currencies; + type RewardAssetId = NativeCurrencyId; + type LiquidationFreeAssetId = RelayCurrencyId; +} + +parameter_types! { + pub const OracleMaxMembers: u32 = 100; +} + +impl pallet_membership::Config for Runtime { + type AddOrigin = MoreThanHalfCouncil; + type RuntimeEvent = RuntimeEvent; + type MaxMembers = OracleMaxMembers; + type MembershipInitialized = (); + type MembershipChanged = (); + type PrimeOrigin = MoreThanHalfCouncil; + type RemoveOrigin = MoreThanHalfCouncil; + type ResetOrigin = MoreThanHalfCouncil; + type SwapOrigin = MoreThanHalfCouncil; + type WeightInfo = pallet_membership::weights::SubstrateWeight; +} + +impl leverage_staking::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = leverage_staking::weights::SubstrateWeight; + type ControlOrigin = EnsureRoot; + type VtokenMinting = VtokenMinting; + type LendMarket = LendMarket; + type StablePoolHandler = StablePool; + type CurrencyIdConversion = AssetIdMaps; +} + // Below is the implementation of tokens manipulation functions other than native token. pub struct LocalAssetAdaptor(PhantomData); @@ -1655,6 +1754,11 @@ construct_runtime! { StableAsset: bifrost_stable_asset::{Pallet, Storage, Event} = 128, StablePool: bifrost_stable_pool = 129, VtokenVoting: bifrost_vtoken_voting = 130, + LendMarket: lend_market = 131, + Prices: pallet_prices = 132, + Oracle: orml_oracle:: = 133, + OracleMembership: pallet_membership::::{Pallet, Call, Storage, Event, Config} = 134, + LeverageStaking: leverage_staking = 135, } } @@ -1967,6 +2071,20 @@ impl_runtime_apis! { } } + impl lend_market_rpc_runtime_api::LendMarketApi for Runtime { + fn get_account_liquidity(account: AccountId) -> Result<(Liquidity, Shortfall, Liquidity, Shortfall), DispatchError> { + LendMarket::get_account_liquidity(&account) + } + + fn get_market_status(asset_id: CurrencyId) -> Result<(Rate, Rate, Rate, Ratio, Balance, Balance, sp_runtime::FixedU128), DispatchError> { + LendMarket::get_market_status(asset_id) + } + + fn get_liquidation_threshold_liquidity(account: AccountId) -> Result<(Liquidity, Shortfall, Liquidity, Shortfall), DispatchError> { + LendMarket::get_account_liquidation_threshold_liquidity(&account) + } + } + impl bifrost_stable_pool_rpc_runtime_api::StablePoolRuntimeApi for Runtime { fn get_swap_output( pool_id: u32, diff --git a/runtime/bifrost-polkadot/src/weights/mod.rs b/runtime/bifrost-polkadot/src/weights/mod.rs index 8179b7b0a..5ece2aeea 100644 --- a/runtime/bifrost-polkadot/src/weights/mod.rs +++ b/runtime/bifrost-polkadot/src/weights/mod.rs @@ -37,5 +37,6 @@ pub mod bifrost_ve_minting; pub mod bifrost_vstoken_conversion; pub mod bifrost_vtoken_minting; pub mod bifrost_vtoken_voting; +pub mod orml_oracle; pub mod orml_tokens; pub mod pallet_xcm; diff --git a/runtime/bifrost-polkadot/src/weights/orml_oracle.rs b/runtime/bifrost-polkadot/src/weights/orml_oracle.rs new file mode 100644 index 000000000..dffcbeaf8 --- /dev/null +++ b/runtime/bifrost-polkadot/src/weights/orml_oracle.rs @@ -0,0 +1,45 @@ +//! Autogenerated weights for orml_oracle +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 +//! DATE: 2021-05-04, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 + +// Executed Command: +// target/release/bifrost +// benchmark +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=orml_oracle +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./oracle/src/weights.rs +// --template +// ../templates/orml-weight-template.hbs + + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(clippy::unnecessary_cast)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +pub struct WeightInfo(PhantomData); +impl orml_oracle::WeightInfo for WeightInfo { + fn feed_values(c: u32, ) -> Weight { + Weight::from_parts(16_800_000, 0) + // Standard Error: 84_000 + .saturating_add(Weight::from_parts(3_600_000, 0).saturating_mul(c as u64)) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + .saturating_add(RocksDbWeight::get().writes((2 as u64).saturating_mul(c as u64))) + } + fn on_finalize() -> Weight { + Weight::from_parts(3_000_000, 0) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } +} \ No newline at end of file