diff --git a/Cargo.lock b/Cargo.lock index 58aa73b8c7..89fa577626 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4180,7 +4180,7 @@ dependencies = [ [[package]] name = "fudge" version = "0.0.10" -source = "git+https://github.com/centrifuge/fudge?branch=polkadot-v0.9.43#c6ee97cc74d023a891fd6029d8f4bd3f67d4fbe2" +source = "git+https://github.com/centrifuge/fudge?branch=polkadot-v0.9.43#a7162cac11809579ff23f74628a70700075e6a14" dependencies = [ "fudge-companion", "fudge-core", @@ -4193,7 +4193,7 @@ dependencies = [ [[package]] name = "fudge-companion" version = "0.0.7" -source = "git+https://github.com/centrifuge/fudge?branch=polkadot-v0.9.43#c6ee97cc74d023a891fd6029d8f4bd3f67d4fbe2" +source = "git+https://github.com/centrifuge/fudge?branch=polkadot-v0.9.43#a7162cac11809579ff23f74628a70700075e6a14" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -4204,7 +4204,7 @@ dependencies = [ [[package]] name = "fudge-core" version = "0.0.10" -source = "git+https://github.com/centrifuge/fudge?branch=polkadot-v0.9.43#c6ee97cc74d023a891fd6029d8f4bd3f67d4fbe2" +source = "git+https://github.com/centrifuge/fudge?branch=polkadot-v0.9.43#a7162cac11809579ff23f74628a70700075e6a14" dependencies = [ "async-trait", "bitvec 1.0.1", @@ -11448,6 +11448,7 @@ dependencies = [ "frame-support", "frame-system", "fudge", + "fudge-core", "getrandom 0.2.10", "hex", "kusama-runtime", @@ -11461,7 +11462,9 @@ dependencies = [ "orml-xtokens", "pallet-aura", "pallet-authorship", + "pallet-babe", "pallet-balances", + "pallet-beefy", "pallet-block-rewards", "pallet-collator-selection", "pallet-collective", @@ -11471,6 +11474,8 @@ dependencies = [ "pallet-evm", "pallet-evm-chain-id", "pallet-foreign-investments", + "pallet-grandpa", + "pallet-im-online", "pallet-investments", "pallet-liquidity-pools", "pallet-liquidity-pools-gateway", @@ -11509,6 +11514,7 @@ dependencies = [ "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", + "sp-consensus-beefy", "sp-consensus-slots", "sp-core", "sp-inherents", @@ -11518,11 +11524,11 @@ dependencies = [ "sp-timestamp", "sp-tracing", "sp-transaction-pool", + "thiserror", "tokio", "tracing-subscriber", "xcm", "xcm-executor", - "xcm-simulator", ] [[package]] @@ -16506,24 +16512,6 @@ dependencies = [ "syn 2.0.38", ] -[[package]] -name = "xcm-simulator" -version = "0.9.43" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.43#ba42b9ce51d25bdaf52d2c61e0763a6e3da50d25" -dependencies = [ - "frame-support", - "parity-scale-codec 3.6.5", - "paste", - "polkadot-core-primitives", - "polkadot-parachain", - "polkadot-runtime-parachains", - "sp-io", - "sp-std", - "xcm", - "xcm-builder", - "xcm-executor", -] - [[package]] name = "yamux" version = "0.10.2" diff --git a/runtime/integration-tests/Cargo.toml b/runtime/integration-tests/Cargo.toml index 53558db31a..7fd20d7122 100644 --- a/runtime/integration-tests/Cargo.toml +++ b/runtime/integration-tests/Cargo.toml @@ -10,10 +10,12 @@ repository = "https://github.com/centrifuge/centrifuge-chain" [dependencies] codec = { package = "parity-scale-codec", version = "3.0", default-features = false, features = ["derive"] } fudge = { git = "https://github.com/centrifuge/fudge", branch = "polkadot-v0.9.43" } +fudge-core = { git = "https://github.com/centrifuge/fudge", branch = "polkadot-v0.9.43" } lazy_static = "1.4.0" serde = { version = "1.0.119" } tokio = { version = "1.15", features = ["macros"] } tracing-subscriber = "0.2" +thiserror = "1.0.30" # Substrate ## Substrate-Frame @@ -22,9 +24,13 @@ frame-support = { git = "https://github.com/paritytech/substrate", branch = "pol frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43" } pallet-aura = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43" } pallet-authorship = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +pallet-babe = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +pallet-beefy = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +pallet-grandpa = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } pallet-collective = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } pallet-democracy = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +pallet-im-online = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } pallet-message-queue = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } pallet-preimage = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } @@ -39,6 +45,7 @@ fp-self-contained = { git = "https://github.com/moonbeam-foundation/frontier", b sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } sp-consensus-babe = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +sp-consensus-beefy = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } sp-consensus-slots = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } @@ -83,9 +90,6 @@ orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-li orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch = "polkadot-v0.9.43" } orml-xtokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch = "polkadot-v0.9.43" } -# Misc -xcm-simulator = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.43" } - # Local altair-runtime = { path = "../altair" } centrifuge-runtime = { path = "../centrifuge" } @@ -167,6 +171,7 @@ std = [ "pallet-balances/std", "pallet-collective/std", "pallet-democracy/std", + "pallet-beefy/std", "pallet-foreign-investments/std", "pallet-investments/std", "pallet-message-queue/std", diff --git a/runtime/integration-tests/src/generic/cases/example.rs b/runtime/integration-tests/src/generic/cases/example.rs index dee3c8a77a..730729412d 100644 --- a/runtime/integration-tests/src/generic/cases/example.rs +++ b/runtime/integration-tests/src/generic/cases/example.rs @@ -32,6 +32,7 @@ fn transfer_balance() { )], }) .storage(), + Genesis::::default().storage(), ); // Call an extrinsic that would be processed immediately @@ -54,7 +55,7 @@ fn transfer_balance() { .unwrap(); // Check the state - env.state(|| { + env.parachain_state(|| { assert_eq!( pallet_balances::Pallet::::free_balance(Keyring::Alice.to_account_id()), T::ExistentialDeposit::get() + FOR_FEES - fee, @@ -83,6 +84,7 @@ fn fudge_transfer_balance() { )], }) .storage(), + Genesis::::default().storage(), ); env.submit_later( @@ -116,7 +118,7 @@ fn fudge_transfer_balance() { .unwrap(); // Check the state - env.state(|| { + env.parachain_state(|| { assert_eq!( pallet_balances::Pallet::::free_balance(Keyring::Alice.to_account_id()), T::ExistentialDeposit::get() + FOR_FEES - fee, @@ -129,9 +131,9 @@ fn fudge_transfer_balance() { } fn call_api() { - let env = RuntimeEnv::::from_storage(Default::default()); + let env = RuntimeEnv::::from_storage(Default::default(), Default::default()); - env.state(|| { + env.parachain_state(|| { // If imported the trait: sp_api::runtime_decl_for_core::CoreV4, // you can easily do: T::Api::version() assert_eq!( @@ -142,7 +144,7 @@ fn call_api() { } fn fudge_call_api() { - let env = FudgeEnv::::from_storage(Default::default()); + let env = FudgeEnv::::from_storage(Default::default(), Default::default()); // Exclusive from fudge environment. // It uses a client to access the runtime api. @@ -158,14 +160,14 @@ fn fudge_call_api() { } fn pass_time_one_block() { - let mut env = RuntimeEnv::::from_storage(Default::default()); + let mut env = RuntimeEnv::::from_storage(Default::default(), Default::default()); - let before = env.state(|| pallet_timestamp::Pallet::::get()); + let before = env.parachain_state(|| pallet_timestamp::Pallet::::get()); // Not supported in fudge env.pass(Blocks::JumpBySeconds(SECONDS_PER_YEAR)); - let after = env.state(|| pallet_timestamp::Pallet::::get()); + let after = env.parachain_state(|| pallet_timestamp::Pallet::::get()); assert_eq!((after - before).into_seconds(), SECONDS_PER_YEAR) } diff --git a/runtime/integration-tests/src/generic/cases/liquidity_pools.rs b/runtime/integration-tests/src/generic/cases/liquidity_pools.rs new file mode 100644 index 0000000000..37ea310a51 --- /dev/null +++ b/runtime/integration-tests/src/generic/cases/liquidity_pools.rs @@ -0,0 +1,2371 @@ +use cfg_primitives::{currency_decimals, parachains, AccountId, Balance}; +use cfg_types::{ + tokens::{CrossChainTransferability, CurrencyId, CustomMetadata}, + xcm::XcmMetadata, +}; +use cfg_utils::vec_to_fixed_array; +use codec::Encode; +use frame_support::{assert_noop, assert_ok, dispatch::RawOrigin, traits::OriginTrait}; +use orml_traits::{asset_registry::AssetMetadata, MultiCurrency}; +use polkadot_parachain::primitives::Id; +use runtime_common::{ + xcm::general_key, + xcm_fees::{default_per_second, ksm_per_second}, +}; +use sp_runtime::{ + traits::{AccountIdConversion, BadOrigin, ConstU32, Convert as C2}, + WeakBoundedVec, +}; +use xcm::{ + prelude::XCM_VERSION, + v3::{ + AssetId, Fungibility, Junction, Junction::*, Junctions, Junctions::*, MultiAsset, + MultiAssets, MultiLocation, WeightLimit, + }, + VersionedMultiAsset, VersionedMultiAssets, VersionedMultiLocation, +}; +use xcm_executor::traits::Convert as C1; + +use crate::{ + generic::{ + config::Runtime, + env::{Blocks, Env}, + envs::fudge_env::{handle::FudgeHandle, FudgeEnv, FudgeSupport}, + utils::{genesis, genesis::Genesis}, + }, + utils::{accounts::Keyring, AUSD_CURRENCY_ID}, +}; + +mod utils { + use super::*; + + pub fn parachain_account(id: u32) -> AccountId { + polkadot_parachain::primitives::Sibling::from(id).into_account_truncating() + } + + pub fn xcm_metadata(transferability: CrossChainTransferability) -> Option { + match transferability { + CrossChainTransferability::Xcm(x) | CrossChainTransferability::All(x) => Some(x), + _ => None, + } + } + + pub fn setup_xcm(env: &mut FudgeEnv) { + env.parachain_state_mut(|| { + // Set the XCM version used when sending XCM messages to sibling. + assert_ok!(pallet_xcm::Pallet::::force_xcm_version( + ::RuntimeOrigin::root(), + Box::new(MultiLocation::new( + 1, + Junctions::X1(Junction::Parachain(T::FudgeHandle::SIBLING_ID)), + )), + XCM_VERSION, + )); + }); + + env.sibling_state_mut(|| { + // Set the XCM version used when sending XCM messages to parachain. + assert_ok!(pallet_xcm::Pallet::::force_xcm_version( + ::RuntimeOrigin::root(), + Box::new(MultiLocation::new( + 1, + Junctions::X1(Junction::Parachain(T::FudgeHandle::PARA_ID)), + )), + XCM_VERSION, + )); + }); + + env.relay_state_mut(|| { + assert_ok!(polkadot_runtime_parachains::hrmp::Pallet::< + FudgeRelayRuntime, + >::force_open_hrmp_channel( + as frame_system::Config>::RuntimeOrigin::root(), + Id::from(T::FudgeHandle::PARA_ID), + Id::from(T::FudgeHandle::SIBLING_ID), + 10, + 1024, + )); + + assert_ok!(polkadot_runtime_parachains::hrmp::Pallet::< + FudgeRelayRuntime, + >::force_open_hrmp_channel( + as frame_system::Config>::RuntimeOrigin::root(), + Id::from(T::FudgeHandle::SIBLING_ID), + Id::from(T::FudgeHandle::PARA_ID), + 10, + 1024, + )); + + assert_ok!(polkadot_runtime_parachains::hrmp::Pallet::< + FudgeRelayRuntime, + >::force_process_hrmp_open( + as frame_system::Config>::RuntimeOrigin::root(), + 0, + )); + }); + + env.pass(Blocks::ByNumber(1)); + } +} + +type FudgeRelayRuntime = <::FudgeHandle as FudgeHandle>::RelayRuntime; + +use utils::*; + +mod altair { + use altair_runtime::{CurrencyIdConvert, PoolPalletIndex}; + + pub const KSM_ASSET_ID: CurrencyId = CurrencyId::ForeignAsset(1000); + + use super::*; + + mod utils { + use super::*; + + pub fn register_ausd() { + let meta: AssetMetadata = AssetMetadata { + decimals: 12, + name: "Acala Dollar".into(), + symbol: "AUSD".into(), + existential_deposit: 1_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::SIBLING_ID), + general_key(parachains::kusama::karura::AUSD_KEY), + ), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(AUSD_CURRENCY_ID) + )); + } + + pub fn register_air() { + let meta: AssetMetadata = AssetMetadata { + decimals: 18, + name: "Altair".into(), + symbol: "AIR".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 1, + X2( + Parachain(parachains::kusama::altair::ID), + general_key(parachains::kusama::altair::AIR_KEY), + ), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(CurrencyId::Native) + )); + } + + pub fn register_ksm() { + let meta: AssetMetadata = AssetMetadata { + decimals: 12, + name: "Kusama".into(), + symbol: "KSM".into(), + existential_deposit: 1_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new(1, Here))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(KSM_ASSET_ID) + )); + } + + pub fn air(amount: Balance) -> Balance { + amount * dollar(currency_decimals::NATIVE) + } + + pub fn ausd(amount: Balance) -> Balance { + amount * dollar(currency_decimals::AUSD) + } + + pub fn ksm(amount: Balance) -> Balance { + amount * dollar(currency_decimals::KSM) + } + + pub fn foreign(amount: Balance, decimals: u32) -> Balance { + amount * dollar(decimals) + } + + pub fn dollar(decimals: u32) -> Balance { + 10u128.saturating_pow(decimals) + } + + pub fn air_fee() -> Balance { + fee(currency_decimals::NATIVE) + } + + pub fn ausd_fee() -> Balance { + fee(currency_decimals::AUSD) + } + + pub fn fee(decimals: u32) -> Balance { + calc_fee(default_per_second(decimals)) + } + + // The fee associated with transferring KSM tokens + pub fn ksm_fee() -> Balance { + calc_fee(ksm_per_second()) + } + + pub fn calc_fee(fee_per_second: Balance) -> Balance { + // We divide the fee to align its unit and multiply by 4 as that seems to be the + // unit of time the tests take. + // NOTE: it is possible that in different machines this value may differ. We + // shall see. + fee_per_second.div_euclid(10_000) * 8 + } + } + + use utils::*; + + mod transfers { + use super::*; + + fn transfer_air_to_sibling(env: &mut FudgeEnv) { + let alice_initial_balance = air(10); + let transfer_amount = air(5); + let air_in_sibling = CurrencyId::ForeignAsset(12); + + env.parachain_state_mut(|| { + assert_eq!( + pallet_balances::Pallet::::free_balance(&Keyring::Alice.into()), + alice_initial_balance + ); + assert_eq!( + pallet_balances::Pallet::::free_balance(¶chain_account( + T::FudgeHandle::SIBLING_ID + )), + 0 + ); + + // Register AIR as foreign asset in the sibling parachain + let meta: AssetMetadata = AssetMetadata { + decimals: 18, + name: "Altair".into(), + symbol: "AIR".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::PARA_ID), + general_key(parachains::kusama::altair::AIR_KEY), + ), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(CurrencyId::Native) + )); + }); + + env.sibling_state_mut(|| { + assert_eq!( + orml_tokens::Pallet::::free_balance(air_in_sibling, &Keyring::Bob.into()), + 0 + ); + + // Register AIR as foreign asset in the sibling parachain + let meta: AssetMetadata = AssetMetadata { + decimals: 18, + name: "Altair".into(), + symbol: "AIR".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::PARA_ID), + general_key(parachains::kusama::altair::AIR_KEY), + ), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(air_in_sibling) + )); + }); + + env.pass(Blocks::ByNumber(1)); + + env.parachain_state_mut(|| { + assert_ok!(orml_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + CurrencyId::Native, + transfer_amount, + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::SIBLING_ID), + Junction::AccountId32 { + network: None, + id: Keyring::Bob.into(), + } + ) + ) + .into() + ), + WeightLimit::Limited(8_000_000_000_000.into()), + )); + + // Confirm that Alice's balance is initial balance - amount transferred + assert_eq!( + pallet_balances::Pallet::::free_balance(&Keyring::Alice.into()), + alice_initial_balance - transfer_amount + ); + + // Verify that the amount transferred is now part of the sibling account here + assert_eq!( + pallet_balances::Pallet::::free_balance(¶chain_account( + T::FudgeHandle::SIBLING_ID + )), + transfer_amount + ); + }); + + env.pass(Blocks::ByNumber(1)); + + env.sibling_state_mut(|| { + let current_balance = + orml_tokens::Pallet::::free_balance(air_in_sibling, &Keyring::Bob.into()); + + // Verify that Keyring::Bob now has (amount transferred - fee) + assert_eq!(current_balance, transfer_amount - fee(18)); + + // Sanity check for the actual amount Keyring::Bob ends up with + assert_eq!(current_balance, 4992960800000000000); + }); + } + + fn test_air_transfers_to_and_from_sibling() { + let mut env = FudgeEnv::::from_storage( + Genesis::default() + .add(genesis::balances::(air(10))) + .storage(), + Default::default(), + ); + + setup_xcm(&mut env); + + // In order to be able to transfer AIR from Sibling to Altair, we need to first + // send AIR from Altair to Sibling, or else it fails since it'd be like Sibling + // had minted AIR on their side. + transfer_air_to_sibling(&mut env); + + let alice_initial_balance = air(5); + let bob_initial_balance = air(5) - air_fee(); + let transfer_amount = air(1); + + // Note: This asset was registered in `transfer_air_to_sibling` + let air_in_sibling = CurrencyId::ForeignAsset(12); + + env.parachain_state_mut(|| { + assert_eq!( + pallet_balances::Pallet::::free_balance(&Keyring::Alice.into()), + alice_initial_balance + ); + }); + + env.sibling_state_mut(|| { + assert_eq!( + pallet_balances::Pallet::::free_balance(¶chain_account( + T::FudgeHandle::PARA_ID + )), + 0 + ); + assert_eq!( + orml_tokens::Pallet::::free_balance(air_in_sibling, &Keyring::Bob.into()), + bob_initial_balance + ); + + assert_ok!(orml_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Bob.into()).into(), + air_in_sibling, + transfer_amount, + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::PARA_ID), + Junction::AccountId32 { + network: None, + id: Keyring::Alice.into(), + } + ) + ) + .into() + ), + WeightLimit::Limited(8_000_000_000_000.into()), + )); + + // Confirm that Bobs's balance is initial balance - amount transferred + assert_eq!( + orml_tokens::Pallet::::free_balance(air_in_sibling, &Keyring::Bob.into()), + bob_initial_balance - transfer_amount + ); + }); + + env.pass(Blocks::ByNumber(2)); + + env.parachain_state_mut(|| { + // Verify that Keyring::Alice now has initial balance + amount transferred - fee + assert_eq!( + pallet_balances::Pallet::::free_balance(&Keyring::Alice.into()), + alice_initial_balance + transfer_amount - air_fee(), + ); + }); + } + + fn transfer_ausd_to_altair() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + setup_xcm(&mut env); + + let alice_initial_balance = ausd(10); + let transfer_amount = ausd(7); + + env.sibling_state_mut(|| { + register_ausd::(); + + assert_ok!(orml_tokens::Pallet::::deposit( + AUSD_CURRENCY_ID, + &Keyring::Alice.into(), + alice_initial_balance + )); + + assert_eq!( + orml_tokens::Pallet::::free_balance( + AUSD_CURRENCY_ID, + ¶chain_account(T::FudgeHandle::PARA_ID) + ), + 0 + ); + }); + + env.parachain_state_mut(|| { + register_ausd::(); + + assert_eq!( + orml_tokens::Pallet::::free_balance(AUSD_CURRENCY_ID, &Keyring::Bob.into()), + 0, + ); + }); + + env.sibling_state_mut(|| { + assert_eq!( + orml_tokens::Pallet::::free_balance( + AUSD_CURRENCY_ID, + &Keyring::Alice.into() + ), + ausd(10), + ); + assert_ok!(orml_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + AUSD_CURRENCY_ID, + transfer_amount, + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::PARA_ID), + Junction::AccountId32 { + network: None, + id: Keyring::Bob.into(), + } + ) + ) + .into() + ), + WeightLimit::Limited(8_000_000_000_000.into()), + )); + + assert_eq!( + orml_tokens::Pallet::::free_balance( + AUSD_CURRENCY_ID, + &Keyring::Alice.into() + ), + alice_initial_balance - transfer_amount + ); + + // Verify that the amount transferred is now part of the altair parachain + // account here + assert_eq!( + orml_tokens::Pallet::::free_balance( + AUSD_CURRENCY_ID, + ¶chain_account(T::FudgeHandle::PARA_ID) + ), + transfer_amount + ); + }); + + env.pass(Blocks::ByNumber(2)); + + env.parachain_state_mut(|| { + // Verify that Keyring::Bob now has initial balance + amount transferred - fee + assert_eq!( + orml_tokens::Pallet::::free_balance(AUSD_CURRENCY_ID, &Keyring::Bob.into()), + transfer_amount - ausd_fee() + ); + }); + } + + fn transfer_ksm_from_relay_chain( + env: &mut FudgeEnv, + transfer_amount: Balance, + currency_id: CurrencyId, + meta: AssetMetadata, + ) { + env.parachain_state_mut(|| { + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta.clone(), + Some(currency_id), + )); + + assert_eq!( + orml_tokens::Pallet::::free_balance(currency_id, &Keyring::Bob.into()), + 0 + ); + }); + + env.relay_state_mut(|| { + assert_ok!( + pallet_balances::Pallet::>::force_set_balance( + as frame_system::Config>::RuntimeOrigin::root(), + Keyring::Alice.to_account_id().into(), + transfer_amount * 2, + ) + ); + + assert_ok!( + pallet_xcm::Pallet::>::force_xcm_version( + as frame_system::Config>::RuntimeOrigin::root(), + Box::new(MultiLocation::new( + 0, + Junctions::X1(Junction::Parachain(T::FudgeHandle::PARA_ID)), + )), + XCM_VERSION, + ) + ); + + assert_ok!( + pallet_xcm::Pallet::>::reserve_transfer_assets( + RawOrigin::Signed(Keyring::Alice.into()).into(), + Box::new(Parachain(T::FudgeHandle::PARA_ID).into()), + Box::new( + Junction::AccountId32 { + network: None, + id: Keyring::Bob.into(), + } + .into() + ), + Box::new((Here, transfer_amount).into()), + 0 + ) + ); + }); + + env.pass(Blocks::ByNumber(1)); + + env.parachain_state(|| { + assert_eq!( + orml_tokens::Pallet::::free_balance(currency_id, &Keyring::Bob.into()), + transfer_amount - fee(meta.decimals) + ); + }); + } + + fn transfer_ksm_to_and_from_relay_chain() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + let transfer_amount: Balance = ksm(2); + let currency_id = CurrencyId::ForeignAsset(3001); + let meta: AssetMetadata = AssetMetadata { + decimals: 12, + name: "Kusama".into(), + symbol: "KSM".into(), + existential_deposit: 1_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new(1, Here))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + + // First we need some KSM on Altair + transfer_ksm_from_relay_chain(&mut env, transfer_amount, currency_id, meta.clone()); + + let currency_id = CurrencyId::ForeignAsset(3001); + + env.parachain_state_mut(|| { + assert_eq!( + orml_tokens::Pallet::::free_balance(currency_id, &Keyring::Bob.into()), + transfer_amount - fee(meta.decimals) + ); + + assert_ok!(pallet_xcm::Pallet::::force_xcm_version( + ::RuntimeOrigin::root(), + Box::new(MultiLocation::new(1, Junctions::Here)), + XCM_VERSION, + )); + + assert_ok!(orml_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Bob.into()).into(), + currency_id, + ksm(1), + Box::new( + MultiLocation::new( + 1, + X1(Junction::AccountId32 { + id: Keyring::Bob.into(), + network: None, + }) + ) + .into() + ), + WeightLimit::Limited(4_000_000_000.into()) + )); + }); + + env.pass(Blocks::ByNumber(1)); + + env.relay_state_mut(|| { + assert_eq!( + pallet_balances::Pallet::>::free_balance( + &Keyring::Bob.into() + ), + 999907996044 + ); + }); + } + + fn transfer_foreign_sibling_to_altair() { + let mut env = FudgeEnv::::from_storage( + Genesis::default() + .add(genesis::balances::(air(10))) + .storage(), + Default::default(), + ); + + setup_xcm(&mut env); + + let sibling_asset_id = CurrencyId::ForeignAsset(1); + let asset_location = MultiLocation::new( + 1, + X2(Parachain(T::FudgeHandle::SIBLING_ID), general_key(&[0, 1])), + ); + let meta: AssetMetadata = AssetMetadata { + decimals: 18, + name: "Sibling Native Token".into(), + symbol: "SBLNG".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V3(asset_location)), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(XcmMetadata { + // We specify a custom fee_per_second and verify below that this value is + // used when XCM transfer fees are charged for this token. + fee_per_second: Some(8420000000000000000), + }), + ..CustomMetadata::default() + }, + }; + let transfer_amount = foreign(1, meta.decimals); + + env.sibling_state_mut(|| { + assert_eq!( + orml_tokens::Pallet::::free_balance(sibling_asset_id, &Keyring::Bob.into()), + 0 + ); + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta.clone(), + Some(CurrencyId::Native), + )); + }); + + env.parachain_state_mut(|| { + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta.clone(), + Some(sibling_asset_id) + )); + }); + + env.sibling_state_mut(|| { + assert_ok!(pallet_balances::Pallet::::force_set_balance( + ::RuntimeOrigin::root(), + Keyring::Alice.to_account_id().into(), + transfer_amount * 2, + )); + + assert_ok!(orml_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + CurrencyId::Native, + transfer_amount, + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::PARA_ID), + Junction::AccountId32 { + network: None, + id: Keyring::Bob.into(), + } + ) + ) + .into() + ), + WeightLimit::Limited(8_000_000_000_000.into()), + )); + + // Confirm that Alice's balance is initial balance - amount transferred + assert_eq!( + pallet_balances::Pallet::::free_balance(&Keyring::Alice.into()), + transfer_amount + ); + }); + + env.pass(Blocks::ByNumber(2)); + + env.parachain_state_mut(|| { + let bob_balance = + orml_tokens::Pallet::::free_balance(sibling_asset_id, &Keyring::Bob.into()); + + // Verify that Keyring::Bob now has initial balance + amount transferred - fee + assert_eq!( + bob_balance, + transfer_amount + - calc_fee( + xcm_metadata(meta.additional.transferability) + .unwrap() + .fee_per_second + .unwrap() + ) + ); + // Sanity check to ensure the calculated is what is expected + assert_eq!(bob_balance, 993264000000000000); + }); + } + + fn transfer_wormhole_usdc_karura_to_altair() { + let mut env = FudgeEnv::::from_storage( + Default::default(), + Genesis::default() + .add(genesis::balances::(air(10))) + .storage(), + ); + + setup_xcm(&mut env); + + let usdc_asset_id = CurrencyId::ForeignAsset(39); + let asset_location = MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::SIBLING_ID), + general_key("0x02f3a00dd12f644daec907013b16eb6d14bf1c4cb4".as_bytes()), + ), + ); + let meta: AssetMetadata = AssetMetadata { + decimals: 6, + name: "Wormhole USDC".into(), + symbol: "WUSDC".into(), + existential_deposit: 1, + location: Some(VersionedMultiLocation::V3(asset_location)), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + let transfer_amount = foreign(12, meta.decimals); + let alice_initial_balance = transfer_amount * 100; + + env.sibling_state_mut(|| { + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta.clone(), + Some(usdc_asset_id) + )); + assert_ok!(orml_tokens::Pallet::::deposit( + usdc_asset_id, + &Keyring::Alice.into(), + alice_initial_balance + )); + assert_eq!( + orml_tokens::Pallet::::free_balance(usdc_asset_id, &Keyring::Alice.into()), + alice_initial_balance + ); + assert_eq!( + pallet_balances::Pallet::::free_balance(&Keyring::Alice.into()), + air(10) + ); + }); + + env.parachain_state_mut(|| { + // First, register the asset in centrifuge + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta.clone(), + Some(usdc_asset_id) + )); + }); + + env.sibling_state_mut(|| { + assert_ok!(orml_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + usdc_asset_id, + transfer_amount, + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::PARA_ID), + Junction::AccountId32 { + network: None, + id: Keyring::Bob.into(), + } + ) + ) + .into() + ), + WeightLimit::Limited(8_000_000_000.into()), + )); + + // Confirm that Alice's balance is initial balance - amount transferred + assert_eq!( + orml_tokens::Pallet::::free_balance(usdc_asset_id, &Keyring::Alice.into()), + alice_initial_balance - transfer_amount + ); + }); + + env.pass(Blocks::ByNumber(2)); + + env.parachain_state_mut(|| { + let bob_balance = + orml_tokens::Pallet::::free_balance(usdc_asset_id, &Keyring::Bob.into()); + + // Sanity check to ensure the calculated is what is expected + assert_eq!(bob_balance, 11992961); + }); + } + + crate::test_for_runtimes!([altair], test_air_transfers_to_and_from_sibling); + crate::test_for_runtimes!([altair], transfer_ausd_to_altair); + crate::test_for_runtimes!([altair], transfer_ksm_to_and_from_relay_chain); + crate::test_for_runtimes!([altair], transfer_foreign_sibling_to_altair); + crate::test_for_runtimes!([altair], transfer_wormhole_usdc_karura_to_altair); + } + + mod asset_registry { + use super::*; + + fn register_air_works() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + env.parachain_state_mut(|| { + let meta: AssetMetadata = AssetMetadata { + decimals: 18, + name: "Altair".into(), + symbol: "AIR".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 0, + X1(general_key(parachains::kusama::altair::AIR_KEY)), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(CurrencyId::Native) + )); + }); + } + + fn register_foreign_asset_works() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + env.parachain_state_mut(|| { + let meta: AssetMetadata = AssetMetadata { + decimals: 12, + name: "Acala Dollar".into(), + symbol: "AUSD".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::SIBLING_ID), + general_key(parachains::kusama::karura::AUSD_KEY), + ), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(CurrencyId::ForeignAsset(42)) + )); + }); + } + + // Verify that registering tranche tokens is not allowed through extrinsics + fn register_tranche_asset_blocked() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + env.parachain_state_mut(|| { + let meta: AssetMetadata = AssetMetadata { + decimals: 12, + name: "Tranche Token 1".into(), + symbol: "TRNCH".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 1, + X2(Parachain(2000), general_key(&[42])), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + + // It fails with `BadOrigin` even when submitted with `Origin::root` since we + // only allow for tranche tokens to be registered through the pools pallet. + let asset_id = CurrencyId::Tranche(42, [42u8; 16]); + assert_noop!( + orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(asset_id) + ), + BadOrigin + ); + }); + } + + crate::test_for_runtimes!([altair], register_air_works); + crate::test_for_runtimes!([altair], register_foreign_asset_works); + crate::test_for_runtimes!([altair], register_tranche_asset_blocked); + } + + mod currency_id_convert { + use super::*; + + fn convert_air() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + assert_eq!(parachains::kusama::altair::AIR_KEY.to_vec(), vec![0, 1]); + + env.parachain_state_mut(|| { + // The way AIR is represented relative within the Altair runtime + let air_location_inner: MultiLocation = + MultiLocation::new(0, X1(general_key(parachains::kusama::altair::AIR_KEY))); + + // register air + register_air::(); + + assert_eq!( + >::convert(air_location_inner), + Ok(CurrencyId::Native), + ); + + // The canonical way AIR is represented out in the wild + let air_location_canonical: MultiLocation = MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::PARA_ID), + general_key(parachains::kusama::altair::AIR_KEY), + ), + ); + + assert_eq!( + >::convert(CurrencyId::Native), + Some(air_location_canonical) + ) + }); + } + + /// Verify that Tranche tokens are not handled by the CurrencyIdConvert + /// since we don't allow Tranche tokens to be transferable through XCM. + fn convert_tranche() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + let tranche_currency = CurrencyId::Tranche(401, [0; 16]); + let tranche_id = + WeakBoundedVec::>::force_from(tranche_currency.encode(), None); + let tranche_multilocation = MultiLocation { + parents: 1, + interior: X3( + Parachain(T::FudgeHandle::PARA_ID), + PalletInstance(PoolPalletIndex::get()), + GeneralKey { + length: tranche_id.len() as u8, + data: vec_to_fixed_array(tranche_id.to_vec()), + }, + ), + }; + + env.parachain_state_mut(|| { + assert_eq!( + >::convert(tranche_multilocation), + Err(tranche_multilocation), + ); + }); + + env.parachain_state_mut(|| { + assert_eq!( + >::convert(tranche_currency), + None + ) + }); + } + + fn convert_ausd() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + env.parachain_state_mut(|| { + assert_eq!(parachains::kusama::karura::AUSD_KEY, &[0, 129]); + + let ausd_location: MultiLocation = MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::SIBLING_ID), + general_key(parachains::kusama::karura::AUSD_KEY), + ), + ); + + register_ausd::(); + + assert_eq!( + >::convert(ausd_location.clone()), + Ok(AUSD_CURRENCY_ID), + ); + + assert_eq!( + >::convert(AUSD_CURRENCY_ID), + Some(ausd_location) + ) + }); + } + + fn convert_ksm() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + let ksm_location: MultiLocation = MultiLocation::parent().into(); + + env.parachain_state_mut(|| { + register_ksm::(); + + assert_eq!( + >::convert(ksm_location), + Ok(KSM_ASSET_ID), + ); + + assert_eq!( + >::convert(KSM_ASSET_ID), + Some(ksm_location) + ) + }); + } + + fn convert_unkown_multilocation() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + let unknown_location: MultiLocation = MultiLocation::new( + 1, + X2(Parachain(T::FudgeHandle::PARA_ID), general_key(&[42])), + ); + + env.parachain_state_mut(|| { + assert!(>::convert(unknown_location).is_err()); + }); + } + + fn convert_unsupported_currency() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + env.parachain_state_mut(|| { + assert_eq!( + >::convert(CurrencyId::Tranche( + 0, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] + )), + None + ) + }); + } + + crate::test_for_runtimes!([altair], convert_air); + crate::test_for_runtimes!([altair], convert_tranche); + crate::test_for_runtimes!([altair], convert_ausd); + crate::test_for_runtimes!([altair], convert_ksm); + crate::test_for_runtimes!([altair], convert_unkown_multilocation); + crate::test_for_runtimes!([altair], convert_unsupported_currency); + } +} + +mod centrifuge { + use centrifuge_runtime::CurrencyIdConvert; + + use super::*; + + mod utils { + use super::*; + + /// The test asset id attributed to DOT + pub const DOT_ASSET_ID: CurrencyId = CurrencyId::ForeignAsset(91); + + /// An Asset that is NOT XCM transferable + pub const NO_XCM_ASSET_ID: CurrencyId = CurrencyId::ForeignAsset(401); + + /// Register DOT in the asset registry. + /// It should be executed within an externalities environment. + pub fn register_dot() { + let meta: AssetMetadata = AssetMetadata { + decimals: 10, + name: "Polkadot".into(), + symbol: "DOT".into(), + existential_deposit: 100_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::parent())), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(DOT_ASSET_ID) + )); + } + + /// Register AUSD in the asset registry. + /// It should be executed within an externalities environment. + pub fn register_ausd() { + let meta: AssetMetadata = AssetMetadata { + decimals: 12, + name: "Acala Dollar".into(), + symbol: "AUSD".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::SIBLING_ID), + general_key(parachains::polkadot::acala::AUSD_KEY), + ), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(AUSD_CURRENCY_ID) + )); + } + + /// Register CFG in the asset registry. + /// It should be executed within an externalities environment. + pub fn register_cfg() { + let meta: AssetMetadata = AssetMetadata { + decimals: 18, + name: "Centrifuge".into(), + symbol: "CFG".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::PARA_ID), + general_key(parachains::polkadot::centrifuge::CFG_KEY), + ), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(CurrencyId::Native) + )); + } + + /// Register CFG in the asset registry as XCM v2, just like it is in + /// production. It should be executed within an externalities + /// environment. + pub fn register_cfg_v2() { + let meta: AssetMetadata = AssetMetadata { + decimals: 18, + name: "Centrifuge".into(), + symbol: "CFG".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V2(xcm::v2::MultiLocation::new( + 1, + xcm::v2::Junctions::X2( + xcm::v2::Junction::Parachain(T::FudgeHandle::PARA_ID), + xcm::v2::Junction::GeneralKey( + WeakBoundedVec::>::force_from( + parachains::polkadot::centrifuge::CFG_KEY.into(), + None, + ), + ), + ), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(CurrencyId::Native) + )); + } + + /// Register a token whose `CrossChainTransferability` does NOT include + /// XCM. + pub fn register_no_xcm_token() { + let meta: AssetMetadata = AssetMetadata { + decimals: 18, + name: "NO XCM".into(), + symbol: "NXCM".into(), + existential_deposit: 1_000_000_000_000, + location: None, + additional: CustomMetadata { + transferability: CrossChainTransferability::LiquidityPools, + ..CustomMetadata::default() + }, + }; + + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(NO_XCM_ASSET_ID) + )); + } + + pub fn cfg_fee() -> Balance { + fee(currency_decimals::NATIVE) + } + + pub fn ausd_fee() -> Balance { + fee(currency_decimals::AUSD) + } + + pub fn fee(decimals: u32) -> Balance { + calc_fee(default_per_second(decimals)) + } + + // The fee associated with transferring DOT tokens + pub fn dot_fee() -> Balance { + fee(10) + } + + pub fn calc_fee(fee_per_second: Balance) -> Balance { + // We divide the fee to align its unit and multiply by 4 as that seems to be the + // unit of time the tests take. + // NOTE: it is possible that in different machines this value may differ. We + // shall see. + fee_per_second.div_euclid(10_000) * 8 + } + + pub fn cfg(amount: Balance) -> Balance { + amount * dollar(currency_decimals::NATIVE) + } + + pub fn dollar(decimals: u32) -> Balance { + 10u128.saturating_pow(decimals) + } + + pub fn ausd(amount: Balance) -> Balance { + amount * dollar(currency_decimals::AUSD) + } + + pub fn dot(amount: Balance) -> Balance { + amount * dollar(10) + } + + pub fn foreign(amount: Balance, decimals: u32) -> Balance { + amount * dollar(decimals) + } + } + + use utils::*; + + mod asset_registry { + use super::*; + + fn register_cfg_works() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + env.parachain_state_mut(|| { + let meta: AssetMetadata = AssetMetadata { + decimals: 18, + name: "Centrifuge".into(), + symbol: "CFG".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 0, + X1(general_key(parachains::polkadot::centrifuge::CFG_KEY)), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(CurrencyId::Native) + )); + }); + } + + fn register_foreign_asset_works() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + env.parachain_state_mut(|| { + let meta: AssetMetadata = AssetMetadata { + decimals: 12, + name: "Acala Dollar".into(), + symbol: "AUSD".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 1, + X2( + Parachain(parachains::polkadot::acala::ID), + general_key(parachains::polkadot::acala::AUSD_KEY), + ), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(CurrencyId::ForeignAsset(42)) + )); + }); + } + + // Verify that registering tranche tokens is not allowed through extrinsics + fn register_tranche_asset_blocked() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + env.parachain_state_mut(|| { + let meta: AssetMetadata = AssetMetadata { + decimals: 12, + name: "Tranche Token 1".into(), + symbol: "TRNCH".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 1, + X2(Parachain(2000), general_key(&[42])), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + + // It fails with `BadOrigin` even when submitted with `Origin::root` since we + // only allow for tranche tokens to be registered through the pools pallet. + let asset_id = CurrencyId::Tranche(42, [42u8; 16]); + assert_noop!( + orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(asset_id) + ), + BadOrigin + ); + }); + } + + crate::test_for_runtimes!([centrifuge], register_cfg_works); + crate::test_for_runtimes!([centrifuge], register_foreign_asset_works); + crate::test_for_runtimes!([centrifuge], register_tranche_asset_blocked); + } + + mod currency_id_convert { + use super::*; + + fn convert_cfg() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + assert_eq!(parachains::polkadot::centrifuge::CFG_KEY, &[0, 1]); + + env.parachain_state_mut(|| { + // The way CFG is represented relative within the Centrifuge runtime + let cfg_location_inner: MultiLocation = MultiLocation::new( + 0, + X1(general_key(parachains::polkadot::centrifuge::CFG_KEY)), + ); + + register_cfg::(); + + assert_eq!( + >::convert(cfg_location_inner), + Ok(CurrencyId::Native), + ); + + // The canonical way CFG is represented out in the wild + let cfg_location_canonical: MultiLocation = MultiLocation::new( + 1, + X2( + Parachain(parachains::polkadot::centrifuge::ID), + general_key(parachains::polkadot::centrifuge::CFG_KEY), + ), + ); + + assert_eq!( + >::convert(CurrencyId::Native), + Some(cfg_location_canonical) + ) + }); + } + + /// Verify that even with CFG registered in the AssetRegistry with a XCM + /// v2 MultiLocation, that `CurrencyIdConvert` can look it up given an + /// identical location in XCM v3. + fn convert_cfg_xcm_v2() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + assert_eq!(parachains::polkadot::centrifuge::CFG_KEY, &[0, 1]); + + env.parachain_state_mut(|| { + // Registered as xcm v2 + register_cfg_v2::(); + + // The way CFG is represented relative within the Centrifuge runtime in xcm v3 + let cfg_location_inner: MultiLocation = MultiLocation::new( + 0, + X1(general_key(parachains::polkadot::centrifuge::CFG_KEY)), + ); + + assert_eq!( + >::convert(cfg_location_inner), + Ok(CurrencyId::Native), + ); + + // The canonical way CFG is represented out in the wild + let cfg_location_canonical: MultiLocation = MultiLocation::new( + 1, + X2( + Parachain(parachains::polkadot::centrifuge::ID), + general_key(parachains::polkadot::centrifuge::CFG_KEY), + ), + ); + + assert_eq!( + >::convert(CurrencyId::Native), + Some(cfg_location_canonical) + ) + }); + } + + /// Verify that a registered token that is NOT XCM transferable is + /// filtered out by CurrencyIdConvert as expected. + fn convert_no_xcm_token() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + env.parachain_state_mut(|| { + register_no_xcm_token::(); + + assert_eq!( + >::convert(NO_XCM_ASSET_ID), + None + ) + }); + } + + fn convert_ausd() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + assert_eq!(parachains::polkadot::acala::AUSD_KEY, &[0, 1]); + + let ausd_location: MultiLocation = MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::SIBLING_ID), + general_key(parachains::polkadot::acala::AUSD_KEY), + ), + ); + + env.parachain_state_mut(|| { + register_ausd::(); + + assert_eq!( + >::convert(ausd_location), + Ok(AUSD_CURRENCY_ID), + ); + + assert_eq!( + >::convert(AUSD_CURRENCY_ID), + Some(ausd_location) + ) + }); + } + + fn convert_dot() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + let dot_location: MultiLocation = MultiLocation::parent(); + + env.parachain_state_mut(|| { + register_dot::(); + + assert_eq!( + >::convert(dot_location), + Ok(DOT_ASSET_ID), + ); + + assert_eq!( + >::convert(DOT_ASSET_ID), + Some(dot_location) + ) + }); + } + + fn convert_unknown_multilocation() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + let unknown_location: MultiLocation = MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::PARA_ID), + general_key([42].as_ref()), + ), + ); + + env.parachain_state_mut(|| { + assert!(>::convert(unknown_location).is_err()); + }); + } + + fn convert_unsupported_currency() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + env.parachain_state_mut(|| { + assert_eq!( + >::convert(CurrencyId::Tranche( + 0, + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] + )), + None + ) + }); + } + + crate::test_for_runtimes!([centrifuge], convert_cfg); + crate::test_for_runtimes!([centrifuge], convert_cfg_xcm_v2); + crate::test_for_runtimes!([centrifuge], convert_no_xcm_token); + crate::test_for_runtimes!([centrifuge], convert_ausd); + crate::test_for_runtimes!([centrifuge], convert_dot); + crate::test_for_runtimes!([centrifuge], convert_unknown_multilocation); + crate::test_for_runtimes!([centrifuge], convert_unsupported_currency); + } + + mod transfers { + use super::*; + + fn transfer_cfg_to_sibling(env: &mut FudgeEnv) { + let alice_initial_balance = cfg(10); + let transfer_amount = cfg(5); + let cfg_in_sibling = CurrencyId::ForeignAsset(12); + + // CFG Metadata + let meta: AssetMetadata = AssetMetadata { + decimals: 18, + name: "Centrifuge".into(), + symbol: "CFG".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::PARA_ID), + general_key(parachains::polkadot::centrifuge::CFG_KEY), + ), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + + env.parachain_state_mut(|| { + assert_eq!( + pallet_balances::Pallet::::free_balance(&Keyring::Alice.into()), + alice_initial_balance + ); + assert_eq!( + pallet_balances::Pallet::::free_balance(¶chain_account( + T::FudgeHandle::SIBLING_ID + )), + 0 + ); + + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta.clone(), + Some(CurrencyId::Native), + )); + }); + + env.sibling_state_mut(|| { + assert_eq!( + orml_tokens::Pallet::::free_balance(cfg_in_sibling, &Keyring::Bob.into()), + 0 + ); + + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(cfg_in_sibling) + )); + }); + + env.parachain_state_mut(|| { + assert_ok!(orml_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + CurrencyId::Native, + transfer_amount, + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::SIBLING_ID), + Junction::AccountId32 { + network: None, + id: Keyring::Bob.into(), + } + ) + ) + .into() + ), + WeightLimit::Limited(8_000_000_000_000.into()), + )); + + // Confirm that Alice's balance is initial balance - amount transferred + assert_eq!( + pallet_balances::Pallet::::free_balance(&Keyring::Alice.into()), + alice_initial_balance - transfer_amount + ); + + // Verify that the amount transferred is now part of the sibling account here + assert_eq!( + pallet_balances::Pallet::::free_balance(¶chain_account( + T::FudgeHandle::SIBLING_ID + )), + transfer_amount + ); + }); + + env.pass(Blocks::ByNumber(1)); + + env.sibling_state_mut(|| { + let current_balance = + orml_tokens::Pallet::::free_balance(cfg_in_sibling, &Keyring::Bob.into()); + + // Verify that Keyring::Bob now has (amount transferred - fee) + assert_eq!(current_balance, transfer_amount - fee(18)); + + // Sanity check for the actual amount Keyring::Bob ends up with + assert_eq!(current_balance, 4992960800000000000); + }); + } + + fn test_cfg_transfers_to_and_from_sibling() { + let mut env = FudgeEnv::::from_storage( + Genesis::default() + .add(genesis::balances::(cfg(10))) + .storage(), + Default::default(), + ); + + setup_xcm(&mut env); + + // In order to be able to transfer CFG from Sibling to Centrifuge, we need to + // first send CFG from Centrifuge to Sibling, or else it fails since it'd be + // like Sibling had minted CFG on their side. + transfer_cfg_to_sibling(&mut env); + + let alice_initial_balance = cfg(5); + let bob_initial_balance = cfg(5) - cfg_fee(); + let transfer_amount = cfg(1); + // Note: This asset was registered in `transfer_cfg_to_sibling` + let cfg_in_sibling = CurrencyId::ForeignAsset(12); + + env.parachain_state_mut(|| { + assert_eq!( + pallet_balances::Pallet::::free_balance(&Keyring::Alice.into()), + alice_initial_balance + ); + }); + + env.sibling_state_mut(|| { + assert_eq!( + pallet_balances::Pallet::::free_balance(¶chain_account( + T::FudgeHandle::PARA_ID + )), + 0 + ); + assert_eq!( + orml_tokens::Pallet::::free_balance(cfg_in_sibling, &Keyring::Bob.into()), + bob_initial_balance + ); + }); + + env.sibling_state_mut(|| { + assert_ok!(orml_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Bob.into()).into(), + cfg_in_sibling, + transfer_amount, + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::PARA_ID), + Junction::AccountId32 { + network: None, + id: Keyring::Alice.into(), + } + ) + ) + .into() + ), + WeightLimit::Limited(8_000_000_000_000.into()), + )); + + // Confirm that Bobs's balance is initial balance - amount transferred + assert_eq!( + orml_tokens::Pallet::::free_balance(cfg_in_sibling, &Keyring::Bob.into()), + bob_initial_balance - transfer_amount + ); + }); + + env.pass(Blocks::ByNumber(2)); + + env.parachain_state_mut(|| { + // Verify that Keyring::Alice now has initial balance + amount transferred - fee + assert_eq!( + pallet_balances::Pallet::::free_balance(&Keyring::Alice.into()), + alice_initial_balance + transfer_amount - cfg_fee(), + ); + }); + } + + fn transfer_ausd_to_centrifuge() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + setup_xcm(&mut env); + + let alice_initial_balance = ausd(10); + let transfer_amount = ausd(7); + + env.sibling_state_mut(|| { + register_ausd::(); + + assert_ok!(orml_tokens::Pallet::::deposit( + AUSD_CURRENCY_ID, + &Keyring::Alice.into(), + alice_initial_balance + )); + + assert_eq!( + orml_tokens::Pallet::::free_balance( + AUSD_CURRENCY_ID, + ¶chain_account(T::FudgeHandle::PARA_ID) + ), + 0 + ); + }); + + env.parachain_state_mut(|| { + register_ausd::(); + + assert_eq!( + orml_tokens::Pallet::::free_balance(AUSD_CURRENCY_ID, &Keyring::Bob.into()), + 0, + ); + }); + + env.sibling_state_mut(|| { + assert_eq!( + orml_tokens::Pallet::::free_balance( + AUSD_CURRENCY_ID, + &Keyring::Alice.into() + ), + ausd(10), + ); + assert_ok!(orml_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + AUSD_CURRENCY_ID, + transfer_amount, + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::PARA_ID), + Junction::AccountId32 { + network: None, + id: Keyring::Bob.into(), + } + ) + ) + .into() + ), + WeightLimit::Limited(8_000_000_000_000.into()), + )); + + assert_eq!( + orml_tokens::Pallet::::free_balance( + AUSD_CURRENCY_ID, + &Keyring::Alice.into() + ), + alice_initial_balance - transfer_amount + ); + + // Verify that the amount transferred is now part of the centrifuge parachain + // account here + assert_eq!( + orml_tokens::Pallet::::free_balance( + AUSD_CURRENCY_ID, + ¶chain_account(T::FudgeHandle::PARA_ID) + ), + transfer_amount + ); + }); + + env.pass(Blocks::ByNumber(2)); + + env.parachain_state_mut(|| { + // Verify that Keyring::Bob now has initial balance + amount transferred - fee + assert_eq!( + orml_tokens::Pallet::::free_balance(AUSD_CURRENCY_ID, &Keyring::Bob.into()), + transfer_amount - ausd_fee() + ); + }); + } + + fn transfer_dot_from_relay_chain(env: &mut FudgeEnv) { + let alice_initial_dot = dot(10); + let transfer_amount: Balance = dot(3); + + env.parachain_state_mut(|| { + register_dot::(); + assert_eq!( + orml_tokens::Pallet::::free_balance(DOT_ASSET_ID, &Keyring::Alice.into()), + 0 + ); + }); + + env.relay_state_mut(|| { + assert_ok!( + pallet_balances::Pallet::>::force_set_balance( + as frame_system::Config>::RuntimeOrigin::root(), + Keyring::Alice.to_account_id().into(), + alice_initial_dot, + ) + ); + + assert_ok!( + pallet_xcm::Pallet::>::force_xcm_version( + as frame_system::Config>::RuntimeOrigin::root(), + Box::new(MultiLocation::new( + 0, + Junctions::X1(Junction::Parachain(T::FudgeHandle::PARA_ID)), + )), + XCM_VERSION, + ) + ); + + assert_ok!( + pallet_xcm::Pallet::>::reserve_transfer_assets( + RawOrigin::Signed(Keyring::Alice.into()).into(), + Box::new(Parachain(T::FudgeHandle::PARA_ID).into()), + Box::new( + Junction::AccountId32 { + network: None, + id: Keyring::Alice.into(), + } + .into() + ), + Box::new((Here, transfer_amount).into()), + 0 + ) + ); + + assert_eq!( + pallet_balances::Pallet::>::free_balance( + &Keyring::Alice.into() + ), + alice_initial_dot - transfer_amount + ); + }); + + env.pass(Blocks::ByNumber(1)); + + env.parachain_state(|| { + assert_eq!( + orml_tokens::Pallet::::free_balance(DOT_ASSET_ID, &Keyring::Alice.into()), + transfer_amount - dot_fee() + ); + }); + } + + fn transfer_dot_to_and_from_relay_chain() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + transfer_dot_from_relay_chain(&mut env); + + env.parachain_state_mut(|| { + let alice_initial_dot = + orml_tokens::Pallet::::free_balance(DOT_ASSET_ID, &Keyring::Alice.into()); + + assert_eq!(alice_initial_dot, dot(3) - dot_fee()); + + assert_ok!(pallet_xcm::Pallet::::force_xcm_version( + ::RuntimeOrigin::root(), + Box::new(MultiLocation::new(1, Junctions::Here)), + XCM_VERSION, + )); + + assert_ok!(orml_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + DOT_ASSET_ID, + dot(1), + Box::new( + MultiLocation::new( + 1, + X1(Junction::AccountId32 { + id: Keyring::Alice.into(), + network: None, + }) + ) + .into() + ), + WeightLimit::Unlimited, + )); + + assert_eq!( + orml_tokens::Pallet::::free_balance(DOT_ASSET_ID, &Keyring::Alice.into()), + alice_initial_dot - dot(1), + ); + }); + + env.pass(Blocks::ByNumber(1)); + + env.relay_state_mut(|| { + assert_eq!( + pallet_balances::Pallet::>::free_balance( + &Keyring::Alice.into() + ), + 79628418552 + ); + }); + } + + fn transfer_foreign_sibling_to_centrifuge() { + let mut env = FudgeEnv::::from_storage( + Genesis::default() + .add(genesis::balances::(cfg(10))) + .storage(), + Default::default(), + ); + + setup_xcm(&mut env); + + let sibling_asset_id = CurrencyId::ForeignAsset(1); + let asset_location = MultiLocation::new( + 1, + X2(Parachain(T::FudgeHandle::SIBLING_ID), general_key(&[0, 1])), + ); + let meta: AssetMetadata = AssetMetadata { + decimals: 18, + name: "Sibling Native Token".into(), + symbol: "SBLNG".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V3(asset_location)), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(XcmMetadata { + // We specify a custom fee_per_second and verify below that this value is + // used when XCM transfer fees are charged for this token. + fee_per_second: Some(8420000000000000000), + }), + ..CustomMetadata::default() + }, + }; + let transfer_amount = foreign(1, meta.decimals); + + env.sibling_state_mut(|| { + assert_eq!( + orml_tokens::Pallet::::free_balance(sibling_asset_id, &Keyring::Bob.into()), + 0 + ); + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta.clone(), + Some(CurrencyId::Native), + )); + }); + + env.parachain_state_mut(|| { + // First, register the asset in centrifuge + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta.clone(), + Some(sibling_asset_id) + )); + }); + + env.sibling_state_mut(|| { + assert_ok!(pallet_balances::Pallet::::force_set_balance( + ::RuntimeOrigin::root(), + Keyring::Alice.to_account_id().into(), + transfer_amount * 2, + )); + + assert_ok!(orml_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + CurrencyId::Native, + transfer_amount, + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::PARA_ID), + Junction::AccountId32 { + network: None, + id: Keyring::Bob.into(), + } + ) + ) + .into() + ), + WeightLimit::Limited(8_000_000_000_000.into()), + )); + + // Confirm that Alice's balance is initial balance - amount transferred + assert_eq!( + pallet_balances::Pallet::::free_balance(&Keyring::Alice.into()), + transfer_amount + ); + }); + + env.pass(Blocks::ByNumber(2)); + + env.parachain_state_mut(|| { + let bob_balance = + orml_tokens::Pallet::::free_balance(sibling_asset_id, &Keyring::Bob.into()); + + // Verify that Keyring::Bob now has initial balance + amount transferred - fee + assert_eq!( + bob_balance, + transfer_amount + - calc_fee( + xcm_metadata(meta.additional.transferability) + .unwrap() + .fee_per_second + .unwrap() + ) + ); + // Sanity check to ensure the calculated is what is expected + assert_eq!(bob_balance, 993264000000000000); + }); + } + + fn transfer_wormhole_usdc_acala_to_centrifuge() { + let mut env = FudgeEnv::::from_storage( + Default::default(), + Genesis::default() + .add(genesis::balances::(cfg(10))) + .storage(), + ); + + setup_xcm(&mut env); + + let usdc_asset_id = CurrencyId::ForeignAsset(39); + let asset_location = MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::SIBLING_ID), + general_key("0x02f3a00dd12f644daec907013b16eb6d14bf1c4cb4".as_bytes()), + ), + ); + let meta: AssetMetadata = AssetMetadata { + decimals: 6, + name: "Wormhole USDC".into(), + symbol: "WUSDC".into(), + existential_deposit: 1, + location: Some(VersionedMultiLocation::V3(asset_location)), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + let transfer_amount = foreign(12, meta.decimals); + let alice_initial_balance = transfer_amount * 100; + + env.sibling_state_mut(|| { + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta.clone(), + Some(usdc_asset_id) + )); + assert_ok!(orml_tokens::Pallet::::deposit( + usdc_asset_id, + &Keyring::Alice.into(), + alice_initial_balance + )); + assert_eq!( + orml_tokens::Pallet::::free_balance(usdc_asset_id, &Keyring::Alice.into()), + alice_initial_balance + ); + assert_eq!( + pallet_balances::Pallet::::free_balance(&Keyring::Alice.into()), + cfg(10) + ); + }); + + env.parachain_state_mut(|| { + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta.clone(), + Some(usdc_asset_id) + )); + }); + + env.sibling_state_mut(|| { + assert_ok!(orml_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + usdc_asset_id, + transfer_amount, + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::PARA_ID), + Junction::AccountId32 { + network: None, + id: Keyring::Bob.into(), + } + ) + ) + .into() + ), + WeightLimit::Limited(8_000_000_000.into()), + )); + // Confirm that Alice's balance is initial balance - amount transferred + assert_eq!( + orml_tokens::Pallet::::free_balance(usdc_asset_id, &Keyring::Alice.into()), + alice_initial_balance - transfer_amount + ); + }); + + env.pass(Blocks::ByNumber(2)); + + env.parachain_state_mut(|| { + let bob_balance = + orml_tokens::Pallet::::free_balance(usdc_asset_id, &Keyring::Bob.into()); + + // Sanity check to ensure the calculated is what is expected + assert_eq!(bob_balance, 11992961); + }); + } + + crate::test_for_runtimes!([centrifuge], test_cfg_transfers_to_and_from_sibling); + crate::test_for_runtimes!([centrifuge], transfer_ausd_to_centrifuge); + crate::test_for_runtimes!([centrifuge], transfer_dot_to_and_from_relay_chain); + crate::test_for_runtimes!([centrifuge], transfer_foreign_sibling_to_centrifuge); + crate::test_for_runtimes!([centrifuge], transfer_wormhole_usdc_acala_to_centrifuge); + } +} + +mod all { + use super::*; + + mod restricted_calls { + use super::*; + + fn xtokens_transfer() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + env.parachain_state_mut(|| { + assert_noop!( + orml_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + CurrencyId::Tranche(401, [0; 16]), + 42, + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::SIBLING_ID), + Junction::AccountId32 { + network: None, + id: Keyring::Bob.into(), + } + ) + ) + .into() + ), + WeightLimit::Limited(8_000_000_000_000.into()), + ), + orml_xtokens::Error::::NotCrossChainTransferableCurrency + ); + }); + } + + fn xtokens_transfer_multiasset() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + let tranche_currency = CurrencyId::Tranche(401, [0; 16]); + let tranche_id = + WeakBoundedVec::>::force_from(tranche_currency.encode(), None); + let tranche_location = MultiLocation { + parents: 1, + interior: X3( + Parachain(123), + PalletInstance(42), + GeneralKey { + length: tranche_id.len() as u8, + data: vec_to_fixed_array(tranche_id.to_vec()), + }, + ), + }; + let tranche_multi_asset = VersionedMultiAsset::from(MultiAsset::from(( + AssetId::Concrete(tranche_location), + Fungibility::Fungible(42), + ))); + + env.parachain_state_mut(|| { + assert_noop!( + orml_xtokens::Pallet::::transfer_multiasset( + RawOrigin::Signed(Keyring::Alice.into()).into(), + Box::new(tranche_multi_asset), + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::SIBLING_ID), + Junction::AccountId32 { + network: None, + id: Keyring::Bob.into(), + } + ) + ) + .into() + ), + WeightLimit::Limited(8_000_000_000_000.into()), + ), + orml_xtokens::Error::::XcmExecutionFailed + ); + }); + } + + fn xtokens_transfer_multiassets() { + let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + + let tranche_currency = CurrencyId::Tranche(401, [0; 16]); + let tranche_id = + WeakBoundedVec::>::force_from(tranche_currency.encode(), None); + let tranche_location = MultiLocation { + parents: 1, + interior: X3( + Parachain(123), + PalletInstance(42), + GeneralKey { + length: tranche_id.len() as u8, + data: vec_to_fixed_array(tranche_id.to_vec()), + }, + ), + }; + let tranche_multi_asset = MultiAsset::from(( + AssetId::Concrete(tranche_location), + Fungibility::Fungible(42), + )); + + env.parachain_state_mut(|| { + assert_noop!( + orml_xtokens::Pallet::::transfer_multiassets( + RawOrigin::Signed(Keyring::Alice.into()).into(), + Box::new(VersionedMultiAssets::from(MultiAssets::from(vec![ + tranche_multi_asset + ]))), + 0, + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::SIBLING_ID), + Junction::AccountId32 { + network: None, + id: Keyring::Bob.into(), + } + ) + ) + .into() + ), + WeightLimit::Limited(8_000_000_000_000.into()), + ), + orml_xtokens::Error::::XcmExecutionFailed + ); + }); + } + + crate::test_for_runtimes!(all, xtokens_transfer); + crate::test_for_runtimes!(all, xtokens_transfer_multiasset); + crate::test_for_runtimes!(all, xtokens_transfer_multiassets); + } +} diff --git a/runtime/integration-tests/src/generic/cases/loans.rs b/runtime/integration-tests/src/generic/cases/loans.rs index 3f1f69472e..046de5552c 100644 --- a/runtime/integration-tests/src/generic/cases/loans.rs +++ b/runtime/integration-tests/src/generic/cases/loans.rs @@ -72,9 +72,10 @@ mod common { .add(genesis::assets(vec![Usd6::ID])) .add(genesis::tokens(vec![(Usd6::ID, Usd6::ED)])) .storage(), + Genesis::::default().storage(), ); - env.state_mut(|| { + env.parachain_state_mut(|| { // Creating a pool utils::give_balance::(POOL_ADMIN.id(), T::PoolDeposit::get()); utils::create_empty_pool::(POOL_ADMIN.id(), POOL_A, Usd6::ID); @@ -96,7 +97,7 @@ mod common { env.pass(Blocks::BySeconds(POOL_MIN_EPOCH_TIME)); - env.state_mut(|| { + env.parachain_state_mut(|| { // New epoch with the investor funds available utils::close_pool_epoch::(POOL_ADMIN.id(), POOL_A); }); @@ -258,7 +259,7 @@ mod call { fn internal_priced() { let mut env = common::initialize_state_for_loans::, T>(); - let info = env.state(|| { + let info = env.parachain_state(|| { let now = as TimeAsSecs>::now(); common::default_loan_info::(now, common::default_internal_pricing()) }); @@ -271,8 +272,8 @@ fn internal_priced() { env.pass(Blocks::BySeconds(SECONDS_PER_MINUTE / 2)); - let loan_portfolio = env.state(|| T::Api::portfolio_loan(POOL_A, loan_id).unwrap()); - env.state_mut(|| { + let loan_portfolio = env.parachain_state(|| T::Api::portfolio_loan(POOL_A, loan_id).unwrap()); + env.parachain_state_mut(|| { // Give required tokens to the borrower to be able to repay the interest accrued // until this moment utils::give_tokens::(BORROWER.id(), Usd6::ID, loan_portfolio.outstanding_interest); @@ -292,9 +293,9 @@ fn internal_priced() { fn oracle_priced() { let mut env = common::initialize_state_for_loans::, T>(); - env.state_mut(|| utils::feed_oracle::(vec![(PRICE_A, PRICE_VALUE_A)])); + env.parachain_state_mut(|| utils::feed_oracle::(vec![(PRICE_A, PRICE_VALUE_A)])); - let info = env.state(|| { + let info = env.parachain_state(|| { let now = as TimeAsSecs>::now(); common::default_loan_info::(now, common::default_external_pricing()) }); @@ -307,8 +308,8 @@ fn oracle_priced() { env.pass(Blocks::BySeconds(SECONDS_PER_MINUTE / 2)); - let loan_portfolio = env.state(|| T::Api::portfolio_loan(POOL_A, loan_id).unwrap()); - env.state_mut(|| { + let loan_portfolio = env.parachain_state(|| T::Api::portfolio_loan(POOL_A, loan_id).unwrap()); + env.parachain_state_mut(|| { // Give required tokens to the borrower to be able to repay the interest accrued // until this moment utils::give_tokens::(BORROWER.id(), Usd6::ID, loan_portfolio.outstanding_interest); @@ -330,7 +331,7 @@ fn oracle_priced() { fn update_maturity_extension() { let mut env = common::initialize_state_for_loans::, T>(); - let info = env.state(|| { + let info = env.parachain_state(|| { let now = as TimeAsSecs>::now(); common::default_loan_info::(now, common::default_internal_pricing()) }); diff --git a/runtime/integration-tests/src/generic/config.rs b/runtime/integration-tests/src/generic/config.rs index a25c5184ff..f52a6cbe0e 100644 --- a/runtime/integration-tests/src/generic/config.rs +++ b/runtime/integration-tests/src/generic/config.rs @@ -91,7 +91,10 @@ pub trait Runtime: Balance = Balance, NativeFungible = pallet_balances::Pallet, > + cumulus_pallet_parachain_system::Config + + parachain_info::Config + orml_oracle::Config + + orml_xtokens::Config + + pallet_xcm::Config { /// Just the RuntimeCall type, but redefined with extra bounds. /// You can add `From` bounds in order to convert pallet calls to diff --git a/runtime/integration-tests/src/generic/env.rs b/runtime/integration-tests/src/generic/env.rs index 1d37a6b0aa..5118d6deca 100644 --- a/runtime/integration-tests/src/generic/env.rs +++ b/runtime/integration-tests/src/generic/env.rs @@ -4,7 +4,7 @@ use codec::Encode; use sp_runtime::{ generic::{Era, SignedPayload}, traits::{Block, Extrinsic}, - DispatchError, DispatchResult, MultiSignature, Storage, + DispatchError, MultiSignature, Storage, }; use sp_std::ops::Range; @@ -61,7 +61,7 @@ impl Blocks { /// Define an environment behavior pub trait Env { /// Load the environment from a storage - fn from_storage(storage: Storage) -> Self; + fn from_storage(parachain_storage: Storage, sibling_storage: Storage) -> Self; /// Submit an extrinsic mutating the state instantly and returning the /// consumed fee @@ -72,11 +72,15 @@ pub trait Env { ) -> Result; /// Submit an extrinsic mutating the state when the block is finalized - fn submit_later(&mut self, who: Keyring, call: impl Into) -> DispatchResult; + fn submit_later( + &mut self, + who: Keyring, + call: impl Into, + ) -> Result<(), Box>; /// Pass any number of blocks fn pass(&mut self, blocks: Blocks) { - let (current, slot) = self.state(|| { + let (current, slot) = self.parachain_state(|| { ( frame_system::Pallet::::block_number(), pallet_aura::Pallet::::slot_duration().into_seconds(), @@ -94,19 +98,30 @@ pub trait Env { } } - /// Allows to mutate the storage state through the closure - fn state_mut(&mut self, f: impl FnOnce() -> R) -> R; + /// Allows to mutate the relay storage state through the closure. + fn relay_state_mut(&mut self, f: impl FnOnce() -> R) -> R; + + /// Allows to read the relay storage state through the closure. + fn relay_state(&self, f: impl FnOnce() -> R) -> R; + + /// Allows to mutate the parachain storage state through the closure. + fn parachain_state_mut(&mut self, f: impl FnOnce() -> R) -> R; + + /// Allows to read the parachain storage state through the closure. + fn parachain_state(&self, f: impl FnOnce() -> R) -> R; + + /// Allows to mutate the sibling storage state through the closure. + fn sibling_state_mut(&mut self, f: impl FnOnce() -> R) -> R; - /// Allows to read the storage state through the closure - /// If storage is modified, it would not be applied. - fn state(&self, f: impl FnOnce() -> R) -> R; + /// Allows to read the sibling storage state through the closure. + fn sibling_state(&self, f: impl FnOnce() -> R) -> R; /// Check for an exact event introduced in the current block. /// Starting from last event introduced /// Returns an Option to unwrap it from the tests and have good panic /// message with the error test line fn check_event(&self, event: impl Into) -> Option<()> { - self.state(|| { + self.parachain_state(|| { let event = event.into(); frame_system::Pallet::::events() .into_iter() @@ -124,7 +139,7 @@ pub trait Env { where T::RuntimeEventExt: TryInto, { - self.state(|| { + self.parachain_state(|| { frame_system::Pallet::::events() .into_iter() .rev() diff --git a/runtime/integration-tests/src/generic/envs/fudge_env.rs b/runtime/integration-tests/src/generic/envs/fudge_env.rs index e0d494dc9e..97a9294147 100644 --- a/runtime/integration-tests/src/generic/envs/fudge_env.rs +++ b/runtime/integration-tests/src/generic/envs/fudge_env.rs @@ -8,7 +8,7 @@ use handle::{FudgeHandle, ParachainClient}; use sc_client_api::HeaderBackend; use sp_api::{ApiRef, ProvideRuntimeApi}; use sp_core::H256; -use sp_runtime::{DispatchError, DispatchResult, Storage}; +use sp_runtime::{DispatchError, Storage}; use crate::{ generic::{ @@ -31,8 +31,9 @@ pub struct FudgeEnv { } impl Env for FudgeEnv { - fn from_storage(storage: Storage) -> Self { - let mut handle = T::FudgeHandle::new(Storage::default(), storage); + fn from_storage(parachain_storage: Storage, sibling_storage: Storage) -> Self { + let mut handle = + T::FudgeHandle::new(Storage::default(), parachain_storage, sibling_storage); handle.evolve(); @@ -50,35 +51,51 @@ impl Env for FudgeEnv { unimplemented!("FudgeEnv does not support submit_now() try submit_later()") } - fn submit_later(&mut self, who: Keyring, call: impl Into) -> DispatchResult { + fn submit_later( + &mut self, + who: Keyring, + call: impl Into, + ) -> Result<(), Box> { let nonce = *self.nonce_storage.entry(who).or_default(); - let extrinsic = self.state(|| utils::create_extrinsic::(who, call, nonce)); + let extrinsic = self.parachain_state(|| utils::create_extrinsic::(who, call, nonce)); self.handle .parachain_mut() .append_extrinsic(extrinsic) - .map(|_| ()) - .map_err(|_| { - DispatchError::Other("Specific kind of DispatchError not supported by fudge now") - // More information, issue: https://github.com/centrifuge/fudge/issues/67 - })?; + .map(|_| ())?; self.nonce_storage.insert(who, nonce + 1); Ok(()) } - fn state_mut(&mut self, f: impl FnOnce() -> R) -> R { + fn relay_state_mut(&mut self, f: impl FnOnce() -> R) -> R { + self.handle.relay_mut().with_mut_state(f).unwrap() + } + + fn relay_state(&self, f: impl FnOnce() -> R) -> R { + self.handle.relay().with_state(f).unwrap() + } + + fn parachain_state_mut(&mut self, f: impl FnOnce() -> R) -> R { self.handle.parachain_mut().with_mut_state(f).unwrap() } - fn state(&self, f: impl FnOnce() -> R) -> R { + fn parachain_state(&self, f: impl FnOnce() -> R) -> R { self.handle.parachain().with_state(f).unwrap() } + fn sibling_state_mut(&mut self, f: impl FnOnce() -> R) -> R { + self.handle.sibling_mut().with_mut_state(f).unwrap() + } + + fn sibling_state(&self, f: impl FnOnce() -> R) -> R { + self.handle.sibling().with_state(f).unwrap() + } + fn __priv_build_block(&mut self, i: BlockNumber) { - let current = self.state(|| frame_system::Pallet::::block_number()); + let current = self.parachain_state(|| frame_system::Pallet::::block_number()); if i > current + 1 { panic!("Jump to future blocks is unsupported in fudge (maybe you've used Blocks::BySecondsFast?)"); } @@ -129,6 +146,7 @@ mod tests { balances: vec![(Keyring::Alice.to_account_id(), 1 * CFG)], }) .storage(), + Genesis::::default().storage(), ); env.submit_later( diff --git a/runtime/integration-tests/src/generic/envs/fudge_env/handle.rs b/runtime/integration-tests/src/generic/envs/fudge_env/handle.rs index 40da6557ec..461bd6df1b 100644 --- a/runtime/integration-tests/src/generic/envs/fudge_env/handle.rs +++ b/runtime/integration-tests/src/generic/envs/fudge_env/handle.rs @@ -1,6 +1,7 @@ use std::sync::Arc; -use cfg_primitives::{AuraId, BlockNumber}; +use cfg_primitives::{AuraId, Balance, BlockNumber}; +use cumulus_primitives_core::CollectCollationInfo; use frame_support::traits::GenesisBuild; use fudge::{ digest::{DigestCreator as DigestCreatorT, DigestProvider, FudgeAuraDigest, FudgeBabeDigest}, @@ -15,6 +16,7 @@ use fudge::{ use polkadot_core_primitives::{Block as RelayBlock, Header as RelayHeader}; use polkadot_parachain::primitives::Id as ParaId; use polkadot_primitives::runtime_api::ParachainHost; +use polkadot_runtime_parachains::configuration::HostConfiguration; use sc_block_builder::BlockBuilderApi; use sc_client_api::Backend; use sc_service::{TFullBackend, TFullClient}; @@ -22,12 +24,12 @@ use sp_api::{ApiExt, ConstructRuntimeApi}; use sp_consensus_aura::{sr25519::AuthorityId, AuraApi}; use sp_consensus_babe::BabeApi; use sp_consensus_slots::SlotDuration; -use sp_core::H256; -use sp_runtime::Storage; +use sp_core::{crypto::AccountId32, ByteArray, H256}; +use sp_runtime::{traits::AccountIdLookup, Storage}; use sp_transaction_pool::runtime_api::TaggedTransactionQueue; use tokio::runtime::Handle; -use crate::{generic::config::Runtime, utils::time::START_DATE}; +use crate::{chain::centrifuge::Header, generic::config::Runtime, utils::time::START_DATE}; type InherentCreator = Box< dyn CreateInherentDataProviders< @@ -74,10 +76,17 @@ pub type RelayClient = TFullClient = TFullClient; pub trait FudgeHandle { - type RelayRuntime: frame_system::Config - + polkadot_runtime_parachains::paras::Config + type RelayRuntime: frame_system::Config< + BlockNumber = BlockNumber, + AccountId = AccountId32, + Lookup = AccountIdLookup, + > + polkadot_runtime_parachains::paras::Config + polkadot_runtime_parachains::session_info::Config - + polkadot_runtime_parachains::initializer::Config; + + polkadot_runtime_parachains::initializer::Config + + polkadot_runtime_parachains::hrmp::Config + + pallet_session::Config + + pallet_xcm::Config + + pallet_balances::Config; type RelayConstructApi: ConstructRuntimeApi< RelayBlock, @@ -104,11 +113,14 @@ pub trait FudgeHandle { type ParachainApi: BlockBuilderApi + ApiExt as Backend>::State> + AuraApi - + TaggedTransactionQueue; + + TaggedTransactionQueue + + CollectCollationInfo; const RELAY_CODE: Option<&'static [u8]>; const PARACHAIN_CODE: Option<&'static [u8]>; + const PARA_ID: u32; + const SIBLING_ID: u32; fn relay(&self) -> &RelaychainBuilder; fn relay_mut(&mut self) -> &mut RelaychainBuilder; @@ -116,37 +128,85 @@ pub trait FudgeHandle { fn parachain(&self) -> &ParachainBuilder; fn parachain_mut(&mut self) -> &mut ParachainBuilder; - fn append_extrinsic(&mut self, chain: Chain, extrinsic: Vec) -> Result<(), ()>; + fn sibling(&self) -> &ParachainBuilder; + fn sibling_mut(&mut self) -> &mut ParachainBuilder; + + fn append_extrinsic( + &mut self, + chain: Chain, + extrinsic: Vec, + ) -> Result<(), Box>; fn with_state(&self, chain: Chain, f: impl FnOnce() -> R) -> R; fn with_mut_state(&mut self, chain: Chain, f: impl FnOnce() -> R) -> R; fn evolve(&mut self); - fn new(relay_storage: Storage, parachain_storage: Storage) -> Self; + fn new(relay_storage: Storage, parachain_storage: Storage, sibling_storage: Storage) -> Self; fn new_relay_builder( storage: Storage, + session_keys: ::Keys, ) -> RelaychainBuilder { sp_tracing::enter_span!(sp_tracing::Level::INFO, "Relay - StartUp"); let code = Self::RELAY_CODE.expect("ESSENTIAL: WASM is built."); - let mut state = StateProvider::new(code); - - state.insert_storage( - polkadot_runtime_parachains::configuration::GenesisConfig::::default() + let mut state = + StateProvider::, RelayBlock>::empty_default(Some(code)) + .expect("ESSENTIAL: State provider can be created."); + + let mut configuration = polkadot_runtime_parachains::configuration::GenesisConfig::< + Self::RelayRuntime, + >::default(); + + let mut host_config = HostConfiguration::::default(); + host_config.max_downward_message_size = 1024; + host_config.hrmp_channel_max_capacity = 100; + host_config.hrmp_channel_max_message_size = 1024; + host_config.hrmp_channel_max_total_size = 1024; + host_config.hrmp_max_parachain_outbound_channels = 10; + host_config.hrmp_max_parachain_inbound_channels = 10; + host_config.hrmp_max_message_num_per_candidate = 100; + host_config.max_upward_queue_count = 10; + host_config.max_upward_queue_size = 1024; + host_config.max_upward_message_size = 1024; + host_config.max_upward_message_num_per_candidate = 100; + + configuration.config = host_config; + + state + .insert_storage( + configuration + .build_storage() + .expect("ESSENTIAL: GenesisBuild must not fail at this stage."), + ) + .expect("ESSENTIAL: Storage can be inserted"); + + state + .insert_storage( + frame_system::GenesisConfig { + code: code.to_vec(), + } + .build_storage::() + .expect("ESSENTIAL: GenesisBuild must not fail at this stage."), + ) + .expect("ESSENTIAL: Storage can be inserted"); + state + .insert_storage( + pallet_session::GenesisConfig:: { + keys: vec![( + AccountId32::from_slice([0u8; 32].as_slice()).unwrap(), + AccountId32::from_slice([0u8; 32].as_slice()).unwrap(), + session_keys, + )], + } .build_storage() - .expect("ESSENTIAL: GenesisBuild must not fail at this stage.") - ); - - state.insert_storage( - frame_system::GenesisConfig { - code: code.to_vec(), - } - .build_storage::() - .expect("ESSENTIAL: GenesisBuild must not fail at this stage."), - ); + .unwrap(), + ) + .unwrap(); - state.insert_storage(storage); + state + .insert_storage(storage) + .expect("ESSENTIAL: Storage can be inserted"); let mut init = fudge::initiator::default(Handle::current()); init.with_genesis(Box::new(state)); @@ -155,7 +215,8 @@ pub trait FudgeHandle { let instance_id = FudgeInherentTimestamp::create_instance( std::time::Duration::from_secs(6), Some(std::time::Duration::from_millis(START_DATE)), - ); + ) + .expect("ESSENTiAL: Instance can be created."); Box::new(move |parent: H256, ()| { let client = client.clone(); @@ -180,50 +241,71 @@ pub trait FudgeHandle { }) }; - let dp: DigestCreator = Box::new(move |parent, inherents| async move { + let dp: DigestCreator = Box::new(move |parent: Header, inherents| async move { let babe = FudgeBabeDigest::::new(); - let digest = babe.build_digest(&parent, &inherents).await?; + let digest = babe.build_digest(parent.clone(), &inherents).await?; + Ok(digest) }); RelaychainBuilder::new(init, |client| (cidp(client), dp)) + .expect("ESSENTIAL: Relaychain Builder can be created.") } fn new_parachain_builder( + para_id: ParaId, relay: &RelaychainBuilder, storage: Storage, ) -> ParachainBuilder { - sp_tracing::enter_span!(sp_tracing::Level::INFO, "Centrifuge - StartUp"); + sp_tracing::enter_span!(sp_tracing::Level::INFO, "Parachain - StartUp"); let code = Self::PARACHAIN_CODE.expect("ESSENTIAL: WASM is built."); - let mut state = StateProvider::new(code); - - state.insert_storage( - frame_system::GenesisConfig { - code: code.to_vec(), - } - .build_storage::() - .expect("ESSENTIAL: GenesisBuild must not fail at this stage."), - ); - state.insert_storage( - pallet_aura::GenesisConfig:: { - authorities: vec![AuraId::from(sp_core::sr25519::Public([0u8; 32]))], - } - .build_storage() - .expect("ESSENTIAL: GenesisBuild must not fail at this stage."), - ); - - state.insert_storage(storage); + let mut state = + StateProvider::, T::Block>::empty_default(Some(code)) + .expect("ESSENTIAL: State provider can be created."); + + state + .insert_storage( + frame_system::GenesisConfig { + code: code.to_vec(), + } + .build_storage::() + .expect("ESSENTIAL: GenesisBuild must not fail at this stage."), + ) + .expect("ESSENTIAL: Storage can be inserted"); + state + .insert_storage( + pallet_aura::GenesisConfig:: { + authorities: vec![AuraId::from(sp_core::sr25519::Public([0u8; 32]))], + } + .build_storage() + .expect("ESSENTIAL: GenesisBuild must not fail at this stage."), + ) + .expect("ESSENTIAL: Storage can be inserted"); + state + .insert_storage( + >::build_storage( + ¶chain_info::GenesisConfig { + parachain_id: para_id, + }, + ) + .expect("ESSENTIAL: Parachain Info GenesisBuild must not fail at this stage."), + ) + .expect("ESSENTIAL: Storage can be inserted"); + + state + .insert_storage(storage) + .expect("ESSENTIAL: Storage can be inserted"); let mut init = fudge::initiator::default(Handle::current()); init.with_genesis(Box::new(state)); - let para_id = ParaId::from(Self::PARA_ID); let inherent_builder = relay.inherent_builder(para_id.clone()); let instance_id = FudgeInherentTimestamp::create_instance( std::time::Duration::from_secs(12), Some(std::time::Duration::from_millis(START_DATE)), - ); + ) + .expect("ESSENTIAL: Instance can be created."); let cidp = Box::new(move |_parent: H256, ()| { let inherent_builder_clone = inherent_builder.clone(); @@ -243,21 +325,24 @@ pub trait FudgeHandle { }); let dp = |clone_client: Arc>| { - Box::new(move |parent, inherents| { + Box::new(move |parent: Header, inherents| { let client = clone_client.clone(); async move { let aura = FudgeAuraDigest::< T::Block, ParachainClient, - >::new(&*client); + >::new(&*client) + .expect("ESSENTIAL: Aura digest can be created."); + + let digest = aura.build_digest(parent.clone(), &inherents).await?; - let digest = aura.build_digest(&parent, &inherents).await?; Ok(digest) } }) }; - ParachainBuilder::new(init, |client| (cidp, dp(client))) + ParachainBuilder::new(para_id, init, |client| (cidp, dp(client))) + .expect("ESSENTIAL: Parachain Builder can be created.") } } diff --git a/runtime/integration-tests/src/generic/envs/runtime_env.rs b/runtime/integration-tests/src/generic/envs/runtime_env.rs index 28096dc3e7..8d7e7ca7f9 100644 --- a/runtime/integration-tests/src/generic/envs/runtime_env.rs +++ b/runtime/integration-tests/src/generic/envs/runtime_env.rs @@ -13,7 +13,7 @@ use sp_api::runtime_decl_for_core::CoreV4; use sp_block_builder::runtime_decl_for_block_builder::BlockBuilderV6; use sp_consensus_aura::{Slot, AURA_ENGINE_ID}; use sp_core::{sr25519::Public, H256}; -use sp_runtime::{traits::Extrinsic, Digest, DigestItem, DispatchError, DispatchResult, Storage}; +use sp_runtime::{traits::Extrinsic, Digest, DigestItem, DispatchError, Storage}; use sp_timestamp::Timestamp; use crate::{ @@ -24,29 +24,42 @@ use crate::{ utils::accounts::Keyring, }; -/// Evironment that interact directly with the runtime, +/// Environment that interact directly with the runtime, /// without the usage of a client. pub struct RuntimeEnv { - ext: Rc>, + parachain_ext: Rc>, + sibling_ext: Rc>, pending_extrinsics: Vec<(Keyring, T::RuntimeCallExt)>, _config: PhantomData, } impl Env for RuntimeEnv { - fn from_storage(mut storage: Storage) -> Self { + fn from_storage(mut parachain_storage: Storage, mut sibling_storage: Storage) -> Self { // Needed for the aura usage pallet_aura::GenesisConfig:: { authorities: vec![AuraId::from(Public([0; 32]))], } - .assimilate_storage(&mut storage) + .assimilate_storage(&mut parachain_storage) .unwrap(); - let mut ext = sp_io::TestExternalities::new(storage); + let mut parachain_ext = sp_io::TestExternalities::new(parachain_storage); - ext.execute_with(|| Self::prepare_block(1)); + parachain_ext.execute_with(|| Self::prepare_block(1)); + + // Needed for the aura usage + pallet_aura::GenesisConfig:: { + authorities: vec![AuraId::from(Public([0; 32]))], + } + .assimilate_storage(&mut sibling_storage) + .unwrap(); + + let mut sibling_ext = sp_io::TestExternalities::new(sibling_storage); + + sibling_ext.execute_with(|| Self::prepare_block(1)); Self { - ext: Rc::new(RefCell::new(ext)), + parachain_ext: Rc::new(RefCell::new(parachain_ext)), + sibling_ext: Rc::new(RefCell::new(sibling_ext)), pending_extrinsics: Vec::default(), _config: PhantomData, } @@ -57,12 +70,12 @@ impl Env for RuntimeEnv { who: Keyring, call: impl Into, ) -> Result { - let extrinsic = self.state(|| { + let extrinsic = self.parachain_state(|| { let nonce = frame_system::Pallet::::account(who.to_account_id()).nonce; utils::create_extrinsic::(who, call, nonce) }); - self.state_mut(|| T::Api::apply_extrinsic(extrinsic).unwrap())?; + self.parachain_state_mut(|| T::Api::apply_extrinsic(extrinsic).unwrap())?; let fee = self .find_event(|e| match e { @@ -76,17 +89,45 @@ impl Env for RuntimeEnv { Ok(fee) } - fn submit_later(&mut self, who: Keyring, call: impl Into) -> DispatchResult { + fn submit_later( + &mut self, + who: Keyring, + call: impl Into, + ) -> Result<(), Box> { self.pending_extrinsics.push((who, call.into())); Ok(()) } - fn state_mut(&mut self, f: impl FnOnce() -> R) -> R { - self.ext.borrow_mut().execute_with(f) + fn relay_state_mut(&mut self, _f: impl FnOnce() -> R) -> R { + unimplemented!("Mutable relay state not implemented for RuntimeEnv") + } + + fn relay_state(&self, _f: impl FnOnce() -> R) -> R { + unimplemented!("Relay state not implemented for RuntimeEnv") + } + + fn parachain_state_mut(&mut self, f: impl FnOnce() -> R) -> R { + self.parachain_ext.borrow_mut().execute_with(f) + } + + fn parachain_state(&self, f: impl FnOnce() -> R) -> R { + self.parachain_ext.borrow_mut().execute_with(|| { + let version = frame_support::StateVersion::V1; + let hash = frame_support::storage_root(version); + + let result = f(); + + assert_eq!(hash, frame_support::storage_root(version)); + result + }) + } + + fn sibling_state_mut(&mut self, f: impl FnOnce() -> R) -> R { + self.sibling_ext.borrow_mut().execute_with(f) } - fn state(&self, f: impl FnOnce() -> R) -> R { - self.ext.borrow_mut().execute_with(|| { + fn sibling_state(&self, f: impl FnOnce() -> R) -> R { + self.sibling_ext.borrow_mut().execute_with(|| { let version = frame_support::StateVersion::V1; let hash = frame_support::storage_root(version); @@ -99,7 +140,7 @@ impl Env for RuntimeEnv { fn __priv_build_block(&mut self, i: BlockNumber) { self.process_pending_extrinsics(); - self.state_mut(|| { + self.parachain_state_mut(|| { T::Api::finalize_block(); Self::prepare_block(i); }); @@ -111,12 +152,12 @@ impl RuntimeEnv { let pending_extrinsics = mem::replace(&mut self.pending_extrinsics, Vec::default()); for (who, call) in pending_extrinsics { - let extrinsic = self.state(|| { + let extrinsic = self.parachain_state(|| { let nonce = frame_system::Pallet::::account(who.to_account_id()).nonce; utils::create_extrinsic::(who, call, nonce) }); - self.state_mut(|| T::Api::apply_extrinsic(extrinsic).unwrap().unwrap()); + self.parachain_state_mut(|| T::Api::apply_extrinsic(extrinsic).unwrap().unwrap()); } } @@ -206,6 +247,7 @@ mod tests { balances: vec![(Keyring::Alice.to_account_id(), 1 * CFG)], }) .storage(), + Genesis::::default().storage(), ); env.submit_now( @@ -228,6 +270,7 @@ mod tests { balances: vec![(Keyring::Alice.to_account_id(), 1 * CFG)], }) .storage(), + Genesis::::default().storage(), ); env.submit_later( diff --git a/runtime/integration-tests/src/generic/impls.rs b/runtime/integration-tests/src/generic/impls.rs index a58d2e47bd..933b1a952e 100644 --- a/runtime/integration-tests/src/generic/impls.rs +++ b/runtime/integration-tests/src/generic/impls.rs @@ -1,3 +1,6 @@ +use polkadot_primitives::{AssignmentId, AuthorityDiscoveryId, ValidatorId}; +use sp_core::ByteArray; + /// Implements the `Runtime` trait for a runtime macro_rules! impl_runtime { ($runtime_path:ident, $kind:ident) => { @@ -26,11 +29,13 @@ macro_rules! impl_fudge_support { ( $fudge_companion_type:ident, $relay_path:ident, + $relay_session_keys:expr, $parachain_path:ident, - $parachain_id:literal + $parachain_id:literal, + $sibling_id:literal ) => { const _: () = { - use fudge::primitives::Chain; + use fudge::primitives::{Chain, ParaId}; use polkadot_core_primitives::Block as RelayBlock; use sp_api::ConstructRuntimeApi; use sp_runtime::Storage; @@ -50,6 +55,9 @@ macro_rules! impl_fudge_support { #[fudge::parachain($parachain_id)] pub parachain: ParachainBuilder<$parachain_path::Block, $parachain_path::RuntimeApi>, + + #[fudge::parachain($sibling_id)] + pub sibling: ParachainBuilder<$parachain_path::Block, $parachain_path::RuntimeApi>, } // Implement for T only one time when fudge::companion @@ -70,12 +78,26 @@ macro_rules! impl_fudge_support { const PARACHAIN_CODE: Option<&'static [u8]> = $parachain_path::WASM_BINARY; const PARA_ID: u32 = $parachain_id; const RELAY_CODE: Option<&'static [u8]> = $relay_path::WASM_BINARY; - - fn new(relay_storage: Storage, parachain_storage: Storage) -> Self { - let relay = Self::new_relay_builder(relay_storage); - let parachain = Self::new_parachain_builder(&relay, parachain_storage); - - Self::new(relay, parachain).unwrap() + const SIBLING_ID: u32 = $sibling_id; + + fn new( + relay_storage: Storage, + parachain_storage: Storage, + sibling_storage: Storage, + ) -> Self { + let relay = Self::new_relay_builder(relay_storage, $relay_session_keys); + let parachain = Self::new_parachain_builder( + ParaId::from(Self::PARA_ID), + &relay, + parachain_storage, + ); + let sibling = Self::new_parachain_builder( + ParaId::from(Self::SIBLING_ID), + &relay, + sibling_storage, + ); + + Self::new(relay, parachain, sibling).unwrap() } fn relay(&self) -> &RelaychainBuilder { @@ -100,8 +122,25 @@ macro_rules! impl_fudge_support { &mut self.parachain } - fn append_extrinsic(&mut self, chain: Chain, extrinsic: Vec) -> Result<(), ()> { + fn sibling( + &self, + ) -> &ParachainBuilder<$parachain_path::Block, Self::ParachainConstructApi> { + &self.sibling + } + + fn sibling_mut( + &mut self, + ) -> &mut ParachainBuilder<$parachain_path::Block, Self::ParachainConstructApi> { + &mut self.sibling + } + + fn append_extrinsic( + &mut self, + chain: Chain, + extrinsic: Vec, + ) -> Result<(), Box> { self.append_extrinsic(chain, extrinsic) + .map_err(|e| e.into()) } fn with_state(&self, chain: Chain, f: impl FnOnce() -> R) -> R { @@ -124,6 +163,66 @@ macro_rules! impl_fudge_support { }; } -impl_fudge_support!(FudgeDevelopment, rococo_runtime, development_runtime, 2000); -impl_fudge_support!(FudgeAltair, kusama_runtime, altair_runtime, 2088); -impl_fudge_support!(CentrifugeAltair, polkadot_runtime, centrifuge_runtime, 2031); +impl_fudge_support!( + FudgeDevelopment, + rococo_runtime, + default_rococo_session_keys(), + development_runtime, + 2000, + 2001 +); + +impl_fudge_support!( + FudgeAltair, + kusama_runtime, + default_kusama_session_keys(), + altair_runtime, + 2088, + 2089 +); + +impl_fudge_support!( + FudgeCentrifuge, + polkadot_runtime, + default_polkadot_session_keys(), + centrifuge_runtime, + 2031, + 2032 +); + +pub fn default_rococo_session_keys() -> rococo_runtime::SessionKeys { + rococo_runtime::SessionKeys { + grandpa: pallet_grandpa::AuthorityId::from_slice([0u8; 32].as_slice()).unwrap(), + babe: pallet_babe::AuthorityId::from_slice([0u8; 32].as_slice()).unwrap(), + im_online: pallet_im_online::sr25519::AuthorityId::from_slice([0u8; 32].as_slice()) + .unwrap(), + para_validator: ValidatorId::from_slice([0u8; 32].as_slice()).unwrap(), + para_assignment: AssignmentId::from_slice([0u8; 32].as_slice()).unwrap(), + authority_discovery: AuthorityDiscoveryId::from_slice([0u8; 32].as_slice()).unwrap(), + beefy: sp_consensus_beefy::crypto::AuthorityId::from_slice([0u8; 33].as_slice()).unwrap(), + } +} + +pub fn default_kusama_session_keys() -> kusama_runtime::SessionKeys { + kusama_runtime::SessionKeys { + grandpa: pallet_grandpa::AuthorityId::from_slice([0u8; 32].as_slice()).unwrap(), + babe: pallet_babe::AuthorityId::from_slice([0u8; 32].as_slice()).unwrap(), + im_online: pallet_im_online::sr25519::AuthorityId::from_slice([0u8; 32].as_slice()) + .unwrap(), + para_validator: ValidatorId::from_slice([0u8; 32].as_slice()).unwrap(), + para_assignment: AssignmentId::from_slice([0u8; 32].as_slice()).unwrap(), + authority_discovery: AuthorityDiscoveryId::from_slice([0u8; 32].as_slice()).unwrap(), + } +} + +pub fn default_polkadot_session_keys() -> polkadot_runtime::SessionKeys { + polkadot_runtime::SessionKeys { + grandpa: pallet_grandpa::AuthorityId::from_slice([0u8; 32].as_slice()).unwrap(), + babe: pallet_babe::AuthorityId::from_slice([0u8; 32].as_slice()).unwrap(), + im_online: pallet_im_online::sr25519::AuthorityId::from_slice([0u8; 32].as_slice()) + .unwrap(), + para_validator: ValidatorId::from_slice([0u8; 32].as_slice()).unwrap(), + para_assignment: AssignmentId::from_slice([0u8; 32].as_slice()).unwrap(), + authority_discovery: AuthorityDiscoveryId::from_slice([0u8; 32].as_slice()).unwrap(), + } +} diff --git a/runtime/integration-tests/src/generic/mod.rs b/runtime/integration-tests/src/generic/mod.rs index 5ead789c2e..4685a67ab9 100644 --- a/runtime/integration-tests/src/generic/mod.rs +++ b/runtime/integration-tests/src/generic/mod.rs @@ -15,6 +15,7 @@ pub mod utils; // Test cases mod cases { mod example; + mod liquidity_pools; mod loans; } @@ -60,6 +61,8 @@ macro_rules! test_for_runtimes { $( #[tokio::test] async fn $runtime_name() { + crate::utils::logs::init_logs(); + $test_name::<$runtime_name::Runtime>() } )* diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/development/mod.rs b/runtime/integration-tests/src/liquidity_pools/pallet/development/mod.rs index 17909ac383..e42269b27a 100644 --- a/runtime/integration-tests/src/liquidity_pools/pallet/development/mod.rs +++ b/runtime/integration-tests/src/liquidity_pools/pallet/development/mod.rs @@ -1,4 +1,3 @@ mod setup; -mod test_net; mod tests; mod transfers; diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/development/setup.rs b/runtime/integration-tests/src/liquidity_pools/pallet/development/setup.rs index 4530a5807c..10e3c66dd8 100644 --- a/runtime/integration-tests/src/liquidity_pools/pallet/development/setup.rs +++ b/runtime/integration-tests/src/liquidity_pools/pallet/development/setup.rs @@ -16,90 +16,7 @@ use cfg_types::{domain_address::Domain, tokens::CustomMetadata}; use frame_support::traits::GenesisBuild; use orml_traits::asset_registry::AssetMetadata; -/// Accounts -pub const ALICE: [u8; 32] = [4u8; 32]; -pub const BOB: [u8; 32] = [5u8; 32]; -pub const CHARLIE: [u8; 32] = [6u8; 32]; - -pub const TEST_DOMAIN: Domain = Domain::EVM(1284); - -/// A PARA ID used for a sibling parachain emulating Moonbeam. -/// It must be one that doesn't collide with any other in use. -pub const PARA_ID_MOONBEAM: u32 = 2023; - -pub struct ExtBuilder { - balances: Vec<(AccountId, CurrencyId, Balance)>, - parachain_id: u32, -} - -impl Default for ExtBuilder { - fn default() -> Self { - Self { - balances: vec![], - parachain_id: parachains::polkadot::centrifuge::ID, - } - } -} - -impl ExtBuilder { - pub fn balances(mut self, balances: Vec<(AccountId, CurrencyId, Balance)>) -> Self { - self.balances = balances; - self - } - - pub fn parachain_id(mut self, parachain_id: u32) -> Self { - self.parachain_id = parachain_id; - self - } - - pub fn build(self) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default() - .build_storage::() - .unwrap(); - let native_currency_id = development_runtime::NativeToken::get(); - pallet_balances::GenesisConfig:: { - balances: self - .balances - .clone() - .into_iter() - .filter(|(_, currency_id, _)| *currency_id == native_currency_id) - .map(|(account_id, _, initial_balance)| (account_id, initial_balance)) - .collect::>(), - } - .assimilate_storage(&mut t) - .unwrap(); - - orml_tokens::GenesisConfig:: { - balances: self - .balances - .into_iter() - .filter(|(_, currency_id, _)| *currency_id != native_currency_id) - .collect::>(), - } - .assimilate_storage(&mut t) - .unwrap(); - - >::assimilate_storage( - ¶chain_info::GenesisConfig { - parachain_id: self.parachain_id.into(), - }, - &mut t, - ) - .unwrap(); - - >::assimilate_storage( - &pallet_xcm::GenesisConfig { - safe_xcm_version: Some(2), - }, - &mut t, - ) - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext - } -} +use crate::{chain::centrifuge::PARA_ID, utils::env::PARA_ID_SIBLING}; pub fn cfg(amount: Balance) -> Balance { amount * dollar(currency_decimals::NATIVE) @@ -109,12 +26,12 @@ pub fn dollar(decimals: u32) -> Balance { 10u128.saturating_pow(decimals) } -pub fn moonbeam_account() -> AccountId { - parachain_account(PARA_ID_MOONBEAM) +pub fn centrifuge_account() -> AccountId { + parachain_account(PARA_ID) } -pub fn centrifuge_account() -> AccountId { - parachain_account(parachains::polkadot::centrifuge::ID) +pub fn sibling_account() -> AccountId { + parachain_account(PARA_ID_SIBLING) } fn parachain_account(id: u32) -> AccountId { diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/development/test_net.rs b/runtime/integration-tests/src/liquidity_pools/pallet/development/test_net.rs deleted file mode 100644 index b899fd6f33..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/development/test_net.rs +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2021 Centrifuge GmbH (centrifuge.io). -// This file is part of Centrifuge chain project. - -// Centrifuge 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 (see http://www.gnu.org/licenses). - -// Centrifuge 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. - -//! Relay chain and parachains emulation. - -use cfg_primitives::{parachains, AccountId}; -use cfg_types::tokens::CurrencyId; -use cumulus_primitives_core::ParaId; -use frame_support::{traits::GenesisBuild, weights::Weight}; -use polkadot_primitives::{BlockNumber, MAX_CODE_SIZE, MAX_POV_SIZE}; -use polkadot_runtime_parachains::configuration::HostConfiguration; -use sp_runtime::traits::AccountIdConversion; -use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; - -use super::setup::{cfg, ExtBuilder, ALICE, BOB, PARA_ID_MOONBEAM}; -use crate::utils::{ - AUSD_CURRENCY_ID, AUSD_ED, GLMR_CURRENCY_ID, GLMR_ED, USDT_CURRENCY_ID, USDT_ED, -}; - -decl_test_relay_chain! { - pub struct RelayChain { - Runtime = polkadot_runtime::Runtime, - RuntimeCall = polkadot_runtime::RuntimeCall, - RuntimeEvent = polkadot_runtime::RuntimeEvent, - XcmConfig = polkadot_runtime::xcm_config::XcmConfig, - MessageQueue = polkadot_runtime::MessageQueue, - System = polkadot_runtime::System, - new_ext = relay_ext(), - } -} - -decl_test_parachain! { - pub struct Development { - Runtime = development_runtime::Runtime, - XcmpMessageHandler = development_runtime::XcmpQueue, - DmpMessageHandler = development_runtime::DmpQueue, - new_ext = para_ext(parachains::polkadot::centrifuge::ID), - } -} - -decl_test_parachain! { - pub struct Moonbeam { - Runtime = development_runtime::Runtime, - XcmpMessageHandler = development_runtime::XcmpQueue, - DmpMessageHandler = development_runtime::DmpQueue, - new_ext = para_ext(PARA_ID_MOONBEAM), - } -} - -decl_test_network! { - pub struct TestNet { - relay_chain = RelayChain, - parachains = vec![ - // N.B: Ideally, we could use the defined para id constants but doing so - // fails with: "error: arbitrary expressions aren't allowed in patterns" - - // Be sure to use `parachains::polkadot::centrifuge::ID` - (2031, Development), - // Be sure to use `PARA_ID_MOONBEAM` - (2023, Moonbeam), - ], - } -} - -pub fn relay_ext() -> sp_io::TestExternalities { - use polkadot_runtime::{Runtime, System}; - - let mut t = frame_system::GenesisConfig::default() - .build_storage::() - .unwrap(); - - pallet_balances::GenesisConfig:: { - balances: vec![ - (AccountId::from(ALICE), cfg(2002)), - ( - ParaId::from(parachains::polkadot::centrifuge::ID).into_account_truncating(), - cfg(7), - ), - ( - ParaId::from(PARA_ID_MOONBEAM).into_account_truncating(), - cfg(7), - ), - ], - } - .assimilate_storage(&mut t) - .unwrap(); - - polkadot_runtime_parachains::configuration::GenesisConfig:: { - config: default_parachains_host_configuration(), - } - .assimilate_storage(&mut t) - .unwrap(); - - >::assimilate_storage( - &pallet_xcm::GenesisConfig { - safe_xcm_version: Some(2), - }, - &mut t, - ) - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext -} - -pub fn para_ext(parachain_id: u32) -> sp_io::TestExternalities { - ExtBuilder::default() - .balances(vec![ - (AccountId::from(ALICE), CurrencyId::Native, cfg(10_000)), - (AccountId::from(BOB), CurrencyId::Native, cfg(10_000)), - (AccountId::from(ALICE), AUSD_CURRENCY_ID, AUSD_ED), - (AccountId::from(BOB), AUSD_CURRENCY_ID, AUSD_ED), - (AccountId::from(ALICE), USDT_CURRENCY_ID, USDT_ED), - (AccountId::from(BOB), USDT_CURRENCY_ID, USDT_ED), - (AccountId::from(ALICE), GLMR_CURRENCY_ID, GLMR_ED), - (AccountId::from(BOB), GLMR_CURRENCY_ID, GLMR_ED), - ]) - .parachain_id(parachain_id) - .build() -} - -fn default_parachains_host_configuration() -> HostConfiguration { - HostConfiguration { - hrmp_channel_max_capacity: u32::MAX, - hrmp_channel_max_total_size: u32::MAX, - hrmp_max_parachain_inbound_channels: 10, - hrmp_max_parachain_outbound_channels: 10, - hrmp_channel_max_message_size: u32::MAX, - // Changed to avoid aritmetic errors within hrmp_close - max_downward_message_size: 100_000u32, - ..Default::default() - } -} diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/add_allow_upgrade.rs b/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/add_allow_upgrade.rs index 03e209edc5..8d5cb3b9f4 100644 --- a/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/add_allow_upgrade.rs +++ b/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/add_allow_upgrade.rs @@ -36,33 +36,49 @@ use cfg_types::{ ForeignAssetId, }, }; -use development_runtime::{ - LiquidityPools, LocationToAccountId, OrderBook, OrmlAssetRegistry, OrmlTokens, Permissions, - Runtime as DevelopmentRuntime, RuntimeOrigin, System, TreasuryAccount, XTokens, XcmTransactor, -}; +use codec::Encode; use frame_support::{assert_noop, assert_ok, traits::fungibles::Mutate}; +use fudge::primitives::{Chain, PoolState}; use orml_traits::{asset_registry::AssetMetadata, FixedConversionRateProvider, MultiCurrency}; +use polkadot_parachain::primitives::Id; use runtime_common::account_conversion::AccountConverter; use sp_runtime::{ traits::{BadOrigin, Convert, One, Zero}, - BoundedVec, DispatchError, + BoundedVec, DispatchError, Storage, +}; +use tokio::runtime::Handle; +use xcm::{ + latest::MultiLocation, + prelude::XCM_VERSION, + v3::{Junction, Junctions}, + VersionedMultiLocation, }; -use xcm::{latest::MultiLocation, VersionedMultiLocation}; -use xcm_simulator::TestExt; use crate::{ + chain::{ + centrifuge::{ + LiquidityPools, LocationToAccountId, OrderBook, OrmlAssetRegistry, OrmlTokens, + Permissions, Runtime as DevelopmentRuntime, RuntimeCall, RuntimeEvent, RuntimeOrigin, + System, TreasuryAccount, XTokens, XcmTransactor, PARA_ID, + }, + relay::{Runtime as RelayRuntime, RuntimeOrigin as RelayRuntimeOrigin}, + }, liquidity_pools::pallet::development::{ - setup::{dollar, ALICE, BOB}, - test_net::{Development, Moonbeam, RelayChain, TestNet}, + setup::dollar, tests::liquidity_pools::setup::{ asset_metadata, create_ausd_pool, create_currency_pool, enable_liquidity_pool_transferability, get_default_moonbeam_native_token_location, investments::default_tranche_id, liquidity_pools_transferable_multilocation, - setup_pre_requirements, DEFAULT_BALANCE_GLMR, DEFAULT_MOONBEAM_LOCATION, - DEFAULT_POOL_ID, DEFAULT_VALIDITY, + setup_pre_requirements, setup_test_env, DEFAULT_BALANCE_GLMR, DEFAULT_POOL_ID, + DEFAULT_SIBLING_LOCATION, DEFAULT_VALIDITY, }, }, - utils::{AUSD_CURRENCY_ID, GLMR_CURRENCY_ID, MOONBEAM_EVM_CHAIN_ID}, + utils::{ + accounts::Keyring, + env, + env::{ChainState, EventRange, PARA_ID_SIBLING}, + genesis, AUSD_CURRENCY_ID, GLMR_CURRENCY_ID, MOONBEAM_EVM_CHAIN_ID, + }, }; /// NOTE: We can't actually verify that the messages hits the @@ -76,17 +92,23 @@ use crate::{ /// be executed on Moonbeam. /// Verify that `LiquidityPools::add_pool` succeeds when called with all the /// necessary requirements. -#[test] -fn add_pool() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); +#[tokio::test] +async fn add_pool() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; // Verify that the pool must exist before we can call LiquidityPools::add_pool assert_noop!( LiquidityPools::add_pool( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), pool_id, Domain::EVM(MOONBEAM_EVM_CHAIN_ID), ), @@ -99,7 +121,7 @@ fn add_pool() { // Verify ALICE can't call `add_pool` given she is not the `PoolAdmin` assert_noop!( LiquidityPools::add_pool( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), pool_id, Domain::EVM(MOONBEAM_EVM_CHAIN_ID), ), @@ -108,7 +130,7 @@ fn add_pool() { // Verify that it works if it's BOB calling it (the pool admin) assert_ok!(LiquidityPools::add_pool( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), pool_id, Domain::EVM(MOONBEAM_EVM_CHAIN_ID), )); @@ -121,13 +143,17 @@ fn add_pool() { /// heavy e2e setup to emulate. Instead, here we test that we can send the /// extrinsic and we have other unit tests verifying the encoding of the remote /// EVM call to be executed on Moonbeam. -#[test] -fn add_tranche() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); - let decimals: u8 = 15; +#[tokio::test] +async fn add_tranche() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { // Now create the pool let pool_id = DEFAULT_POOL_ID; create_ausd_pool(pool_id); @@ -137,7 +163,7 @@ fn add_tranche() { let nonexistent_tranche = [71u8; 16]; assert_noop!( LiquidityPools::add_tranche( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), pool_id, nonexistent_tranche, Domain::EVM(MOONBEAM_EVM_CHAIN_ID), @@ -149,7 +175,7 @@ fn add_tranche() { // Verify ALICE can't call `add_tranche` given she is not the `PoolAdmin` assert_noop!( LiquidityPools::add_tranche( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), pool_id, tranche_id, Domain::EVM(MOONBEAM_EVM_CHAIN_ID), @@ -160,7 +186,7 @@ fn add_tranche() { // Finally, verify we can call LiquidityPools::add_tranche successfully // when called by the PoolAdmin with the right pool + tranche id pair. assert_ok!(LiquidityPools::add_tranche( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), pool_id, tranche_id, Domain::EVM(MOONBEAM_EVM_CHAIN_ID), @@ -171,7 +197,7 @@ fn add_tranche() { orml_asset_registry::Metadata::::remove(tranche_currency_id); assert_noop!( LiquidityPools::update_tranche_token_metadata( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), pool_id, tranche_id, Domain::EVM(MOONBEAM_EVM_CHAIN_ID), @@ -181,15 +207,22 @@ fn add_tranche() { }); } -#[test] -fn update_member() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); +#[tokio::test] +async fn update_member() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + env.with_mut_state(Chain::Para(PARA_ID), || { // Now create the pool let pool_id = DEFAULT_POOL_ID; + create_ausd_pool(pool_id); + let tranche_id = default_tranche_id(pool_id); // Finally, verify we can call LiquidityPools::add_tranche successfully @@ -201,7 +234,7 @@ fn update_member() { assert_ok!(Permissions::add( RuntimeOrigin::root(), Role::PoolRole(PoolRole::PoolAdmin), - ALICE.into(), + Keyring::Alice.into(), PermissionScope::Pool(pool_id), Role::PoolRole(PoolRole::InvestorAdmin), )); @@ -209,7 +242,7 @@ fn update_member() { // Verify it fails if the destination is not whitelisted yet assert_noop!( LiquidityPools::update_member( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), pool_id, tranche_id, new_member.clone(), @@ -220,7 +253,7 @@ fn update_member() { // Whitelist destination as TrancheInvestor of this Pool assert_ok!(Permissions::add( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), Role::PoolRole(PoolRole::InvestorAdmin), AccountConverter::::convert( new_member.clone() @@ -243,7 +276,7 @@ fn update_member() { // Verify it now works assert_ok!(LiquidityPools::update_member( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), pool_id, tranche_id, new_member, @@ -254,7 +287,7 @@ fn update_member() { // beforehand assert_noop!( LiquidityPools::update_member( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), pool_id, tranche_id, DomainAddress::EVM(MOONBEAM_EVM_CHAIN_ID, [9; 20]), @@ -265,19 +298,26 @@ fn update_member() { }); } -#[test] -fn update_token_price() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); - let decimals: u8 = 15; +#[tokio::test] +async fn update_token_price() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let currency_id = AUSD_CURRENCY_ID; let pool_id = DEFAULT_POOL_ID; - create_ausd_pool(pool_id); + enable_liquidity_pool_transferability(currency_id); + create_ausd_pool(pool_id); + assert_ok!(LiquidityPools::update_token_price( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), pool_id, default_tranche_id(pool_id), currency_id, @@ -286,69 +326,79 @@ fn update_token_price() { }); } -#[test] -fn add_currency() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); +#[tokio::test] +async fn add_currency() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_state(Chain::Para(PARA_ID), || { + let gateway_sender = + ::Sender::get(); let currency_id = AUSD_CURRENCY_ID; - // Enable LiquidityPools transferability enable_liquidity_pool_transferability(currency_id); assert_eq!( - OrmlTokens::free_balance( - GLMR_CURRENCY_ID, - &::Sender::get() - ), + OrmlTokens::free_balance(GLMR_CURRENCY_ID, &gateway_sender), DEFAULT_BALANCE_GLMR ); assert_ok!(LiquidityPools::add_currency( - RuntimeOrigin::signed(BOB.into()), - currency_id + RuntimeOrigin::signed(Keyring::Bob.into()), + currency_id, )); assert_eq!( - OrmlTokens::free_balance( - GLMR_CURRENCY_ID, - &::Sender::get() - ), + OrmlTokens::free_balance(GLMR_CURRENCY_ID, &gateway_sender), /// Ensure it only charged the 0.2 GLMR of fee DEFAULT_BALANCE_GLMR - dollar(18).saturating_div(5) ); - }); + }) + .unwrap(); } -#[test] -fn add_currency_should_fail() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); +#[tokio::test] +async fn add_currency_should_fail() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + env.with_mut_state(Chain::Para(PARA_ID), || { assert_noop!( LiquidityPools::add_currency( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), CurrencyId::ForeignAsset(42) ), pallet_liquidity_pools::Error::::AssetNotFound ); assert_noop!( - LiquidityPools::add_currency(RuntimeOrigin::signed(BOB.into()), CurrencyId::Native), + LiquidityPools::add_currency( + RuntimeOrigin::signed(Keyring::Bob.into()), + CurrencyId::Native + ), pallet_liquidity_pools::Error::::AssetNotFound ); assert_noop!( LiquidityPools::add_currency( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), CurrencyId::Staking(cfg_types::tokens::StakingCurrency::BlockRewards) ), pallet_liquidity_pools::Error::::AssetNotFound ); assert_noop!( LiquidityPools::add_currency( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), CurrencyId::Staking(cfg_types::tokens::StakingCurrency::BlockRewards) ), pallet_liquidity_pools::Error::::AssetNotFound @@ -371,7 +421,7 @@ fn add_currency_should_fail() { Some(currency_id) )); assert_noop!( - LiquidityPools::add_currency(RuntimeOrigin::signed(BOB.into()), currency_id), + LiquidityPools::add_currency(RuntimeOrigin::signed(Keyring::Bob.into()), currency_id), pallet_liquidity_pools::Error::::AssetNotLiquidityPoolsWrappedToken ); @@ -398,7 +448,7 @@ fn add_currency_should_fail() { }), )); assert_noop!( - LiquidityPools::add_currency(RuntimeOrigin::signed(BOB.into()), currency_id), + LiquidityPools::add_currency(RuntimeOrigin::signed(Keyring::Bob.into()), currency_id), pallet_liquidity_pools::Error::::AssetNotLiquidityPoolsTransferable ); @@ -420,18 +470,23 @@ fn add_currency_should_fail() { }) )); assert_noop!( - LiquidityPools::add_currency(RuntimeOrigin::signed(BOB.into()), currency_id), + LiquidityPools::add_currency(RuntimeOrigin::signed(Keyring::Bob.into()), currency_id), pallet_liquidity_pools::Error::::AssetNotLiquidityPoolsTransferable ); }); } -#[test] -fn allow_investment_currency() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); +#[tokio::test] +async fn allow_investment_currency() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let currency_id = AUSD_CURRENCY_ID; let pool_id = DEFAULT_POOL_ID; let evm_chain_id: u64 = MOONBEAM_EVM_CHAIN_ID; @@ -440,6 +495,8 @@ fn allow_investment_currency() { // Create an AUSD pool create_ausd_pool(pool_id); + enable_liquidity_pool_transferability(currency_id); + // Enable LiquidityPools transferability assert_ok!(OrmlAssetRegistry::update_asset( RuntimeOrigin::root(), @@ -463,7 +520,7 @@ fn allow_investment_currency() { )); assert_ok!(LiquidityPools::allow_investment_currency( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), pool_id, default_tranche_id(pool_id), currency_id, @@ -471,20 +528,25 @@ fn allow_investment_currency() { }); } -#[test] -fn allow_pool_should_fail() { - TestNet::reset(); +#[tokio::test] +async fn allow_pool_should_fail() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); - Development::execute_with(|| { + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let currency_id = CurrencyId::ForeignAsset(42); let ausd_currency_id = AUSD_CURRENCY_ID; - setup_pre_requirements(); // Should fail if pool does not exist assert_noop!( LiquidityPools::allow_investment_currency( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), pool_id, // Tranche id is arbitrary in this case as pool does not exist [0u8; 16], @@ -512,10 +574,9 @@ fn allow_pool_should_fail() { create_currency_pool(pool_id, currency_id, 10_000 * dollar(12)); // Should fail if asset is not payment currency - assert!(currency_id != ausd_currency_id); assert_noop!( LiquidityPools::allow_investment_currency( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), pool_id, default_tranche_id(pool_id), ausd_currency_id, @@ -534,7 +595,7 @@ fn allow_pool_should_fail() { enable_liquidity_pool_transferability(ausd_currency_id); assert_noop!( LiquidityPools::allow_investment_currency( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), pool_id, default_tranche_id(pool_id), ausd_currency_id, @@ -562,7 +623,7 @@ fn allow_pool_should_fail() { )); assert_noop!( LiquidityPools::allow_investment_currency( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), pool_id, default_tranche_id(pool_id), currency_id, @@ -590,7 +651,7 @@ fn allow_pool_should_fail() { )); assert_noop!( LiquidityPools::allow_investment_currency( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), pool_id, default_tranche_id(pool_id), currency_id, @@ -614,7 +675,7 @@ fn allow_pool_should_fail() { )); assert_noop!( LiquidityPools::allow_investment_currency( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), pool_id, default_tranche_id(pool_id), currency_id, @@ -641,7 +702,7 @@ fn allow_pool_should_fail() { // Should fail if currency is not foreign asset assert_noop!( LiquidityPools::allow_investment_currency( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), pool_id + 1, // Tranche id is arbitrary in this case, so we don't need to check for the exact // pool_id @@ -653,16 +714,21 @@ fn allow_pool_should_fail() { }); } -#[test] -fn schedule_upgrade() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); +#[tokio::test] +async fn schedule_upgrade() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { // Only Root can call `schedule_upgrade` assert_noop!( LiquidityPools::schedule_upgrade( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), MOONBEAM_EVM_CHAIN_ID, [7; 20] ), @@ -678,16 +744,21 @@ fn schedule_upgrade() { }); } -#[test] -fn cancel_upgrade_upgrade() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); +#[tokio::test] +async fn cancel_upgrade_upgrade() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + env.with_mut_state(Chain::Para(PARA_ID), || { // Only Root can call `cancel_upgrade` assert_noop!( LiquidityPools::cancel_upgrade( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), MOONBEAM_EVM_CHAIN_ID, [7; 20] ), @@ -703,12 +774,17 @@ fn cancel_upgrade_upgrade() { }); } -#[test] -fn update_tranche_token_metadata() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); - let decimals: u8 = 15; +#[tokio::test] +async fn update_tranche_token_metadata() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; // NOTE: Default pool admin is BOB create_ausd_pool(pool_id); @@ -717,7 +793,7 @@ fn update_tranche_token_metadata() { let nonexistent_tranche = [71u8; 16]; assert_noop!( LiquidityPools::update_tranche_token_metadata( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), pool_id, nonexistent_tranche, Domain::EVM(MOONBEAM_EVM_CHAIN_ID), @@ -729,7 +805,7 @@ fn update_tranche_token_metadata() { // Should throw if called by anything but `PoolAdmin` assert_noop!( LiquidityPools::update_tranche_token_metadata( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), pool_id, tranche_id, Domain::EVM(MOONBEAM_EVM_CHAIN_ID), @@ -738,7 +814,7 @@ fn update_tranche_token_metadata() { ); assert_ok!(LiquidityPools::update_tranche_token_metadata( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), pool_id, tranche_id, Domain::EVM(MOONBEAM_EVM_CHAIN_ID), @@ -749,7 +825,7 @@ fn update_tranche_token_metadata() { orml_asset_registry::Metadata::::remove(tranche_currency_id); assert_noop!( LiquidityPools::update_tranche_token_metadata( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), pool_id, tranche_id, Domain::EVM(MOONBEAM_EVM_CHAIN_ID), diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/foreign_investments.rs b/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/foreign_investments.rs index 90f9f4fcd4..e0b27cdfa5 100644 --- a/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/foreign_investments.rs +++ b/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/foreign_investments.rs @@ -40,11 +40,6 @@ use cfg_types::{ ForeignAssetId, TrancheCurrency, }, }; -use development_runtime::{ - Balances, ForeignInvestments, Investments, LiquidityPools, LocationToAccountId, - MinFulfillmentAmountNative, OrmlAssetRegistry, Permissions, PoolSystem, - Runtime as DevelopmentRuntime, RuntimeOrigin, System, Tokens, TreasuryAccount, -}; use frame_support::{ assert_noop, assert_ok, traits::{ @@ -52,6 +47,7 @@ use frame_support::{ Get, PalletInfo, }, }; +use fudge::primitives::Chain; use orml_traits::{asset_registry::AssetMetadata, FixedConversionRateProvider, MultiCurrency}; use pallet_foreign_investments::{ types::{InvestState, RedeemState}, @@ -64,18 +60,23 @@ use runtime_common::{ }; use sp_runtime::{ traits::{AccountIdConversion, BadOrigin, ConstU32, Convert, EnsureAdd, One, Zero}, - BoundedVec, DispatchError, FixedPointNumber, Perquintill, SaturatedConversion, WeakBoundedVec, + BoundedVec, DispatchError, FixedPointNumber, Perquintill, SaturatedConversion, Storage, + WeakBoundedVec, }; -use xcm_simulator::TestExt; +use tokio::runtime::Handle; use crate::{ + chain::centrifuge::{ + Balances, ForeignInvestments, Investments, LiquidityPools, LocationToAccountId, + MinFulfillmentAmountNative, OrmlAssetRegistry, Permissions, PoolSystem, + Runtime as DevelopmentRuntime, RuntimeOrigin, System, Tokens, TreasuryAccount, PARA_ID, + }, liquidity_pools::pallet::development::{ - setup::{dollar, ALICE, BOB}, - test_net::{Development, Moonbeam, RelayChain, TestNet}, + setup::dollar, tests::liquidity_pools::{ foreign_investments::setup::{ do_initial_increase_investment, do_initial_increase_redemption, - ensure_executed_collect_redeem_not_dispatched, + ensure_executed_collect_redeem_not_dispatched, min_fulfillment_amount, }, setup::{ asset_metadata, create_ausd_pool, create_currency_pool, @@ -84,12 +85,12 @@ use crate::{ default_investment_account, default_investment_id, default_tranche_id, general_currency_index, investment_id, }, - setup_pre_requirements, LiquidityPoolMessage, DEFAULT_DOMAIN_ADDRESS_MOONBEAM, + setup_test_env, LiquidityPoolMessage, DEFAULT_DOMAIN_ADDRESS_MOONBEAM, DEFAULT_POOL_ID, DEFAULT_VALIDITY, DOMAIN_MOONBEAM, }, }, }, - utils::{AUSD_CURRENCY_ID, AUSD_ED}, + utils::{accounts::Keyring, env, genesis, AUSD_CURRENCY_ID, AUSD_ED}, }; mod same_currencies { @@ -97,17 +98,23 @@ mod same_currencies { use super::*; - #[test] - fn increase_invest_order() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn increase_invest_order() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let amount = 10 * dollar(12); let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); let currency_id = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; @@ -145,11 +152,17 @@ mod same_currencies { }); } - #[test] - fn decrease_invest_order() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn decrease_invest_order() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let invest_amount: u128 = 10 * dollar(12); let decrease_amount = invest_amount / 3; @@ -157,7 +170,7 @@ mod same_currencies { let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); let currency_id: CurrencyId = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; @@ -227,17 +240,23 @@ mod same_currencies { }); } - #[test] - fn cancel_invest_order() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn cancel_invest_order() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let invest_amount = 10 * dollar(12); let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); let currency_id = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; @@ -288,7 +307,7 @@ mod same_currencies { investor: investor.clone(), investment_id: default_investment_id(), } - .into() + .into() })); // Verify investment was entirely drained from investment account @@ -324,17 +343,23 @@ mod same_currencies { }); } - #[test] - fn collect_invest_order() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn collect_invest_order() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let amount = 10 * dollar(12); let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); let currency_id = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; @@ -396,11 +421,11 @@ mod same_currencies { ); assert!(!events_since_collect.iter().any(|e| { e.event - == pallet_investments::Event::::InvestCollectedForNonClearedOrderId { + == pallet_investments::Event::::InvestCollectedForNonClearedOrderId { investment_id: default_investment_id(), who: investor.clone(), } - .into() + .into() })); // Order should not have been updated since everything is collected @@ -449,11 +474,11 @@ mod same_currencies { // Clearing of foreign InvestState should be dispatched assert!(events_since_collect.iter().any(|e| { e.event - == pallet_foreign_investments::Event::::ForeignInvestmentCleared { + == pallet_foreign_investments::Event::::ForeignInvestmentCleared { investor: investor.clone(), investment_id: default_investment_id(), } - .into() + .into() })); assert!(System::events().iter().any(|e| { @@ -476,17 +501,23 @@ mod same_currencies { }); } - #[test] - fn partially_collect_investment_for_through_investments() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn partially_collect_investment_for_through_investments() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let invest_amount = 10 * dollar(12); let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); let currency_id = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; @@ -535,7 +566,7 @@ mod same_currencies { // Collecting through Investments should denote amounts and transition // state assert_ok!(Investments::collect_investments_for( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), investor.clone(), default_investment_id() )); @@ -625,7 +656,7 @@ mod same_currencies { // Collect remainder through Investments assert_ok!(Investments::collect_investments_for( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), investor.clone(), default_investment_id() )); @@ -667,11 +698,11 @@ mod same_currencies { ); assert!(!System::events().iter().any(|e| { e.event - == pallet_investments::Event::::InvestCollectedForNonClearedOrderId { + == pallet_investments::Event::::InvestCollectedForNonClearedOrderId { investment_id: default_investment_id(), who: investor.clone(), } - .into() + .into() })); assert!(System::events().iter().any(|e| { e.event @@ -710,11 +741,11 @@ mod same_currencies { .iter() .filter(|e| { e.event - == pallet_foreign_investments::Event::::ForeignInvestmentCleared { - investor: investor.clone(), - investment_id: default_investment_id(), - } - .into() + == pallet_foreign_investments::Event::::ForeignInvestmentCleared { + investor: investor.clone(), + investment_id: default_investment_id(), + } + .into() }) .count(), 1 @@ -734,17 +765,23 @@ mod same_currencies { }); } - #[test] - fn increase_redeem_order() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn increase_redeem_order() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let amount = 10 * dollar(12); let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); let currency_id = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; @@ -787,11 +824,17 @@ mod same_currencies { }); } - #[test] - fn decrease_redeem_order() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn decrease_redeem_order() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let redeem_amount = 10 * dollar(12); let decrease_amount = redeem_amount / 3; @@ -799,7 +842,7 @@ mod same_currencies { let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); let currency_id = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; @@ -854,14 +897,14 @@ mod same_currencies { // Foreign RedemptionState should be updated assert!(System::events().iter().any(|e| { e.event - == pallet_foreign_investments::Event::::ForeignRedemptionUpdated { + == pallet_foreign_investments::Event::::ForeignRedemptionUpdated { investor: investor.clone(), investment_id: default_investment_id(), state: RedeemState::Redeeming { - redeem_amount: final_amount + redeem_amount: final_amount } } - .into() + .into() })); // Order should have been updated @@ -900,17 +943,23 @@ mod same_currencies { }); } - #[test] - fn cancel_redeem_order() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn cancel_redeem_order() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let redeem_amount = 10 * dollar(12); let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); let currency_id = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; @@ -970,11 +1019,11 @@ mod same_currencies { // Foreign RedemptionState should be updated assert!(System::events().iter().any(|e| { e.event - == pallet_foreign_investments::Event::::ForeignRedemptionCleared { + == pallet_foreign_investments::Event::::ForeignRedemptionCleared { investor: investor.clone(), investment_id: default_investment_id(), } - .into() + .into() })); // Order should have been updated @@ -996,17 +1045,23 @@ mod same_currencies { }); } - #[test] - fn fully_collect_redeem_order() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn fully_collect_redeem_order() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let amount = 10 * dollar(12); let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); let currency_id = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; @@ -1076,11 +1131,11 @@ mod same_currencies { ); assert!(!events_since_collect.iter().any(|e| { e.event - == pallet_investments::Event::::RedeemCollectedForNonClearedOrderId { + == pallet_investments::Event::::RedeemCollectedForNonClearedOrderId { investment_id: default_investment_id(), who: investor.clone(), } - .into() + .into() })); // Order should not have been updated since everything is collected @@ -1150,17 +1205,23 @@ mod same_currencies { }); } - #[test] - fn partially_collect_redemption_for_through_investments() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn partially_collect_redemption_for_through_investments() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let redeem_amount = 10 * dollar(12); let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); let currency_id = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; @@ -1205,7 +1266,7 @@ mod same_currencies { // Collecting through investments should denote amounts and transition // state assert_ok!(Investments::collect_redemptions_for( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), investor.clone(), default_investment_id() )); @@ -1284,7 +1345,7 @@ mod same_currencies { // Collect remainder through Investments assert_ok!(Investments::collect_redemptions_for( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), investor.clone(), default_investment_id() )); @@ -1302,11 +1363,11 @@ mod same_currencies { )); assert!(!System::events().iter().any(|e| { e.event - == pallet_investments::Event::::RedeemCollectedForNonClearedOrderId { + == pallet_investments::Event::::RedeemCollectedForNonClearedOrderId { investment_id: default_investment_id(), who: investor.clone(), } - .into() + .into() })); assert!(System::events().iter().any(|e| { e.event @@ -1337,11 +1398,11 @@ mod same_currencies { .iter() .filter(|e| { e.event - == pallet_foreign_investments::Event::::ForeignRedemptionCleared { - investor: investor.clone(), - investment_id: default_investment_id(), - } - .into() + == pallet_foreign_investments::Event::::ForeignRedemptionCleared { + investor: investor.clone(), + investment_id: default_investment_id(), + } + .into() }) .count(), 1 @@ -1374,18 +1435,24 @@ mod same_currencies { mod decrease_should_underflow { use super::*; - #[test] - fn invest_decrease_underflow() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn invest_decrease_underflow() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let invest_amount: u128 = 10 * dollar(12); let decrease_amount = invest_amount + 1; let investor: AccountId = AccountConverter::< DevelopmentRuntime, LocationToAccountId, - >::convert((DOMAIN_MOONBEAM, BOB)); + >::convert((DOMAIN_MOONBEAM, Keyring::Bob.into())); let currency_id: CurrencyId = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; create_currency_pool(pool_id, currency_id, currency_decimals.into()); @@ -1416,18 +1483,24 @@ mod same_currencies { }); } - #[test] - fn redeem_decrease_underflow() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn redeem_decrease_underflow() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let redeem_amount: u128 = 10 * dollar(12); let decrease_amount = redeem_amount + 1; let investor: AccountId = AccountConverter::< DevelopmentRuntime, LocationToAccountId, - >::convert((DOMAIN_MOONBEAM, BOB)); + >::convert((DOMAIN_MOONBEAM, Keyring::Bob.into())); let currency_id: CurrencyId = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; create_currency_pool(pool_id, currency_id, currency_decimals.into()); @@ -1459,17 +1532,24 @@ mod same_currencies { mod should_throw_requires_collect { use super::*; - #[test] - fn invest_requires_collect() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + + #[tokio::test] + async fn invest_requires_collect() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let amount: u128 = 10 * dollar(12); let investor: AccountId = AccountConverter::< DevelopmentRuntime, LocationToAccountId, - >::convert((DOMAIN_MOONBEAM, BOB)); + >::convert((DOMAIN_MOONBEAM, Keyring::Bob.into())); let currency_id: CurrencyId = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; create_currency_pool(pool_id, currency_id, currency_decimals.into()); @@ -1527,17 +1607,23 @@ mod same_currencies { }); } - #[test] - fn redeem_requires_collect() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn redeem_requires_collect() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let amount: u128 = 10 * dollar(12); let investor: AccountId = AccountConverter::< DevelopmentRuntime, LocationToAccountId, - >::convert((DOMAIN_MOONBEAM, BOB)); + >::convert((DOMAIN_MOONBEAM, Keyring::Bob.into())); let currency_id: CurrencyId = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; create_currency_pool(pool_id, currency_id, currency_decimals.into()); @@ -1604,16 +1690,22 @@ mod same_currencies { utils::USDT_CURRENCY_ID, }; - #[test] - fn invalid_invest_payment_currency() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn invalid_invest_payment_currency() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let investor: AccountId = AccountConverter::< DevelopmentRuntime, LocationToAccountId, - >::convert((DOMAIN_MOONBEAM, BOB)); + >::convert((DOMAIN_MOONBEAM, Keyring::Bob.into())); let pool_currency = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; let foreign_currency: CurrencyId = USDT_CURRENCY_ID; @@ -1672,16 +1764,22 @@ mod same_currencies { }); } - #[test] - fn invalid_redeem_payout_currency() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn invalid_redeem_payout_currency() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let investor: AccountId = AccountConverter::< DevelopmentRuntime, LocationToAccountId, - >::convert((DOMAIN_MOONBEAM, BOB)); + >::convert((DOMAIN_MOONBEAM, Keyring::Bob.into())); let pool_currency = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; let foreign_currency: CurrencyId = USDT_CURRENCY_ID; @@ -1744,16 +1842,22 @@ mod same_currencies { }); } - #[test] - fn invest_payment_currency_not_found() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn invest_payment_currency_not_found() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let investor: AccountId = AccountConverter::< DevelopmentRuntime, LocationToAccountId, - >::convert((DOMAIN_MOONBEAM, BOB)); + >::convert((DOMAIN_MOONBEAM, Keyring::Bob.into())); let pool_currency = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; let foreign_currency: CurrencyId = USDT_CURRENCY_ID; @@ -1795,16 +1899,22 @@ mod same_currencies { }); } - #[test] - fn redeem_payout_currency_not_found() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn redeem_payout_currency_not_found() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let investor: AccountId = AccountConverter::< DevelopmentRuntime, LocationToAccountId, - >::convert((DOMAIN_MOONBEAM, BOB)); + >::convert((DOMAIN_MOONBEAM, Keyring::Bob.into())); let pool_currency = AUSD_CURRENCY_ID; let currency_decimals = currency_decimals::AUSD; let foreign_currency: CurrencyId = USDT_CURRENCY_ID; @@ -1865,28 +1975,28 @@ mod mismatching_currencies { use super::*; use crate::{ - liquidity_pools::pallet::development::{ - setup::CHARLIE, - tests::{ - liquidity_pools::foreign_investments::setup::{ - enable_usdt_trading, min_fulfillment_amount, - }, - register_usdt, - }, + liquidity_pools::pallet::development::tests::{ + liquidity_pools::foreign_investments::setup::enable_usdt_trading, register_usdt, }, utils::{GLMR_CURRENCY_ID, USDT_CURRENCY_ID}, }; - #[test] - fn collect_foreign_investment_for() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn collect_foreign_investment_for() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); let pool_currency: CurrencyId = AUSD_CURRENCY_ID; let foreign_currency: CurrencyId = USDT_CURRENCY_ID; @@ -1935,7 +2045,7 @@ mod mismatching_currencies { } )); assert_ok!(Investments::collect_investments_for( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), investor.clone(), default_investment_id() )); @@ -1984,16 +2094,22 @@ mod mismatching_currencies { /// Invest in pool currency, then increase in allowed foreign currency, then /// decrease in same foreign currency multiple times. - #[test] - fn invest_increase_decrease() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn invest_increase_decrease() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); let pool_currency: CurrencyId = AUSD_CURRENCY_ID; let foreign_currency: CurrencyId = USDT_CURRENCY_ID; @@ -2057,7 +2173,7 @@ mod mismatching_currencies { invest_amount: invest_amount_pool_denominated }, } - .into() + .into() })); // Should be able to to decrease in the swapping foreign currency @@ -2082,14 +2198,14 @@ mod mismatching_currencies { ); assert!(System::events().iter().any(|e| { e.event == - pallet_foreign_investments::Event::::ForeignInvestmentUpdated - { investor: investor.clone(), - investment_id: default_investment_id(), - state: InvestState::InvestmentOngoing { - invest_amount: invest_amount_pool_denominated - }, - } - .into() + pallet_foreign_investments::Event::::ForeignInvestmentUpdated + { investor: investor.clone(), + investment_id: default_investment_id(), + state: InvestState::InvestmentOngoing { + invest_amount: invest_amount_pool_denominated + }, + } + .into() })); // Decrease partial investing amount @@ -2120,12 +2236,12 @@ mod mismatching_currencies { ); assert!(System::events().iter().any(|e| { e.event == - pallet_foreign_investments::Event::::ForeignInvestmentUpdated - { investor: investor.clone(), - investment_id: default_investment_id(), - state: expected_state.clone() - } - .into() + pallet_foreign_investments::Event::::ForeignInvestmentUpdated + { investor: investor.clone(), + investment_id: default_investment_id(), + state: expected_state.clone() + } + .into() })); /// Consume entire investing amount by sending same message @@ -2146,12 +2262,12 @@ mod mismatching_currencies { ); assert!(System::events().iter().any(|e| { e.event == - pallet_foreign_investments::Event::::ForeignInvestmentUpdated - { investor: investor.clone(), - investment_id: default_investment_id(), - state: expected_state.clone() - } - .into() + pallet_foreign_investments::Event::::ForeignInvestmentUpdated + { investor: investor.clone(), + investment_id: default_investment_id(), + state: expected_state.clone() + } + .into() })); }); } @@ -2159,18 +2275,24 @@ mod mismatching_currencies { /// Propagate swaps only via OrderBook fulfillments. /// /// Flow: Increase, fulfill, decrease, fulfill - #[test] - fn invest_swaps_happy_path() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); + #[tokio::test] + async fn invest_swaps_happy_path() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); - let trader: AccountId = ALICE.into(); + let trader: AccountId = Keyring::Alice.into(); let pool_currency: CurrencyId = AUSD_CURRENCY_ID; let foreign_currency: CurrencyId = USDT_CURRENCY_ID; let pool_currency_decimals = currency_decimals::AUSD; @@ -2330,19 +2452,24 @@ mod mismatching_currencies { /// * Invest is swapping from pool to foreign after decreasing an /// unprocessed investment /// * Redeem is swapping from pool to foreign after collecting - #[test] - fn concurrent_swap_orders_same_direction() { - TestNet::reset(); - Development::execute_with(|| { - // Increase invest setup - setup_pre_requirements(); + #[tokio::test] + async fn concurrent_swap_orders_same_direction() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); - let trader: AccountId = ALICE.into(); + let trader: AccountId = Keyring::Alice.into(); let pool_currency: CurrencyId = AUSD_CURRENCY_ID; let foreign_currency: CurrencyId = USDT_CURRENCY_ID; let pool_currency_decimals = currency_decimals::AUSD; @@ -2432,7 +2559,7 @@ mod mismatching_currencies { TokenSwapReason::Investment ); assert_ok!(Investments::collect_redemptions_for( - RuntimeOrigin::signed(CHARLIE.into()), + RuntimeOrigin::signed(Keyring::Charlie.into()), investor.clone(), default_investment_id() )); @@ -2498,7 +2625,7 @@ mod mismatching_currencies { } )); assert_ok!(Investments::collect_redemptions_for( - RuntimeOrigin::signed(CHARLIE.into()), + RuntimeOrigin::signed(Keyring::Charlie.into()), investor.clone(), default_investment_id() )); @@ -2597,19 +2724,24 @@ mod mismatching_currencies { /// Verify handling concurrent swap orders works if /// * Invest is swapping from foreign to pool after increasing /// * Redeem is swapping from pool to foreign after collecting - #[test] - fn concurrent_swap_orders_opposite_direction() { - TestNet::reset(); - Development::execute_with(|| { - // Increase invest setup - setup_pre_requirements(); + #[tokio::test] + async fn concurrent_swap_orders_opposite_direction() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); - let trader: AccountId = ALICE.into(); + let trader: AccountId = Keyring::Alice.into(); let pool_currency: CurrencyId = AUSD_CURRENCY_ID; let foreign_currency: CurrencyId = USDT_CURRENCY_ID; let pool_currency_decimals = currency_decimals::AUSD; @@ -2707,7 +2839,7 @@ mod mismatching_currencies { } )); assert_ok!(Investments::collect_redemptions_for( - RuntimeOrigin::signed(CHARLIE.into()), + RuntimeOrigin::signed(Keyring::Charlie.into()), investor.clone(), default_investment_id() )); @@ -2780,7 +2912,7 @@ mod mismatching_currencies { } )); assert_ok!(Investments::collect_redemptions_for( - RuntimeOrigin::signed(CHARLIE.into()), + RuntimeOrigin::signed(Keyring::Charlie.into()), investor.clone(), default_investment_id() )); @@ -2852,7 +2984,7 @@ mod mismatching_currencies { } )); assert_ok!(Investments::collect_redemptions_for( - RuntimeOrigin::signed(CHARLIE.into()), + RuntimeOrigin::signed(Keyring::Charlie.into()), investor.clone(), default_investment_id() )); @@ -2946,19 +3078,24 @@ mod mismatching_currencies { /// 2. increase invest in foreign /// 3. process invest /// 4. fulfill swap order - #[test] - fn fulfill_invest_swap_order_requires_collect() { - TestNet::reset(); - Development::execute_with(|| { - // Increase invest setup - setup_pre_requirements(); + #[tokio::test] + async fn fulfill_invest_swap_order_requires_collect() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); - let trader: AccountId = ALICE.into(); + let trader: AccountId = Keyring::Alice.into(); let pool_currency: CurrencyId = AUSD_CURRENCY_ID; let foreign_currency: CurrencyId = USDT_CURRENCY_ID; let pool_currency_decimals = currency_decimals::AUSD; @@ -3050,104 +3187,170 @@ mod mismatching_currencies { /// 3. collect /// 4. process redemption /// 5. fulfill swap order should implicitly collect - #[test] - fn fulfill_redeem_swap_order_requires_collect() { - TestNet::reset(); - Development::execute_with(|| { - // Increase redeem setup - setup_pre_requirements(); + #[tokio::test] + async fn fulfill_redeem_swap_order_requires_collect() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); - let trader: AccountId = ALICE.into(); + let trader: AccountId = Keyring::Alice.into(); let pool_currency: CurrencyId = AUSD_CURRENCY_ID; let foreign_currency: CurrencyId = USDT_CURRENCY_ID; let pool_currency_decimals = currency_decimals::AUSD; - let redeem_amount_pool_denominated: u128 = 10 * dollar(18); + let invest_amount_pool_denominated: u128 = 10 * dollar(18); let swap_order_id = 1; create_currency_pool(pool_id, pool_currency, pool_currency_decimals.into()); - let pool_account = - pallet_pool_system::pool_types::PoolLocator { pool_id }.into_account_truncating(); - let redeem_amount_foreign_denominated: u128 = enable_usdt_trading( + let invest_amount_foreign_denominated: u128 = enable_usdt_trading( pool_currency, - redeem_amount_pool_denominated, + invest_amount_pool_denominated, true, true, true, || {}, ); - assert_ok!(Tokens::mint_into( + // invest in pool currency to reach `InvestmentOngoing` quickly + do_initial_increase_investment( + pool_id, + invest_amount_pool_denominated, + investor.clone(), pool_currency, - &pool_account, - redeem_amount_pool_denominated - )); + true, + ); + // Manually set payment currency since we removed it in the above shortcut setup + InvestmentPaymentCurrency::::insert( + &investor, + default_investment_id(), + foreign_currency, + ); assert_ok!(Tokens::mint_into( foreign_currency, &trader, - redeem_amount_foreign_denominated + invest_amount_foreign_denominated * 2 )); - do_initial_increase_redemption( + + // Decrease invest setup to have invest order swapping into foreign currency + let msg = LiquidityPoolMessage::DecreaseInvestOrder { pool_id, - redeem_amount_pool_denominated, - investor.clone(), - foreign_currency, - ); + tranche_id: default_tranche_id(pool_id), + investor: investor.clone().into(), + currency: general_currency_index(foreign_currency), + amount: invest_amount_foreign_denominated, + }; + assert_ok!(LiquidityPools::submit( + DEFAULT_DOMAIN_ADDRESS_MOONBEAM, + msg.clone() + )); - // Process 50% of redemption at 50% rate, i.e. 1 pool currency = 2 tranche - // tokens + // Redeem setup: Increase and process + assert_ok!(Tokens::mint_into( + default_investment_id().into(), + &Domain::convert(DEFAULT_DOMAIN_ADDRESS_MOONBEAM.domain()), + invest_amount_pool_denominated + )); + let msg = LiquidityPoolMessage::IncreaseRedeemOrder { + pool_id, + tranche_id: default_tranche_id(pool_id), + investor: investor.clone().into(), + currency: general_currency_index(foreign_currency), + amount: invest_amount_pool_denominated, + }; + assert_ok!(LiquidityPools::submit( + DEFAULT_DOMAIN_ADDRESS_MOONBEAM, + msg.clone() + )); + let pool_account = + pallet_pool_system::pool_types::PoolLocator { pool_id }.into_account_truncating(); + assert_ok!(Tokens::mint_into( + pool_currency, + &pool_account, + invest_amount_pool_denominated + )); assert_ok!(Investments::process_redeem_orders(default_investment_id())); + // Process 50% of redemption at 25% rate, i.e. 1 pool currency = 4 tranche + // tokens assert_ok!(Investments::redeem_fulfillment( default_investment_id(), FulfillmentWithPrice { of_amount: Perquintill::from_percent(50), - price: Ratio::checked_from_rational(1, 2).unwrap(), + price: Ratio::checked_from_rational(1, 4).unwrap(), } )); - assert_noop!( - OrderBook::fill_order_full(RuntimeOrigin::signed(trader.clone()), swap_order_id), - pallet_order_book::Error::::OrderNotFound + assert_eq!( + ForeignInvestments::foreign_investment_info(swap_order_id) + .unwrap() + .last_swap_reason + .unwrap(), + TokenSwapReason::Investment ); - assert!(Investments::redemption_requires_collect( - &investor, - default_investment_id() - )); assert_ok!(Investments::collect_redemptions_for( - RuntimeOrigin::signed(CHARLIE.into()), + RuntimeOrigin::signed(Keyring::Charlie.into()), investor.clone(), default_investment_id() )); + assert_eq!( + ForeignInvestments::foreign_investment_info(swap_order_id) + .unwrap() + .last_swap_reason + .unwrap(), + TokenSwapReason::InvestmentAndRedemption + ); + assert_eq!( + InvestmentState::::get(&investor, default_investment_id()), + InvestState::ActiveSwapIntoForeignCurrency { + swap: Swap { + amount: invest_amount_foreign_denominated, + currency_in: foreign_currency, + currency_out: pool_currency + } + } + ); assert_eq!( RedemptionState::::get(&investor, default_investment_id()), RedeemState::RedeemingAndActiveSwapIntoForeignCurrency { - redeem_amount: redeem_amount_pool_denominated / 2, + redeem_amount: invest_amount_pool_denominated / 2, swap: Swap { - amount: redeem_amount_foreign_denominated / 4, + amount: invest_amount_foreign_denominated / 8, currency_in: foreign_currency, currency_out: pool_currency } } ); - // ExecutedCollectRedeem should not have been dispatched + assert_eq!( + RedemptionPayoutCurrency::::get( + &investor, + default_investment_id() + ) + .unwrap(), + foreign_currency + ); + let swap_amount = + invest_amount_foreign_denominated + invest_amount_foreign_denominated / 8; assert!(System::events().iter().any(|e| { - match &e.event { - development_runtime::RuntimeEvent::LiquidityPoolsGateway( - pallet_liquidity_pools_gateway::Event::OutboundMessageSubmitted { - message, - .. - }, - ) => match message { - pallet_liquidity_pools::Message::ExecutedCollectRedeem { .. } => false, - _ => true, - }, - _ => true, - } + e.event + == pallet_order_book::Event::::OrderUpdated { + order_id: swap_order_id, + account: investor.clone(), + buy_amount: swap_amount, + sell_rate_limit: Ratio::one(), + min_fulfillment_amount: min_fulfillment_amount(foreign_currency), + } + .into() })); + ensure_executed_collect_redeem_not_dispatched(); - // Process remaining redemption at 25% rate, i.e. 1 pool currency = 4 tranche - // tokens + // Process remaining redemption at 25% rate, i.e. 1 pool currency = + // 4 tranche tokens assert_ok!(Investments::process_redeem_orders(default_investment_id())); assert_ok!(Investments::redeem_fulfillment( default_investment_id(), @@ -3156,42 +3359,195 @@ mod mismatching_currencies { price: Ratio::checked_from_rational(1, 4).unwrap(), } )); - assert!(Investments::redemption_requires_collect( - &investor, + assert_ok!(Investments::collect_redemptions_for( + RuntimeOrigin::signed(Keyring::Charlie.into()), + investor.clone(), default_investment_id() )); - assert_ok!(OrderBook::fill_order_full( + assert_eq!( + InvestmentState::::get(&investor, default_investment_id()), + InvestState::ActiveSwapIntoForeignCurrency { + swap: Swap { + amount: invest_amount_foreign_denominated, + currency_in: foreign_currency, + currency_out: pool_currency + } + } + ); + assert_eq!( + RedemptionState::::get(&investor, default_investment_id()), + RedeemState::ActiveSwapIntoForeignCurrency { + swap: Swap { + amount: invest_amount_foreign_denominated / 4, + currency_in: foreign_currency, + currency_out: pool_currency + } + } + ); + let swap_amount = + invest_amount_foreign_denominated + invest_amount_foreign_denominated / 4; + assert!(System::events().iter().any(|e| { + e.event + == pallet_order_book::Event::::OrderUpdated { + order_id: swap_order_id, + account: investor.clone(), + buy_amount: swap_amount, + sell_rate_limit: Ratio::one(), + min_fulfillment_amount: min_fulfillment_amount(foreign_currency), + } + .into() + })); + + // Partially fulfilling the swap order below the invest swapping amount should + // still have both states swapping into foreign + assert_ok!(OrderBook::fill_order_partial( RuntimeOrigin::signed(trader.clone()), - swap_order_id + swap_order_id, + invest_amount_foreign_denominated / 2 )); - assert!(!Investments::redemption_requires_collect( + assert!(System::events().iter().any(|e| { + e.event + == pallet_order_book::Event::::OrderFulfillment { + order_id: swap_order_id, + placing_account: investor.clone(), + fulfilling_account: trader.clone(), + partial_fulfillment: true, + fulfillment_amount: invest_amount_foreign_denominated / 2, + currency_in: foreign_currency, + currency_out: pool_currency, + sell_rate_limit: Ratio::one(), + } + .into() + })); + assert_eq!( + InvestmentState::::get(&investor, default_investment_id()), + InvestState::ActiveSwapIntoForeignCurrencyAndSwapIntoForeignDone { + swap: Swap { + amount: invest_amount_foreign_denominated / 2, + currency_in: foreign_currency, + currency_out: pool_currency + }, + done_amount: invest_amount_foreign_denominated / 2 + } + ); + assert_eq!( + RedemptionState::::get(&investor, default_investment_id()), + RedeemState::ActiveSwapIntoForeignCurrency { + swap: Swap { + amount: invest_amount_foreign_denominated / 4, + currency_in: foreign_currency, + currency_out: pool_currency + }, + } + ); + assert!( + RedemptionPayoutCurrency::::contains_key( + &investor, + default_investment_id() + ) + ); + assert!(ForeignInvestments::foreign_investment_info(swap_order_id).is_some()); + assert!( + ForeignInvestments::token_swap_order_ids(&investor, default_investment_id()) + .is_some() + ); + ensure_executed_collect_redeem_not_dispatched(); + + // Partially fulfilling the swap order for the remaining invest swap amount + // should still clear the investment state + assert_ok!(OrderBook::fill_order_partial( + RuntimeOrigin::signed(trader.clone()), + swap_order_id, + invest_amount_foreign_denominated / 2 + )); + assert!(!InvestmentState::::contains_key( &investor, default_investment_id() + ),); + assert_eq!( + RedemptionState::::get(&investor, default_investment_id()), + RedeemState::ActiveSwapIntoForeignCurrency { + swap: Swap { + amount: invest_amount_foreign_denominated / 4, + currency_in: foreign_currency, + currency_out: pool_currency + }, + } + ); + assert!( + RedemptionPayoutCurrency::::contains_key( + &investor, + default_investment_id() + ) + ); + assert!(ForeignInvestments::foreign_investment_info(swap_order_id).is_some()); + assert!( + ForeignInvestments::token_swap_order_ids(&investor, default_investment_id()) + .is_some() + ); + ensure_executed_collect_redeem_not_dispatched(); + + // Partially fulfilling the swap order below the redeem swap amount should still + // clear the investment state + assert_ok!(OrderBook::fill_order_partial( + RuntimeOrigin::signed(trader.clone()), + swap_order_id, + invest_amount_foreign_denominated / 8 )); + assert!(!InvestmentState::::contains_key( + &investor, + default_investment_id() + ),); assert_eq!( RedemptionState::::get(&investor, default_investment_id()), RedeemState::ActiveSwapIntoForeignCurrencyAndSwapIntoForeignDone { - done_amount: redeem_amount_foreign_denominated / 4, swap: Swap { - amount: redeem_amount_foreign_denominated / 8, + amount: invest_amount_foreign_denominated / 8, currency_in: foreign_currency, currency_out: pool_currency - } + }, + done_amount: invest_amount_foreign_denominated / 8 } ); - // ExecutedCollectRedeem should not have been dispatched as RedemptionState is - // still swapping + assert!( + RedemptionPayoutCurrency::::contains_key( + &investor, + default_investment_id() + ) + ); + assert!(ForeignInvestments::foreign_investment_info(swap_order_id).is_some()); + assert!( + ForeignInvestments::token_swap_order_ids(&investor, default_investment_id()) + .is_some() + ); ensure_executed_collect_redeem_not_dispatched(); - // Fulfill redemption swap - assert_ok!(OrderBook::fill_order_full( + // Partially fulfilling the swap order below the redeem swap amount should still + // clear the investment state + assert_ok!(OrderBook::fill_order_partial( RuntimeOrigin::signed(trader.clone()), - swap_order_id + 1 + swap_order_id, + invest_amount_foreign_denominated / 8 )); + assert!(!InvestmentState::::contains_key( + &investor, + default_investment_id() + ),); assert!(!RedemptionState::::contains_key( &investor, default_investment_id() - )); + ),); + assert!( + !RedemptionPayoutCurrency::::contains_key( + &investor, + default_investment_id() + ) + ); + assert!(ForeignInvestments::foreign_investment_info(swap_order_id).is_none()); + assert!( + ForeignInvestments::token_swap_order_ids(&investor, default_investment_id()) + .is_none() + ); assert!(System::events().iter().any(|e| { e.event == pallet_liquidity_pools_gateway::Event::OutboundMessageSubmitted { @@ -3202,8 +3558,8 @@ mod mismatching_currencies { tranche_id: default_tranche_id(pool_id), investor: investor.clone().into(), currency: general_currency_index(foreign_currency), - currency_payout: redeem_amount_foreign_denominated / 8 * 3, - tranche_tokens_payout: redeem_amount_pool_denominated, + currency_payout: invest_amount_foreign_denominated / 4, + tranche_tokens_payout: invest_amount_pool_denominated, remaining_redeem_amount: 0, }, } @@ -3214,19 +3570,25 @@ mod mismatching_currencies { /// Similar to [concurrent_swap_orders_same_direction] but with partial /// fulfillment - #[test] - fn partial_fulfillment_concurrent_swap_orders_same_direction() { - TestNet::reset(); - Development::execute_with(|| { + #[tokio::test] + async fn partial_fulfillment_concurrent_swap_orders_same_direction() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { // Increase invest setup - setup_pre_requirements(); let pool_id = DEFAULT_POOL_ID; let investor: AccountId = AccountConverter::::convert(( DOMAIN_MOONBEAM, - BOB, + Keyring::Bob.into(), )); - let trader: AccountId = ALICE.into(); + let trader: AccountId = Keyring::Alice.into(); let pool_currency: CurrencyId = AUSD_CURRENCY_ID; let foreign_currency: CurrencyId = USDT_CURRENCY_ID; let pool_currency_decimals = currency_decimals::AUSD; @@ -3316,7 +3678,7 @@ mod mismatching_currencies { TokenSwapReason::Investment ); assert_ok!(Investments::collect_redemptions_for( - RuntimeOrigin::signed(CHARLIE.into()), + RuntimeOrigin::signed(Keyring::Charlie.into()), investor.clone(), default_investment_id() )); @@ -3382,7 +3744,7 @@ mod mismatching_currencies { } )); assert_ok!(Investments::collect_redemptions_for( - RuntimeOrigin::signed(CHARLIE.into()), + RuntimeOrigin::signed(Keyring::Charlie.into()), investor.clone(), default_investment_id() )); @@ -3772,7 +4134,7 @@ mod setup { // Mock incoming increase invest message let msg = LiquidityPoolMessage::IncreaseRedeemOrder { - pool_id: 42, + pool_id: pool_id, tranche_id: default_tranche_id(pool_id), investor: investor.clone().into(), currency: general_currency_index(currency_id), diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/mod.rs b/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/mod.rs index e8e509c216..1bc730757d 100644 --- a/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/mod.rs +++ b/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/mod.rs @@ -24,7 +24,7 @@ mod add_allow_upgrade; mod foreign_investments; -mod setup; +pub(crate) mod setup; mod transfers; #[test] diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/setup.rs b/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/setup.rs index 6f38dcdb5a..ca329c7c4b 100644 --- a/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/setup.rs +++ b/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/setup.rs @@ -31,10 +31,6 @@ use cfg_types::{ tokens::{CrossChainTransferability, CurrencyId, CustomMetadata}, }; use cumulus_primitives_core::Junction::GlobalConsensus; -use development_runtime::{ - LiquidityPools, LiquidityPoolsGateway, OrmlAssetRegistry, OrmlTokens, PoolSystem, - Runtime as DevelopmentRuntime, RuntimeOrigin, TreasuryPalletId, -}; use frame_support::{ assert_ok, traits::{ @@ -42,7 +38,9 @@ use frame_support::{ fungibles::{Balanced, Mutate}, Get, PalletInfo, }, + weights::Weight, }; +use fudge::primitives::Chain; use liquidity_pools_gateway_routers::{ ethereum_xcm::EthereumXCMRouter, DomainRouter, XCMRouter, XcmDomain as GatewayXcmDomain, XcmTransactInfo, DEFAULT_PROOF_SIZE, @@ -51,6 +49,7 @@ use orml_asset_registry::{AssetMetadata, Metadata}; use orml_traits::MultiCurrency; use pallet_liquidity_pools::Message; use pallet_pool_system::tranches::{TrancheInput, TrancheType}; +use polkadot_parachain::primitives::Id; use runtime_common::{ account_conversion::AccountConverter, xcm::general_key, xcm_fees::default_per_second, }; @@ -60,18 +59,26 @@ use sp_runtime::{ BoundedVec, DispatchError, Perquintill, SaturatedConversion, WeakBoundedVec, }; use xcm::{ - latest::{Junction, Junction::*, Junctions::*, MultiLocation, NetworkId}, - prelude::{Parachain, X1, X2}, + prelude::{Parachain, X1, X2, X3, XCM_VERSION}, + v3::{Junction, Junction::*, Junctions, MultiLocation, NetworkId}, VersionedMultiLocation, }; use crate::{ - chain::centrifuge::development, - liquidity_pools::pallet::development::{ - setup::{dollar, ALICE, BOB, PARA_ID_MOONBEAM}, - tests::register_ausd, + chain::{ + centrifuge::{ + LiquidityPools, LiquidityPoolsGateway, OrmlAssetRegistry, OrmlTokens, PolkadotXcm, + PoolSystem, Runtime as DevelopmentRuntime, RuntimeOrigin, Tokens, TreasuryPalletId, + PARA_ID, + }, + relay::{Hrmp as RelayHrmp, RuntimeOrigin as RelayRuntimeOrigin}, + }, + liquidity_pools::pallet::development::{setup::dollar, tests::register_ausd}, + utils::{ + accounts::Keyring, + env::{TestEnv, PARA_ID_SIBLING}, + AUSD_CURRENCY_ID, GLMR_CURRENCY_ID, GLMR_ED, MOONBEAM_EVM_CHAIN_ID, }, - utils::{AUSD_CURRENCY_ID, GLMR_CURRENCY_ID, GLMR_ED, MOONBEAM_EVM_CHAIN_ID}, }; // 10 GLMR (18 decimals) @@ -84,18 +91,17 @@ pub const DEFAULT_VALIDITY: Seconds = 2555583502; pub const DEFAULT_OTHER_DOMAIN_ADDRESS: DomainAddress = DomainAddress::EVM(MOONBEAM_EVM_CHAIN_ID, [0; 20]); pub const DEFAULT_POOL_ID: u64 = 42; -pub const DEFAULT_MOONBEAM_LOCATION: MultiLocation = MultiLocation { +pub const DEFAULT_SIBLING_LOCATION: MultiLocation = MultiLocation { parents: 1, - interior: X1(Parachain(PARA_ID_MOONBEAM)), + interior: X1(Parachain(PARA_ID_SIBLING)), }; -use frame_support::weights::Weight; pub type LiquidityPoolMessage = Message; pub fn get_default_moonbeam_native_token_location() -> MultiLocation { MultiLocation { parents: 1, - interior: X2(Parachain(PARA_ID_MOONBEAM), general_key(&[0, 1])), + interior: X2(Parachain(PARA_ID_SIBLING), general_key(&[0, 1])), } } @@ -135,12 +141,27 @@ pub fn set_test_domain_router( )); } +pub fn setup_test_env(env: &mut TestEnv) { + env.with_mut_state(Chain::Para(PARA_ID), || { + setup_pre_requirements(); + }) + .unwrap(); + + env.with_mut_state(Chain::Relay, || { + setup_hrmp_channel(); + }) + .unwrap(); + + env.evolve().unwrap(); +} + /// Initializes universally required storage for liquidityPools tests: /// * Set the EthereumXCM router which in turn sets: /// * transact info and domain router for Moonbeam `MultiLocation`, /// * fee for GLMR (`GLMR_CURRENCY_ID`), /// * Register GLMR and AUSD in `OrmlAssetRegistry`, -/// * Mint 10 GLMR (`DEFAULT_BALANCE_GLMR`) for Alice, Bob and the Treasury. +/// * Mint 10 GLMR (`DEFAULT_BALANCE_GLMR`) for the LP Gateway Sender. +/// * Set the XCM version for the sibling parachain. /// /// NOTE: AUSD is the default pool currency in `create_pool`. /// Neither AUSD nor GLMR are registered as a liquidityPools-transferable @@ -149,7 +170,7 @@ pub fn setup_pre_requirements() { /// Set the EthereumXCM router necessary for Moonbeam. set_test_domain_router( MOONBEAM_EVM_CHAIN_ID, - DEFAULT_MOONBEAM_LOCATION.into(), + DEFAULT_SIBLING_LOCATION.into(), GLMR_CURRENCY_ID, ); @@ -171,22 +192,45 @@ pub fn setup_pre_requirements() { )); // Fund the gateway sender account with enough glimmer to pay for fees - OrmlTokens::deposit( + assert_ok!(Tokens::set_balance( + RuntimeOrigin::root(), + ::Sender::get().into(), GLMR_CURRENCY_ID, - &::Sender::get(), DEFAULT_BALANCE_GLMR, - ); - // TODO: Check - // // Treasury pays for `Executed*` messages - // OrmlTokens::deposit( - // GLMR_CURRENCY_ID, - // &TreasuryPalletId::get().into_account_truncating(), - // DEFAULT_BALANCE_GLMR * dollar(18), - // ); + 0, + )); // Register AUSD in the asset registry which is the default pool currency in // `create_pool` register_ausd(); + + // Set the XCM version used when sending XCM messages to sibling. + assert_ok!(PolkadotXcm::force_xcm_version( + RuntimeOrigin::root(), + Box::new(MultiLocation::new( + 1, + Junctions::X1(Junction::Parachain(PARA_ID_SIBLING)), + )), + XCM_VERSION, + )); +} + +/// Opens the required HRMP channel between parachain and sibling. +/// +/// NOTE - this is should be done on the relay chain. +pub fn setup_hrmp_channel() { + assert_ok!(RelayHrmp::force_open_hrmp_channel( + RelayRuntimeOrigin::root(), + Id::from(PARA_ID), + Id::from(PARA_ID_SIBLING), + 10, + 1024, + )); + + assert_ok!(RelayHrmp::force_process_hrmp_open( + RelayRuntimeOrigin::root(), + 0, + )); } /// Creates a new pool for the given id with @@ -203,8 +247,8 @@ pub fn create_ausd_pool(pool_id: u64) { /// * The given `currency` as pool currency with of `currency_decimals`. pub fn create_currency_pool(pool_id: u64, currency_id: CurrencyId, currency_decimals: Balance) { assert_ok!(PoolSystem::create( - BOB.into(), - BOB.into(), + Keyring::Bob.into(), + Keyring::Bob.into(), pool_id, vec![ TrancheInput { diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/transfers.rs b/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/transfers.rs index 8a1779e8b8..d95e21ce2b 100644 --- a/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/transfers.rs +++ b/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools/transfers.rs @@ -36,48 +36,51 @@ use cfg_types::{ ForeignAssetId, }, }; -use development_runtime::{ - LiquidityPools, LocationToAccountId, OrmlTokens, Permissions, PoolSystem, - Runtime as DevelopmentRuntime, RuntimeOrigin, System, -}; use frame_support::{assert_noop, assert_ok, dispatch::Weight, traits::fungibles::Mutate}; +use fudge::primitives::Chain; use orml_traits::{asset_registry::AssetMetadata, FixedConversionRateProvider, MultiCurrency}; use runtime_common::account_conversion::AccountConverter; use sp_runtime::{ traits::{Convert, One, Zero}, - BoundedVec, DispatchError, + BoundedVec, DispatchError, Storage, }; +use tokio::runtime::Handle; use xcm::{latest::MultiLocation, VersionedMultiLocation}; -use xcm_simulator::TestExt; use crate::{ + chain::centrifuge::{ + LiquidityPools, LocationToAccountId, OrmlTokens, Permissions, PoolSystem, + Runtime as DevelopmentRuntime, RuntimeOrigin, System, PARA_ID, + }, liquidity_pools::pallet::development::{ - setup::{dollar, ALICE, BOB, CHARLIE}, - test_net::{Development, Moonbeam, RelayChain, TestNet}, + setup::dollar, tests::liquidity_pools::setup::{ asset_metadata, create_ausd_pool, create_currency_pool, enable_liquidity_pool_transferability, investments::{default_tranche_id, general_currency_index, investment_id}, - liquidity_pools_transferable_multilocation, setup_pre_requirements, - LiquidityPoolMessage, DEFAULT_BALANCE_GLMR, DEFAULT_DOMAIN_ADDRESS_MOONBEAM, - DEFAULT_POOL_ID, + liquidity_pools_transferable_multilocation, setup_test_env, LiquidityPoolMessage, + DEFAULT_BALANCE_GLMR, DEFAULT_DOMAIN_ADDRESS_MOONBEAM, DEFAULT_POOL_ID, }, }, - utils::{AUSD_CURRENCY_ID, AUSD_ED, MOONBEAM_EVM_CHAIN_ID}, + utils::{accounts::Keyring, env, genesis, AUSD_CURRENCY_ID, AUSD_ED, MOONBEAM_EVM_CHAIN_ID}, }; -#[test] -fn transfer_non_tranche_tokens_from_local() { - TestNet::reset(); - Development::execute_with(|| { - // Register GLMR and fund BOB - setup_pre_requirements(); +#[tokio::test] +async fn transfer_non_tranche_tokens_from_local() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_native_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + env.with_mut_state(Chain::Para(PARA_ID), || { let initial_balance = 2 * AUSD_ED; let amount = initial_balance / 2; let dest_address = DEFAULT_DOMAIN_ADDRESS_MOONBEAM; let currency_id = AUSD_CURRENCY_ID; - let source_account = CHARLIE; + let source_account = Keyring::Charlie; // Mint sufficient balance assert_eq!( @@ -171,42 +174,41 @@ fn transfer_non_tranche_tokens_from_local() { }); } -#[test] -fn transfer_non_tranche_tokens_to_local() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); +#[tokio::test] +async fn transfer_non_tranche_tokens_to_local() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_native_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let initial_balance = DEFAULT_BALANCE_GLMR; let amount = DEFAULT_BALANCE_GLMR / 2; let dest_address = DEFAULT_DOMAIN_ADDRESS_MOONBEAM; let currency_id = AUSD_CURRENCY_ID; - let receiver: AccountId = BOB.into(); + let receiver: AccountId = Keyring::Bob.into(); // Mock incoming decrease message let msg = LiquidityPoolMessage::Transfer { currency: general_currency_index(currency_id), // sender is irrelevant for other -> local - sender: ALICE, + sender: Keyring::Alice.into(), receiver: receiver.clone().into(), amount, }; - assert_eq!(OrmlTokens::total_issuance(currency_id), AUSD_ED * 2); + assert_eq!(OrmlTokens::total_issuance(currency_id), 0); // Finally, verify that we can now transfer the tranche to the destination // address assert_ok!(LiquidityPools::submit(DEFAULT_DOMAIN_ADDRESS_MOONBEAM, msg)); // Verify that the correct amount was minted - assert_eq!( - OrmlTokens::total_issuance(currency_id), - amount + AUSD_ED * 2 - ); - assert_eq!( - OrmlTokens::free_balance(currency_id, &receiver), - amount + AUSD_ED - ); + assert_eq!(OrmlTokens::total_issuance(currency_id), amount); + assert_eq!(OrmlTokens::free_balance(currency_id, &receiver), amount); // Verify empty transfers throw assert_noop!( @@ -214,7 +216,7 @@ fn transfer_non_tranche_tokens_to_local() { DEFAULT_DOMAIN_ADDRESS_MOONBEAM, LiquidityPoolMessage::Transfer { currency: general_currency_index(currency_id), - sender: ALICE, + sender: Keyring::Alice.into(), receiver: receiver.into(), amount: 0, }, @@ -224,16 +226,21 @@ fn transfer_non_tranche_tokens_to_local() { }); } -#[test] -fn transfer_tranche_tokens_from_local() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); +#[tokio::test] +async fn transfer_tranche_tokens_from_local() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + env.with_mut_state(Chain::Para(PARA_ID), || { let pool_id = DEFAULT_POOL_ID; let amount = 100_000; let dest_address: DomainAddress = DomainAddress::EVM(1284, [99; 20]); - let receiver = BOB; + let receiver = Keyring::Bob; // Create the pool create_ausd_pool(pool_id); @@ -245,7 +252,7 @@ fn transfer_tranche_tokens_from_local() { // Verify that we first need the destination address to be whitelisted assert_noop!( LiquidityPools::transfer_tranche_tokens( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), pool_id, default_tranche_id(pool_id), dest_address.clone(), @@ -315,18 +322,23 @@ fn transfer_tranche_tokens_from_local() { }); } -#[test] -fn transfer_tranche_tokens_to_local() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); +#[tokio::test] +async fn transfer_tranche_tokens_to_local() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { // Create new pool let pool_id = DEFAULT_POOL_ID; create_ausd_pool(pool_id); let amount = 100_000_000; - let receiver: AccountId = BOB.into(); + let receiver: AccountId = Keyring::Bob.into(); let sender: DomainAddress = DomainAddress::EVM(1284, [99; 20]); let sending_domain_locator = Domain::convert(DEFAULT_DOMAIN_ADDRESS_MOONBEAM.domain()); let tranche_id = default_tranche_id(pool_id); @@ -390,13 +402,19 @@ fn transfer_tranche_tokens_to_local() { }); } -#[test] /// Try to transfer tranches for non-existing pools or invalid tranche ids for /// existing pools. -fn transferring_invalid_tranche_tokens_should_fail() { - TestNet::reset(); - Development::execute_with(|| { - setup_pre_requirements(); +#[tokio::test] +async fn transferring_invalid_tranche_tokens_should_fail() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { let dest_address: DomainAddress = DomainAddress::EVM(1284, [99; 20]); let valid_pool_id: u64 = 42; @@ -408,26 +426,26 @@ fn transferring_invalid_tranche_tokens_should_fail() { let invalid_tranche_id = valid_tranche_id.map(|i| i.saturating_add(1)); assert!(PoolSystem::pool(invalid_pool_id).is_none()); - // Make BOB the MembersListAdmin of both pools + // Make Keyring::Bob the MembersListAdmin of both pools assert_ok!(Permissions::add( RuntimeOrigin::root(), Role::PoolRole(PoolRole::PoolAdmin), - BOB.into(), + Keyring::Bob.into(), PermissionScope::Pool(valid_pool_id), Role::PoolRole(PoolRole::InvestorAdmin), )); assert_ok!(Permissions::add( RuntimeOrigin::root(), Role::PoolRole(PoolRole::PoolAdmin), - BOB.into(), + Keyring::Bob.into(), PermissionScope::Pool(invalid_pool_id), Role::PoolRole(PoolRole::InvestorAdmin), )); - // Give BOB investor role for (valid_pool_id, invalid_tranche_id) and + // Give Keyring::Bob investor role for (valid_pool_id, invalid_tranche_id) and // (invalid_pool_id, valid_tranche_id) assert_ok!(Permissions::add( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), Role::PoolRole(PoolRole::InvestorAdmin), AccountConverter::::convert( dest_address.clone() @@ -436,7 +454,7 @@ fn transferring_invalid_tranche_tokens_should_fail() { Role::PoolRole(PoolRole::TrancheInvestor(valid_tranche_id, valid_until)), )); assert_ok!(Permissions::add( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), Role::PoolRole(PoolRole::InvestorAdmin), AccountConverter::::convert( dest_address.clone() @@ -446,7 +464,7 @@ fn transferring_invalid_tranche_tokens_should_fail() { )); assert_noop!( LiquidityPools::transfer_tranche_tokens( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), invalid_pool_id, valid_tranche_id, dest_address.clone(), @@ -456,7 +474,7 @@ fn transferring_invalid_tranche_tokens_should_fail() { ); assert_noop!( LiquidityPools::transfer_tranche_tokens( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), valid_pool_id, invalid_tranche_id, dest_address, diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/routers/ethereum_xcm.rs b/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/routers/ethereum_xcm.rs index bdb22376b7..99e1f58079 100644 --- a/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/routers/ethereum_xcm.rs +++ b/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/routers/ethereum_xcm.rs @@ -23,6 +23,7 @@ use cfg_types::{ tokens::{CrossChainTransferability, CurrencyId, CustomMetadata}, }; use frame_support::{assert_noop, assert_ok}; +use fudge::primitives::Chain; use hex::FromHex; use liquidity_pools_gateway_routers::{ ethereum_xcm::EthereumXCMRouter, AxelarXCMRouter, DomainRouter, EVMDomain, FeeValues, @@ -32,52 +33,64 @@ use orml_traits::{asset_registry::AssetMetadata, MultiCurrency}; use pallet_liquidity_pools::Message; use runtime_common::{xcm::general_key, xcm_fees::default_per_second}; use sp_core::{bounded::BoundedVec, H160}; -use xcm_simulator::TestExt; +use sp_runtime::Storage; +use tokio::runtime::Handle; use crate::{ chain::centrifuge::{ Balance, LiquidityPoolsGateway, OrmlAssetRegistry, OrmlTokens, Runtime, RuntimeOrigin, + PARA_ID, }, liquidity_pools::pallet::development::{ - setup::{dollar, ALICE, BOB, CHARLIE, PARA_ID_MOONBEAM, TEST_DOMAIN}, - test_net::{Development, Moonbeam, RelayChain, TestNet}, - tests::routers::axelar_evm::TEST_EVM_CHAIN, + setup::dollar, + tests::{ + liquidity_pools::setup::{setup_test_env, DEFAULT_SIBLING_LOCATION}, + routers::axelar_evm::TEST_EVM_CHAIN, + }, }, - utils::accounts::Keyring, + utils::{accounts::Keyring, env, env::PARA_ID_SIBLING, genesis, GLMR_CURRENCY_ID}, }; -#[test] -fn submit_ethereum_xcm() { +const TEST_DOMAIN: Domain = Domain::EVM(1); + +#[tokio::test] +async fn submit_ethereum_xcm() { submit_test_fn(get_ethereum_xcm_router_fn()); } -#[test] -fn submit_axelar_xcm() { +#[tokio::test] +async fn submit_axelar_xcm() { submit_test_fn(get_axelar_xcm_router_fn()); } fn submit_test_fn(router_creation_fn: RouterCreationFn) { - TestNet::reset(); + let mut env = { + let mut genesis = Storage::default(); + genesis::default_native_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; - Development::execute_with(|| { + setup_test_env(&mut env); + + env.with_mut_state(Chain::Para(PARA_ID), || { setup(router_creation_fn); let msg = Message::::Transfer { currency: 0, - sender: ALICE.into(), - receiver: BOB.into(), + sender: Keyring::Alice.into(), + receiver: Keyring::Bob.into(), amount: 1_000u128, }; assert_ok!(::submit( - ALICE.into(), + Keyring::Alice.into(), TEST_DOMAIN, msg.clone(), )); assert_noop!( ::submit( - ALICE.into(), + Keyring::Alice.into(), Domain::EVM(1285), msg.clone(), ), @@ -141,65 +154,11 @@ fn get_ethereum_xcm_router_fn() -> RouterCreationFn { } fn setup(router_creation_fn: RouterCreationFn) { - let moonbeam_location = MultiLocation { - parents: 1, - interior: X1(Parachain(PARA_ID_MOONBEAM)), - }; - let moonbeam_native_token = MultiLocation { - parents: 1, - interior: X2(Parachain(PARA_ID_MOONBEAM), general_key(&[0, 1])), - }; - - /// Register Moonbeam's native token - let glmr_currency_id = CurrencyId::ForeignAsset(1); - let meta: AssetMetadata = AssetMetadata { - decimals: 18, - name: "Glimmer".into(), - symbol: "GLMR".into(), - existential_deposit: 1_000_000, - location: Some(VersionedMultiLocation::V3(moonbeam_native_token)), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - - let domain_router = router_creation_fn(moonbeam_location.into(), glmr_currency_id); + let domain_router = router_creation_fn(DEFAULT_SIBLING_LOCATION.into(), GLMR_CURRENCY_ID); assert_ok!(LiquidityPoolsGateway::set_domain_router( RuntimeOrigin::root(), TEST_DOMAIN, domain_router, )); - - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta, - Some(glmr_currency_id) - )); - - // Fund the gateway sender account with enough glimmer to pay for fees - OrmlTokens::deposit( - glmr_currency_id, - &::Sender::get(), - 1_000_000_000_000 * dollar(18), - ); - - // We first need to register AUSD in the asset registry - let ausd_meta: AssetMetadata = AssetMetadata { - decimals: 12, - name: "Acala Dollar".into(), - symbol: "AUSD".into(), - existential_deposit: 1_000, - location: None, - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - ausd_meta, - Some(CurrencyId::AUSD) - )); } diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/development/transfers.rs b/runtime/integration-tests/src/liquidity_pools/pallet/development/transfers.rs index beb715182e..7d88b26392 100644 --- a/runtime/integration-tests/src/liquidity_pools/pallet/development/transfers.rs +++ b/runtime/integration-tests/src/liquidity_pools/pallet/development/transfers.rs @@ -27,27 +27,40 @@ use cfg_types::{ tokens::{CrossChainTransferability, CurrencyId, CustomMetadata}, xcm::XcmMetadata, }; -use development_runtime::{Balances, OrmlAssetRegistry, OrmlTokens, RuntimeOrigin, XTokens}; use frame_support::assert_ok; +use fudge::primitives::Chain; use orml_traits::{asset_registry::AssetMetadata, FixedConversionRateProvider, MultiCurrency}; +use polkadot_parachain::primitives::Id; use runtime_common::{ xcm::general_key, xcm_fees::{default_per_second, ksm_per_second}, }; -use sp_runtime::traits::BadOrigin; +use sp_runtime::{traits::BadOrigin, Storage}; +use tokio::runtime::Handle; use xcm::{ - latest::{Junction, Junction::*, Junctions::*, MultiLocation, NetworkId, WeightLimit}, + prelude::XCM_VERSION, + v3::{Junction, Junction::*, Junctions, Junctions::*, MultiLocation, NetworkId, WeightLimit}, VersionedMultiLocation, }; -use xcm_simulator::TestExt; -use crate::liquidity_pools::pallet::{ - development::{ - setup::{centrifuge_account, cfg, moonbeam_account, ALICE, BOB, CHARLIE, PARA_ID_MOONBEAM}, - test_net::{Development, Moonbeam, RelayChain, TestNet}, +use crate::{ + chain::{ + centrifuge::{ + AccountId, Balances, OrmlAssetRegistry, OrmlTokens, PolkadotXcm, Runtime, + RuntimeOrigin, XTokens, PARA_ID, + }, + relay::{Hrmp as RelayHrmp, RuntimeOrigin as RelayRuntimeOrigin}, + }, + liquidity_pools::pallet::development::{ + setup::{centrifuge_account, cfg, sibling_account}, tests::register_ausd, }, - xcm_metadata, + utils::{ + accounts::Keyring, + env, + env::{TestEnv, PARA_ID_SIBLING}, + genesis, + }, }; /* @@ -61,12 +74,19 @@ which would go unnoticed and untreated otherwise. */ -#[test] -fn transfer_cfg_to_sibling() { - TestNet::reset(); +#[tokio::test] +async fn test_transfer_cfg_to_sibling() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_native_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; + + transfer_cfg_to_sibling(&mut env); +} - let alice_initial_balance = cfg(10_000); - let bob_initial_balance = cfg(10_000); +fn transfer_cfg_to_sibling(env: &mut TestEnv) { + let alice_initial_balance = cfg(100_000); let transfer_amount = cfg(5); let cfg_in_sibling = CurrencyId::ForeignAsset(12); @@ -79,7 +99,7 @@ fn transfer_cfg_to_sibling() { location: Some(VersionedMultiLocation::V3(MultiLocation::new( 1, X2( - Parachain(parachains::polkadot::centrifuge::ID), + Parachain(PARA_ID), general_key(parachains::polkadot::centrifuge::CFG_KEY), ), ))), @@ -89,19 +109,51 @@ fn transfer_cfg_to_sibling() { }, }; - Development::execute_with(|| { - assert_eq!(Balances::free_balance(&ALICE.into()), alice_initial_balance); - assert_eq!(Balances::free_balance(&moonbeam_account()), 0); + env.with_mut_state(Chain::Para(PARA_ID), || { + assert_eq!( + Balances::free_balance(&Keyring::Alice.into()), + alice_initial_balance + ); + assert_eq!(Balances::free_balance(&sibling_account()), 0); assert_ok!(OrmlAssetRegistry::register_asset( RuntimeOrigin::root(), meta.clone(), Some(CurrencyId::Native), )); + + assert_ok!(PolkadotXcm::force_xcm_version( + RuntimeOrigin::root(), + Box::new(MultiLocation::new( + 1, + Junctions::X1(Junction::Parachain(PARA_ID_SIBLING)), + )), + XCM_VERSION, + )); }); - Moonbeam::execute_with(|| { - assert_eq!(OrmlTokens::free_balance(cfg_in_sibling, &BOB.into()), 0); + env.with_mut_state(Chain::Relay, || { + assert_ok!(RelayHrmp::force_open_hrmp_channel( + RelayRuntimeOrigin::root(), + Id::from(PARA_ID), + Id::from(PARA_ID_SIBLING), + 10, + 1024, + )); + + assert_ok!(RelayHrmp::force_process_hrmp_open( + RelayRuntimeOrigin::root(), + 0, + )); + }); + + env.evolve().unwrap(); + + env.with_mut_state(Chain::Para(PARA_ID_SIBLING), || { + assert_eq!( + OrmlTokens::free_balance(cfg_in_sibling, &Keyring::Bob.into()), + 0 + ); assert_ok!(OrmlAssetRegistry::register_asset( RuntimeOrigin::root(), @@ -110,86 +162,133 @@ fn transfer_cfg_to_sibling() { )); }); - Development::execute_with(|| { + env.with_mut_state(Chain::Para(PARA_ID), || { assert_ok!(XTokens::transfer( - RuntimeOrigin::signed(ALICE.into()), + RuntimeOrigin::signed(Keyring::Alice.into()), CurrencyId::Native, transfer_amount, Box::new( MultiLocation::new( 1, X2( - Parachain(PARA_ID_MOONBEAM), + Parachain(PARA_ID_SIBLING), Junction::AccountId32 { network: None, - id: BOB, - } - ) + id: Keyring::Bob.into(), + }, + ), ) .into() ), WeightLimit::Limited(8_000_000_000_000.into()), )); - // Confirm that Alice's balance is initial balance - amount transferred + // Confirm that Keyring::Alice's balance is initial balance - amount transferred assert_eq!( - Balances::free_balance(&ALICE.into()), + Balances::free_balance(&Keyring::Alice.into()), alice_initial_balance - transfer_amount ); // Verify that the amount transferred is now part of the sibling account here - assert_eq!(Balances::free_balance(&moonbeam_account()), transfer_amount); + assert_eq!(Balances::free_balance(&sibling_account()), transfer_amount); }); - Moonbeam::execute_with(|| { - let current_balance = OrmlTokens::free_balance(cfg_in_sibling, &BOB.into()); + env.evolve().unwrap(); + + env.with_mut_state(Chain::Para(PARA_ID_SIBLING), || { + let current_balance = OrmlTokens::free_balance(cfg_in_sibling, &Keyring::Bob.into()); - // Verify that BOB now has (amount transferred - fee) + // Verify that Keyring::Bob now has (amount transferred - fee) assert_eq!(current_balance, transfer_amount - fee(18)); - // Sanity check for the actual amount BOB ends up with - assert_eq!(current_balance, 4991987200000000000); + // Sanity check for the actual amount Keyring::Bob ends up with + assert_eq!(current_balance, 4992960800000000000); }); } -#[test] -fn transfer_cfg_sibling_to_centrifuge() { - TestNet::reset(); +#[tokio::test] +async fn transfer_cfg_sibling_to_centrifuge() { + let mut env = { + let mut genesis = Storage::default(); + genesis::default_native_balances::(&mut genesis); + env::test_env_with_centrifuge_storage(Handle::current(), genesis) + }; // In order to be able to transfer CFG from Moonbeam to Development, we need to // first send CFG from Development to Moonbeam, or else it fails since it'd be // like Moonbeam had minted CFG on their side. - transfer_cfg_to_sibling(); + transfer_cfg_to_sibling(&mut env); + + let para_to_sibling_transfer_amount = cfg(5); + + let alice_balance = cfg(100_000) - para_to_sibling_transfer_amount; + let bob_balance = para_to_sibling_transfer_amount - fee(18); + let charlie_balance = cfg(100_000); - let alice_initial_balance = 9995000000000000000000; - let bob_initial_balance = cfg(5) - cfg_fee(); - let transfer_amount = cfg(4); + let sibling_to_para_transfer_amount = cfg(4); // Note: This asset was registered in `transfer_cfg_to_sibling` let cfg_in_sibling = CurrencyId::ForeignAsset(12); - Development::execute_with(|| { - assert_eq!(Balances::free_balance(&ALICE.into()), alice_initial_balance); + env.with_mut_state(Chain::Para(PARA_ID), || { + assert_eq!( + Balances::free_balance(&Keyring::Alice.into()), + alice_balance + ); }); - Moonbeam::execute_with(|| { + env.with_mut_state(Chain::Para(PARA_ID_SIBLING), || { assert_eq!(Balances::free_balance(¢rifuge_account()), 0); + assert_eq!( - OrmlTokens::free_balance(cfg_in_sibling, &BOB.into()), - bob_initial_balance + Balances::free_balance(&Keyring::Charlie.into()), + charlie_balance ); + assert_eq!( + OrmlTokens::free_balance(cfg_in_sibling, &Keyring::Bob.into()), + bob_balance + ); + + assert_ok!(PolkadotXcm::force_xcm_version( + RuntimeOrigin::root(), + Box::new(MultiLocation::new( + 1, + Junctions::X1(Junction::Parachain(PARA_ID)), + )), + XCM_VERSION, + )); + }); + + env.with_mut_state(Chain::Relay, || { + assert_ok!(RelayHrmp::force_open_hrmp_channel( + RelayRuntimeOrigin::root(), + Id::from(PARA_ID_SIBLING), + Id::from(PARA_ID), + 10, + 1024, + )); + + assert_ok!(RelayHrmp::force_process_hrmp_open( + RelayRuntimeOrigin::root(), + 0, + )); + }); + + env.evolve().unwrap(); + + env.with_mut_state(Chain::Para(PARA_ID_SIBLING), || { assert_ok!(XTokens::transfer( - RuntimeOrigin::signed(BOB.into()), + RuntimeOrigin::signed(Keyring::Bob.into()), cfg_in_sibling, - transfer_amount, + sibling_to_para_transfer_amount, Box::new( MultiLocation::new( 1, X2( - Parachain(parachains::polkadot::centrifuge::ID), + Parachain(PARA_ID), Junction::AccountId32 { network: None, - id: CHARLIE, + id: Keyring::Charlie.into(), } ) ) @@ -200,23 +299,26 @@ fn transfer_cfg_sibling_to_centrifuge() { // Confirm that Charlie's balance is initial balance - amount transferred assert_eq!( - OrmlTokens::free_balance(cfg_in_sibling, &BOB.into()), - bob_initial_balance - transfer_amount + OrmlTokens::free_balance(cfg_in_sibling, &Keyring::Bob.into()), + bob_balance - sibling_to_para_transfer_amount ); }); - Development::execute_with(|| { + env.evolve().unwrap(); + env.evolve().unwrap(); + + env.with_mut_state(Chain::Para(PARA_ID), || { // Verify that Charlie's balance equals the amount transferred - fee assert_eq!( - Balances::free_balance(&CHARLIE.into()), - transfer_amount - cfg_fee(), + Balances::free_balance(&Into::::into(Keyring::Charlie)), + charlie_balance + sibling_to_para_transfer_amount - cfg_fee(), ); }); } #[test] fn test_total_fee() { - assert_eq!(cfg_fee(), 8012800000000000); + assert_eq!(cfg_fee(), 7039200000000000); } fn cfg_fee() -> Balance { @@ -243,3 +345,12 @@ fn calc_fee(fee_per_second: Balance) -> Balance { // shall see. fee_per_second.div_euclid(10_000) * 8 } + +/// Get the `XcmMetadata` for a given `CrossChainTransferability` value if +/// possible. +fn xcm_metadata(transferability: CrossChainTransferability) -> Option { + match transferability { + CrossChainTransferability::Xcm(x) | CrossChainTransferability::All(x) => Some(x), + _ => None, + } +} diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/kusama/mod.rs b/runtime/integration-tests/src/liquidity_pools/pallet/kusama/mod.rs deleted file mode 100644 index 30b8ca39c6..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/kusama/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod restricted_calls; -mod setup; -mod test_net; -mod tests; diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/kusama/restricted_calls.rs b/runtime/integration-tests/src/liquidity_pools/pallet/kusama/restricted_calls.rs deleted file mode 100644 index 287003b7ea..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/kusama/restricted_calls.rs +++ /dev/null @@ -1,189 +0,0 @@ -// This file is part of Altair chain project. -// -// Altair 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 (see http://www.gnu.org/licenses). -// Altair 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. - -// Copyright 2021 Altair GmbH (altair.io). -// This file is part of Altair chain project. -// -// Altair 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 (see http://www.gnu.org/licenses). -// Altair 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. - -use altair_runtime::{Balances, PolkadotXcm, RuntimeCall, RuntimeOrigin, XTokens}; -use cfg_primitives::{constants::currency_decimals, parachains, Balance}; -use cfg_types::{ - tokens::{CurrencyId, CustomMetadata}, - xcm::XcmMetadata, -}; -use frame_support::{assert_err, assert_noop, assert_ok, dispatch::Dispatchable}; -use orml_traits::{asset_registry::AssetMetadata, FixedConversionRateProvider, MultiCurrency}; -use runtime_common::xcm_fees::{default_per_second, ksm_per_second}; -use sp_runtime::{DispatchError, DispatchError::BadOrigin}; -use xcm::{ - latest::{ - AssetId, Fungibility, Junction, Junction::*, Junctions::*, MultiAsset, MultiLocation, - NetworkId, WeightLimit, - }, - v2::{Instruction::WithdrawAsset, Xcm}, - VersionedMultiLocation, -}; -use xcm_simulator::TestExt; - -use crate::liquidity_pools::pallet::kusama::{ - setup::{air, foreign, sibling_account, ALICE, BOB, PARA_ID_SIBLING}, - test_net::{Altair, KusamaNet, Sibling, TestNet}, -}; - -/// Verify that calls that would allow for Tranche token to be transferred -/// through XCM fail because the underlying CurrencyIdConvert doesn't handle -/// Tranche tokens. -pub mod blocked { - use cfg_utils::vec_to_fixed_array; - use sp_runtime::{traits::ConstU32, WeakBoundedVec}; - use xcm::{latest::MultiAssets, VersionedMultiAsset, VersionedMultiAssets}; - - use super::*; - - #[test] - fn xtokens_transfer() { - // For now, Tranche tokens are not supported in the XCM config so - // we just safe-guard that trying to transfer a tranche token fails. - Altair::execute_with(|| { - assert_noop!( - XTokens::transfer( - RuntimeOrigin::signed(ALICE.into()), - CurrencyId::Tranche(401, [0; 16]), - 42, - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(PARA_ID_SIBLING), - Junction::AccountId32 { - network: None, - id: BOB, - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000_000.into()), - ), - orml_xtokens::Error::::NotCrossChainTransferableCurrency - ); - }); - } - - // Verify that trying to transfer Tranche tokens using their MultiLocation - // representation also fails. - #[test] - fn xtokens_transfer_multiasset() { - use codec::Encode; - - let tranche_currency = CurrencyId::Tranche(401, [0; 16]); - let tranche_id = - WeakBoundedVec::>::force_from(tranche_currency.encode(), None); - let tranche_location = MultiLocation { - parents: 1, - interior: X3( - Parachain(123), - PalletInstance(42), - GeneralKey { - length: tranche_id.len() as u8, - data: vec_to_fixed_array(tranche_id.to_vec()), - }, - ), - }; - let tranche_multi_asset = VersionedMultiAsset::from(MultiAsset::from(( - AssetId::Concrete(tranche_location), - Fungibility::Fungible(42), - ))); - - Altair::execute_with(|| { - assert_noop!( - XTokens::transfer_multiasset( - RuntimeOrigin::signed(ALICE.into()), - Box::new(tranche_multi_asset), - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(PARA_ID_SIBLING), - Junction::AccountId32 { - network: None, - id: BOB, - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000_000.into()), - ), - orml_xtokens::Error::::XcmExecutionFailed - ); - }); - } - - #[test] - fn xtokens_transfer_multiassets() { - use codec::Encode; - - let tranche_currency = CurrencyId::Tranche(401, [0; 16]); - let tranche_id = - WeakBoundedVec::>::force_from(tranche_currency.encode(), None); - let tranche_location = MultiLocation { - parents: 1, - interior: X3( - Parachain(123), - PalletInstance(42), - GeneralKey { - length: tranche_id.len() as u8, - data: vec_to_fixed_array(tranche_id.to_vec()), - }, - ), - }; - let tranche_multi_asset = MultiAsset::from(( - AssetId::Concrete(tranche_location), - Fungibility::Fungible(42), - )); - - Altair::execute_with(|| { - assert_noop!( - XTokens::transfer_multiassets( - RuntimeOrigin::signed(ALICE.into()), - Box::new(VersionedMultiAssets::from(MultiAssets::from(vec![ - tranche_multi_asset - ]))), - 0, - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(PARA_ID_SIBLING), - Junction::AccountId32 { - network: None, - id: BOB, - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000_000.into()), - ), - orml_xtokens::Error::::XcmExecutionFailed - ); - }); - } -} diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/kusama/setup.rs b/runtime/integration-tests/src/liquidity_pools/pallet/kusama/setup.rs deleted file mode 100644 index c734d8fbec..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/kusama/setup.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2021 Centrifuge Foundation (centrifuge.io). -// -// This file is part of the Centrifuge chain project. -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -pub use altair_runtime::{Runtime, RuntimeOrigin, System}; -use cfg_primitives::{constants::currency_decimals, parachains, AccountId, Balance}; -use cfg_types::tokens::{CurrencyId, CustomMetadata}; -use frame_support::traits::GenesisBuild; -use orml_traits::asset_registry::AssetMetadata; - -/// Accounts -pub const ALICE: [u8; 32] = [4u8; 32]; -pub const BOB: [u8; 32] = [5u8; 32]; - -/// A PARA ID used for a sibling parachain. -/// It must be one that doesn't collide with any other in use. -pub const PARA_ID_SIBLING: u32 = 3000; - -/// The test asset id attributed to KSM -pub const KSM_ASSET_ID: CurrencyId = CurrencyId::ForeignAsset(1000); - -pub struct ExtBuilder { - balances: Vec<(AccountId, CurrencyId, Balance)>, - parachain_id: u32, -} - -impl Default for ExtBuilder { - fn default() -> Self { - Self { - balances: vec![], - parachain_id: parachains::kusama::altair::ID, - } - } -} - -impl ExtBuilder { - pub fn balances(mut self, balances: Vec<(AccountId, CurrencyId, Balance)>) -> Self { - self.balances = balances; - self - } - - pub fn parachain_id(mut self, parachain_id: u32) -> Self { - self.parachain_id = parachain_id; - self - } - - pub fn build(self) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default() - .build_storage::() - .unwrap(); - let native_currency_id = altair_runtime::NativeToken::get(); - pallet_balances::GenesisConfig:: { - balances: self - .balances - .clone() - .into_iter() - .filter(|(_, currency_id, _)| *currency_id == native_currency_id) - .map(|(account_id, _, initial_balance)| (account_id, initial_balance)) - .collect::>(), - } - .assimilate_storage(&mut t) - .unwrap(); - - orml_tokens::GenesisConfig:: { - balances: self - .balances - .into_iter() - .filter(|(_, currency_id, _)| *currency_id != native_currency_id) - .collect::>(), - } - .assimilate_storage(&mut t) - .unwrap(); - - >::assimilate_storage( - ¶chain_info::GenesisConfig { - parachain_id: self.parachain_id.into(), - }, - &mut t, - ) - .unwrap(); - - >::assimilate_storage( - &pallet_xcm::GenesisConfig { - safe_xcm_version: Some(2), - }, - &mut t, - ) - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext - } -} - -pub fn air(amount: Balance) -> Balance { - amount * dollar(currency_decimals::NATIVE) -} - -pub fn ausd(amount: Balance) -> Balance { - amount * dollar(currency_decimals::AUSD) -} - -pub fn ksm(amount: Balance) -> Balance { - amount * dollar(currency_decimals::KSM) -} - -pub fn foreign(amount: Balance, decimals: u32) -> Balance { - amount * dollar(decimals) -} - -pub fn dollar(decimals: u32) -> Balance { - 10u128.saturating_pow(decimals) -} - -pub fn sibling_account() -> AccountId { - parachain_account(PARA_ID_SIBLING) -} - -pub fn karura_account() -> AccountId { - parachain_account(parachains::kusama::karura::ID) -} - -pub fn altair_account() -> AccountId { - parachain_account(parachains::kusama::altair::ID) -} - -fn parachain_account(id: u32) -> AccountId { - use sp_runtime::traits::AccountIdConversion; - - polkadot_parachain::primitives::Sibling::from(id).into_account_truncating() -} diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/kusama/test_net.rs b/runtime/integration-tests/src/liquidity_pools/pallet/kusama/test_net.rs deleted file mode 100644 index 445105496f..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/kusama/test_net.rs +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2021 Centrifuge GmbH (centrifuge.io). -// This file is part of Centrifuge chain project. - -// Centrifuge 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 (see http://www.gnu.org/licenses). - -// Centrifuge 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. - -//! Relay chain and parachains emulation. - -use altair_runtime::CurrencyId; -use cfg_primitives::{parachains, AccountId}; -use cumulus_primitives_core::ParaId; -use frame_support::{traits::GenesisBuild, weights::Weight}; -use polkadot_primitives::{BlockNumber, MAX_CODE_SIZE, MAX_POV_SIZE}; -use polkadot_runtime_parachains::configuration::HostConfiguration; -use sp_runtime::traits::AccountIdConversion; -use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; - -use super::setup::{air, ksm, ExtBuilder, ALICE, BOB, PARA_ID_SIBLING}; - -decl_test_relay_chain! { - pub struct KusamaNet { - Runtime = kusama_runtime::Runtime, - RuntimeCall = kusama_runtime::RuntimeCall, - RuntimeEvent = kusama_runtime::RuntimeEvent, - XcmConfig = kusama_runtime::xcm_config::XcmConfig, - MessageQueue = kusama_runtime::MessageQueue, - System = kusama_runtime::System, - new_ext = relay_ext(), - } -} - -decl_test_parachain! { - pub struct Altair { - Runtime = altair_runtime::Runtime, - XcmpMessageHandler = altair_runtime::XcmpQueue, - DmpMessageHandler = altair_runtime::DmpQueue, - new_ext = para_ext(parachains::kusama::altair::ID), - } -} - -decl_test_parachain! { - pub struct Sibling { - Runtime = altair_runtime::Runtime, - XcmpMessageHandler = altair_runtime::XcmpQueue, - DmpMessageHandler = altair_runtime::DmpQueue, - new_ext = para_ext(PARA_ID_SIBLING), - } -} - -decl_test_parachain! { - pub struct Karura { - Runtime = altair_runtime::Runtime, - XcmpMessageHandler = altair_runtime::XcmpQueue, - DmpMessageHandler = altair_runtime::DmpQueue, - new_ext = para_ext(parachains::kusama::karura::ID), - } -} - -decl_test_network! { - pub struct TestNet { - relay_chain = KusamaNet, - parachains = vec![ - // N.B: Ideally, we could use the defined para id constants but doing so - // fails with: "error: arbitrary expressions aren't allowed in patterns" - - // Be sure to use `parachains::kusama::altair::ID` - (2088, Altair), - // Be sure to use `PARA_ID_SIBLING` - (3000, Sibling), - // Be sure to use `parachains::kusama::karura::ID` - (2000, Karura), - ], - } -} - -pub fn relay_ext() -> sp_io::TestExternalities { - use kusama_runtime::{Runtime, System}; - - let mut t = frame_system::GenesisConfig::default() - .build_storage::() - .unwrap(); - - pallet_balances::GenesisConfig:: { - balances: vec![ - (AccountId::from(ALICE), air(2002)), - ( - ParaId::from(parachains::kusama::altair::ID).into_account_truncating(), - air(7), - ), - ( - ParaId::from(PARA_ID_SIBLING).into_account_truncating(), - air(7), - ), - ], - } - .assimilate_storage(&mut t) - .unwrap(); - - polkadot_runtime_parachains::configuration::GenesisConfig:: { - config: default_parachains_host_configuration(), - } - .assimilate_storage(&mut t) - .unwrap(); - - >::assimilate_storage( - &pallet_xcm::GenesisConfig { - safe_xcm_version: Some(2), - }, - &mut t, - ) - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext -} - -pub fn para_ext(parachain_id: u32) -> sp_io::TestExternalities { - ExtBuilder::default() - .balances(vec![ - (AccountId::from(ALICE), CurrencyId::Native, air(10)), - (AccountId::from(BOB), CurrencyId::Native, air(10)), - ]) - .parachain_id(parachain_id) - .build() -} - -fn default_parachains_host_configuration() -> HostConfiguration { - HostConfiguration { - hrmp_channel_max_capacity: u32::MAX, - hrmp_channel_max_total_size: u32::MAX, - hrmp_max_parachain_inbound_channels: 10, - hrmp_max_parachain_outbound_channels: 10, - hrmp_channel_max_message_size: u32::MAX, - // Changed to avoid aritmetic errors within hrmp_close - max_downward_message_size: 100_000u32, - ..Default::default() - } -} diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/kusama/tests/asset_registry.rs b/runtime/integration-tests/src/liquidity_pools/pallet/kusama/tests/asset_registry.rs deleted file mode 100644 index 04b016617f..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/kusama/tests/asset_registry.rs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2021 Centrifuge GmbH (centrifuge.io). -// This file is part of Centrifuge chain project. -// -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -// Copyright 2021 Centrifuge GmbH (centrifuge.io). -// This file is part of Centrifuge chain project. -// -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -use altair_runtime::{Balances, OrmlAssetRegistry, OrmlTokens, RuntimeOrigin, XTokens}; -use cfg_primitives::{constants::currency_decimals, parachains, Balance}; -use cfg_types::{ - tokens::{CrossChainTransferability, CurrencyId, CustomMetadata}, - xcm::XcmMetadata, -}; -use frame_support::{assert_noop, assert_ok}; -use orml_traits::{asset_registry::AssetMetadata, FixedConversionRateProvider, MultiCurrency}; -use runtime_common::{ - xcm::general_key, - xcm_fees::{default_per_second, ksm_per_second}, -}; -use sp_runtime::traits::BadOrigin; -use xcm::{ - latest::{Junction, Junction::*, Junctions::*, MultiLocation, NetworkId}, - prelude::{Parachain, X2}, - VersionedMultiLocation, -}; -use xcm_simulator::TestExt; - -use crate::liquidity_pools::pallet::kusama::{ - setup::{ - air, altair_account, ausd, foreign, karura_account, ksm, sibling_account, ALICE, BOB, - PARA_ID_SIBLING, - }, - test_net::{Altair, Karura, KusamaNet, Sibling, TestNet}, -}; - -#[test] -fn register_air_works() { - Altair::execute_with(|| { - let meta: AssetMetadata = AssetMetadata { - decimals: 18, - name: "Altair".into(), - symbol: "AIR".into(), - existential_deposit: 1_000_000_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::new( - 0, - X1(general_key(parachains::kusama::altair::AIR_KEY)), - ))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta, - Some(CurrencyId::Native) - )); - }); -} - -#[test] -fn register_foreign_asset_works() { - Altair::execute_with(|| { - let meta: AssetMetadata = AssetMetadata { - decimals: 12, - name: "Acala Dollar".into(), - symbol: "AUSD".into(), - existential_deposit: 1_000_000_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::new( - 1, - X2( - Parachain(parachains::kusama::karura::ID), - general_key(parachains::kusama::karura::AUSD_KEY), - ), - ))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta, - Some(CurrencyId::ForeignAsset(42)) - )); - }); -} - -#[test] -// Verify that registering tranche tokens is not allowed through extrinsics -fn register_tranche_asset_blocked() { - Altair::execute_with(|| { - let meta: AssetMetadata = AssetMetadata { - decimals: 12, - name: "Tranche Token 1".into(), - symbol: "TRNCH".into(), - existential_deposit: 1_000_000_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::new( - 1, - X2(Parachain(2000), general_key(&[42])), - ))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - - // It fails with `BadOrigin` even when submitted with `Origin::root` since we - // only allow for tranche tokens to be registered through the pools pallet. - let asset_id = CurrencyId::Tranche(42, [42u8; 16]); - assert_noop!( - OrmlAssetRegistry::register_asset(RuntimeOrigin::root(), meta, Some(asset_id)), - BadOrigin - ); - }); -} diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/kusama/tests/currency_id_convert.rs b/runtime/integration-tests/src/liquidity_pools/pallet/kusama/tests/currency_id_convert.rs deleted file mode 100644 index 0253849a8b..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/kusama/tests/currency_id_convert.rs +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2021 Centrifuge GmbH (centrifuge.io). -// This file is part of Centrifuge chain project. -// -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -// Copyright 2021 Centrifuge GmbH (centrifuge.io). -// This file is part of Centrifuge chain project. -// -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -use altair_runtime::{ - Balances, CurrencyIdConvert, OrmlAssetRegistry, OrmlTokens, PoolPalletIndex, RuntimeOrigin, - XTokens, -}; -use cfg_primitives::{constants::currency_decimals, parachains, Balance}; -use cfg_types::{ - tokens::{CurrencyId, CustomMetadata}, - xcm::XcmMetadata, -}; -use cfg_utils::vec_to_fixed_array; -use codec::Encode; -use frame_support::{assert_ok, traits::Len}; -use orml_traits::{asset_registry::AssetMetadata, FixedConversionRateProvider, MultiCurrency}; -use runtime_common::{ - xcm::general_key, - xcm_fees::{default_per_second, ksm_per_second}, -}; -use sp_runtime::{ - traits::{ConstU32, Convert as C2}, - WeakBoundedVec, -}; -use xcm::{ - latest::{Error::BadOrigin, Junction, Junction::*, Junctions::*, MultiLocation, NetworkId}, - VersionedMultiLocation, -}; -use xcm_executor::traits::Convert as C1; -use xcm_simulator::TestExt; - -use crate::{ - liquidity_pools::pallet::kusama::{ - setup::{ - air, altair_account, ausd, foreign, karura_account, ksm, sibling_account, ALICE, BOB, - KSM_ASSET_ID, PARA_ID_SIBLING, - }, - test_net::{Altair, Karura, KusamaNet, Sibling, TestNet}, - tests::{register_air, register_ausd, register_ksm}, - }, - utils::AUSD_CURRENCY_ID, -}; - -#[test] -fn convert_air() { - assert_eq!(parachains::kusama::altair::AIR_KEY.to_vec(), vec![0, 1]); - - Altair::execute_with(|| { - // The way AIR is represented relative within the Altair runtime - let air_location_inner: MultiLocation = - MultiLocation::new(0, X1(general_key(parachains::kusama::altair::AIR_KEY))); - - // register air - register_air(); - - assert_eq!( - >::convert(air_location_inner), - Ok(CurrencyId::Native), - ); - - // The canonical way AIR is represented out in the wild - let air_location_canonical: MultiLocation = MultiLocation::new( - 1, - X2( - Parachain(parachains::kusama::altair::ID), - general_key(parachains::kusama::altair::AIR_KEY), - ), - ); - - assert_eq!( - >::convert(CurrencyId::Native), - Some(air_location_canonical) - ) - }); -} - -/// Verify that Tranche tokens are not handled by the CurrencyIdConvert -/// since we don't allow Tranche tokens to be transferable through XCM. -#[test] -fn convert_tranche() { - let tranche_currency = CurrencyId::Tranche(401, [0; 16]); - let tranche_id = - WeakBoundedVec::>::force_from(tranche_currency.encode(), None); - let tranche_multilocation = MultiLocation { - parents: 1, - interior: X3( - Parachain(parachains::kusama::altair::ID), - PalletInstance(PoolPalletIndex::get()), - GeneralKey { - length: tranche_id.len() as u8, - data: vec_to_fixed_array(tranche_id.to_vec()), - }, - ), - }; - - Altair::execute_with(|| { - assert_eq!( - >::convert(tranche_multilocation), - Err(tranche_multilocation), - ); - }); - - Altair::execute_with(|| { - assert_eq!( - >::convert(tranche_currency), - None - ) - }); -} - -#[test] -fn convert_ausd() { - Altair::execute_with(|| { - assert_eq!(parachains::kusama::karura::AUSD_KEY, &[0, 129]); - - let ausd_location: MultiLocation = MultiLocation::new( - 1, - X2( - Parachain(parachains::kusama::karura::ID), - general_key(parachains::kusama::karura::AUSD_KEY), - ), - ); - - register_ausd(); - - assert_eq!( - >::convert(ausd_location.clone()), - Ok(AUSD_CURRENCY_ID), - ); - - assert_eq!( - >::convert(AUSD_CURRENCY_ID), - Some(ausd_location) - ) - }); -} - -#[test] -fn convert_ksm() { - let ksm_location: MultiLocation = MultiLocation::parent().into(); - - Altair::execute_with(|| { - register_ksm(); - - assert_eq!( - >::convert(ksm_location), - Ok(KSM_ASSET_ID), - ); - - assert_eq!( - >::convert(KSM_ASSET_ID), - Some(ksm_location) - ) - }); -} - -#[test] -fn convert_unkown_multilocation() { - let unknown_location: MultiLocation = MultiLocation::new( - 1, - X2( - Parachain(parachains::kusama::altair::ID), - general_key(&[42]), - ), - ); - - Altair::execute_with(|| { - assert!(>::convert(unknown_location).is_err()); - }); -} - -#[test] -fn convert_unsupported_currency() { - Altair::execute_with(|| { - assert_eq!( - >::convert(CurrencyId::Tranche( - 0, - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] - )), - None - ) - }); -} diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/kusama/tests/mod.rs b/runtime/integration-tests/src/liquidity_pools/pallet/kusama/tests/mod.rs deleted file mode 100644 index 806d045ae0..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/kusama/tests/mod.rs +++ /dev/null @@ -1,98 +0,0 @@ -use centrifuge_runtime::{OrmlAssetRegistry, RuntimeOrigin}; -use cfg_primitives::{parachains, Balance}; -use cfg_types::{ - tokens::{CrossChainTransferability, CurrencyId, CustomMetadata}, - xcm::XcmMetadata, -}; -use frame_support::assert_ok; -use orml_traits::asset_registry::AssetMetadata; -use runtime_common::{xcm::general_key, xcm_fees::ksm_per_second}; -use xcm::{ - latest::MultiLocation, - prelude::{Here, Parachain, X2}, - VersionedMultiLocation, -}; - -use crate::{liquidity_pools::pallet::kusama::setup::KSM_ASSET_ID, utils::AUSD_CURRENCY_ID}; - -mod asset_registry; -mod currency_id_convert; -mod transfers; - -/// Register AIR in the asset registry. -/// It should be executed within an externalities environment. -fn register_air() { - let meta: AssetMetadata = AssetMetadata { - decimals: 18, - name: "Altair".into(), - symbol: "AIR".into(), - existential_deposit: 1_000_000_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::new( - 1, - X2( - Parachain(parachains::kusama::altair::ID), - general_key(parachains::kusama::altair::AIR_KEY), - ), - ))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta, - Some(CurrencyId::Native) - )); -} - -/// Register AUSD in the asset registry. -/// It should be executed within an externalities environment. -fn register_ausd() { - let meta: AssetMetadata = AssetMetadata { - decimals: 12, - name: "Acala Dollar".into(), - symbol: "AUSD".into(), - existential_deposit: 1_000_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::new( - 1, - X2( - Parachain(parachains::kusama::karura::ID), - general_key(parachains::kusama::karura::AUSD_KEY), - ), - ))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta, - Some(AUSD_CURRENCY_ID) - )); -} - -/// Register KSM in the asset registry. -/// It should be executed within an externalities environment. -fn register_ksm() { - let meta: AssetMetadata = AssetMetadata { - decimals: 12, - name: "Kusama".into(), - symbol: "KSM".into(), - existential_deposit: 1_000_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::new(1, Here))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta, - Some(KSM_ASSET_ID) - )); -} diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/kusama/tests/transfers.rs b/runtime/integration-tests/src/liquidity_pools/pallet/kusama/tests/transfers.rs deleted file mode 100644 index 6ff029a58a..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/kusama/tests/transfers.rs +++ /dev/null @@ -1,621 +0,0 @@ -// Copyright 2021 Centrifuge GmbH (centrifuge.io). -// This file is part of Centrifuge chain project. -// -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -// Copyright 2021 Centrifuge GmbH (centrifuge.io). -// This file is part of Centrifuge chain project. -// -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -use altair_runtime::{Balances, OrmlAssetRegistry, OrmlTokens, RuntimeOrigin, XTokens}; -use cfg_primitives::{constants::currency_decimals, parachains, Balance}; -use cfg_types::{ - tokens::{CrossChainTransferability, CurrencyId, CustomMetadata}, - xcm::XcmMetadata, -}; -use frame_support::assert_ok; -use orml_traits::{asset_registry::AssetMetadata, FixedConversionRateProvider, MultiCurrency}; -use runtime_common::{ - xcm::general_key, - xcm_fees::{default_per_second, ksm_per_second}, -}; -use sp_runtime::DispatchError::BadOrigin; -use xcm::{ - latest::{Junction, Junction::*, Junctions::*, MultiLocation, NetworkId, WeightLimit}, - VersionedMultiLocation, -}; -use xcm_simulator::TestExt; - -use crate::{ - liquidity_pools::pallet::{ - kusama::{ - setup::{ - air, altair_account, ausd, foreign, karura_account, ksm, sibling_account, ALICE, - BOB, PARA_ID_SIBLING, - }, - test_net::{Altair, Karura, KusamaNet, Sibling, TestNet}, - tests::register_ausd, - }, - xcm_metadata, - }, - utils::AUSD_CURRENCY_ID, -}; - -/* - -NOTE: We hardcode the expected balances after an XCM operation given that the weights involved in -XCM execution often change slightly with each Polkadot update. We could simply test that the final -balance after some XCM operation is `initialBalance - amount - fee`, which would mean we would -never have to touch the tests again. However, by hard-coding these values we are forced to catch -an unexpectedly big change that would have a big impact on the weights and fees and thus balances, -which would go unnoticed and untreated otherwise. - - */ - -#[test] -fn transfer_air_to_sibling() { - TestNet::reset(); - - let alice_initial_balance = air(10); - let bob_initial_balance = air(10); - let transfer_amount = air(1); - let transfer_amount = air(5); - let air_in_sibling = CurrencyId::ForeignAsset(12); - - Altair::execute_with(|| { - assert_eq!(Balances::free_balance(&ALICE.into()), alice_initial_balance); - assert_eq!(Balances::free_balance(&sibling_account()), 0); - - // Register AIR as foreign asset in the sibling parachain - let meta: AssetMetadata = AssetMetadata { - decimals: 18, - name: "Altair".into(), - symbol: "AIR".into(), - existential_deposit: 1_000_000_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::new( - 1, - X2( - Parachain(parachains::kusama::altair::ID), - general_key(parachains::kusama::altair::AIR_KEY), - ), - ))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta, - Some(CurrencyId::Native) - )); - }); - - Sibling::execute_with(|| { - assert_eq!(OrmlTokens::free_balance(air_in_sibling, &BOB.into()), 0); - - // Register AIR as foreign asset in the sibling parachain - let meta: AssetMetadata = AssetMetadata { - decimals: 18, - name: "Altair".into(), - symbol: "AIR".into(), - existential_deposit: 1_000_000_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::new( - 1, - X2( - Parachain(parachains::kusama::altair::ID), - general_key(parachains::kusama::altair::AIR_KEY), - ), - ))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta, - Some(air_in_sibling) - )); - }); - - Altair::execute_with(|| { - assert_ok!(XTokens::transfer( - RuntimeOrigin::signed(ALICE.into()), - CurrencyId::Native, - transfer_amount, - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(PARA_ID_SIBLING), - Junction::AccountId32 { - network: None, - id: BOB, - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000_000.into()), - )); - - // Confirm that Alice's balance is initial balance - amount transferred - assert_eq!( - Balances::free_balance(&ALICE.into()), - alice_initial_balance - transfer_amount - ); - - // Verify that the amount transferred is now part of the sibling account here - assert_eq!(Balances::free_balance(&sibling_account()), transfer_amount); - }); - - Sibling::execute_with(|| { - let current_balance = OrmlTokens::free_balance(air_in_sibling, &BOB.into()); - - // Verify that BOB now has (amount transferred - fee) - assert_eq!(current_balance, transfer_amount - fee(18)); - - // Sanity check for the actual amount BOB ends up with - assert_eq!(current_balance, 4991987200000000000); - }); -} - -#[test] -fn transfer_air_sibling_to_altair() { - TestNet::reset(); - - // In order to be able to transfer AIR from Sibling to Altair, we need to first - // send AIR from Altair to Sibling, or else it fails since it'd be like Sibling - // had minted AIR on their side. - transfer_air_to_sibling(); - - let alice_initial_balance = air(5); - let bob_initial_balance = air(5) - air_fee(); - let transfer_amount = air(1); - // Note: This asset was registered in `transfer_air_to_sibling` - let air_in_sibling = CurrencyId::ForeignAsset(12); - - Altair::execute_with(|| { - assert_eq!(Balances::free_balance(&ALICE.into()), alice_initial_balance); - }); - - Sibling::execute_with(|| { - assert_eq!(Balances::free_balance(&altair_account()), 0); - assert_eq!( - OrmlTokens::free_balance(air_in_sibling, &BOB.into()), - bob_initial_balance - ); - }); - - Sibling::execute_with(|| { - assert_ok!(XTokens::transfer( - RuntimeOrigin::signed(BOB.into()), - air_in_sibling, - transfer_amount, - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(parachains::kusama::altair::ID), - Junction::AccountId32 { - network: None, - id: ALICE, - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000_000.into()), - )); - - // Confirm that Bobs's balance is initial balance - amount transferred - assert_eq!( - OrmlTokens::free_balance(air_in_sibling, &BOB.into()), - bob_initial_balance - transfer_amount - ); - }); - - Altair::execute_with(|| { - // Verify that ALICE now has initial balance + amount transferred - fee - assert_eq!( - Balances::free_balance(&ALICE.into()), - alice_initial_balance + transfer_amount - air_fee(), - ); - }); -} - -#[test] -fn transfer_ausd_to_altair() { - TestNet::reset(); - - let alice_initial_balance = ausd(10); - let transfer_amount = ausd(7); - - Karura::execute_with(|| { - register_ausd(); - - assert_ok!(OrmlTokens::deposit( - AUSD_CURRENCY_ID, - &ALICE.into(), - alice_initial_balance - )); - - assert_eq!( - OrmlTokens::free_balance(AUSD_CURRENCY_ID, &altair_account()), - 0 - ); - }); - - Altair::execute_with(|| { - register_ausd(); - - assert_eq!(OrmlTokens::free_balance(AUSD_CURRENCY_ID, &BOB.into()), 0,); - }); - - Karura::execute_with(|| { - assert_eq!( - OrmlTokens::free_balance(AUSD_CURRENCY_ID, &ALICE.into()), - ausd(10), - ); - assert_ok!(XTokens::transfer( - RuntimeOrigin::signed(ALICE.into()), - AUSD_CURRENCY_ID, - transfer_amount, - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(parachains::kusama::altair::ID), - Junction::AccountId32 { - network: None, - id: BOB, - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000_000.into()), - )); - - assert_eq!( - OrmlTokens::free_balance(AUSD_CURRENCY_ID, &ALICE.into()), - alice_initial_balance - transfer_amount - ); - - // Verify that the amount transferred is now part of the altair parachain - // account here - assert_eq!( - OrmlTokens::free_balance(AUSD_CURRENCY_ID, &altair_account()), - transfer_amount - ); - }); - - Altair::execute_with(|| { - // Verify that BOB now has initial balance + amount transferred - fee - assert_eq!( - OrmlTokens::free_balance(AUSD_CURRENCY_ID, &BOB.into()), - transfer_amount - ausd_fee() - ); - }); -} - -#[test] -fn transfer_ksm_from_relay_chain() { - let transfer_amount: Balance = ksm(2); - let currency_id = CurrencyId::ForeignAsset(3001); - let meta: AssetMetadata = AssetMetadata { - decimals: 12, - name: "Kusama".into(), - symbol: "KSM".into(), - existential_deposit: 1_000_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::new(1, Here))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - - Altair::execute_with(|| { - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta.clone(), - Some(currency_id), - )); - - assert_eq!(OrmlTokens::free_balance(currency_id, &BOB.into()), 0); - }); - - KusamaNet::execute_with(|| { - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - AssetMetadata { - location: Some(VersionedMultiLocation::V3(MultiLocation::new(0, Here))), - ..meta.clone() - }, - Some(CurrencyId::Native), - )); - - assert_ok!(kusama_runtime::XcmPallet::reserve_transfer_assets( - kusama_runtime::RuntimeOrigin::signed(ALICE.into()), - Box::new(Parachain(parachains::kusama::altair::ID).into()), - Box::new( - Junction::AccountId32 { - network: None, - id: BOB, - } - .into() - ), - Box::new((Here, transfer_amount).into()), - 0 - )); - }); - - Altair::execute_with(|| { - assert_eq!( - OrmlTokens::free_balance(currency_id, &BOB.into()), - transfer_amount - fee(meta.decimals) - ); - }); -} - -#[test] -fn transfer_ksm_to_relay_chain() { - // First we need some KSM on Altair - transfer_ksm_from_relay_chain(); - - let currency_id = CurrencyId::ForeignAsset(3001); - Altair::execute_with(|| { - assert_eq!( - OrmlTokens::free_balance(currency_id, &BOB.into()), - 1991987200000 - ); - - assert_ok!(XTokens::transfer( - RuntimeOrigin::signed(BOB.into()), - currency_id, - ksm(1), - Box::new( - MultiLocation::new( - 1, - X1(Junction::AccountId32 { - id: BOB, - network: None, - }) - ) - .into() - ), - WeightLimit::Limited(4_000_000_000.into()) - )); - }); - - KusamaNet::execute_with(|| { - assert_eq!( - kusama_runtime::Balances::free_balance(&BOB.into()), - 999909712564 - ); - }); -} - -#[test] -fn transfer_foreign_sibling_to_altair() { - TestNet::reset(); - - let alice_initial_balance = air(10); - let sibling_asset_id = CurrencyId::ForeignAsset(1); - let asset_location = - MultiLocation::new(1, X2(Parachain(PARA_ID_SIBLING), general_key(&[0, 1]))); - let meta: AssetMetadata = AssetMetadata { - decimals: 18, - name: "Sibling Native Token".into(), - symbol: "SBLNG".into(), - existential_deposit: 1_000_000_000_000, - location: Some(VersionedMultiLocation::V3(asset_location)), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(XcmMetadata { - // We specify a custom fee_per_second and verify below that this value is - // used when XCM transfer fees are charged for this token. - fee_per_second: Some(8420000000000000000), - }), - ..CustomMetadata::default() - }, - }; - let transfer_amount = foreign(1, meta.decimals); - - Sibling::execute_with(|| { - assert_eq!(OrmlTokens::free_balance(sibling_asset_id, &BOB.into()), 0); - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta.clone(), - Some(CurrencyId::Native), - )); - }); - - Altair::execute_with(|| { - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta.clone(), - Some(sibling_asset_id) - )); - }); - - Sibling::execute_with(|| { - assert_ok!(XTokens::transfer( - RuntimeOrigin::signed(ALICE.into()), - CurrencyId::Native, - transfer_amount, - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(parachains::kusama::altair::ID), - Junction::AccountId32 { - network: None, - id: BOB, - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000_000.into()), - )); - - // Confirm that Alice's balance is initial balance - amount transferred - assert_eq!( - Balances::free_balance(&ALICE.into()), - alice_initial_balance - transfer_amount - ); - }); - - Altair::execute_with(|| { - let bob_balance = OrmlTokens::free_balance(sibling_asset_id, &BOB.into()); - - // Verify that BOB now has initial balance + amount transferred - fee - assert_eq!( - bob_balance, - transfer_amount - - calc_fee( - xcm_metadata(meta.additional.transferability) - .unwrap() - .fee_per_second - .unwrap() - ) - ); - // Sanity check to ensure the calculated is what is expected - assert_eq!(bob_balance, 993264000000000000); - }); -} - -#[test] -fn transfer_wormhole_usdc_karura_to_altair() { - TestNet::reset(); - - let usdc_asset_id = CurrencyId::ForeignAsset(39); - let asset_location = MultiLocation::new( - 1, - X2( - Parachain(parachains::kusama::karura::ID), - general_key("0x02f3a00dd12f644daec907013b16eb6d14bf1c4cb4".as_bytes()), - ), - ); - let meta: AssetMetadata = AssetMetadata { - decimals: 6, - name: "Wormhole USDC".into(), - symbol: "WUSDC".into(), - existential_deposit: 1, - location: Some(VersionedMultiLocation::V3(asset_location)), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - let transfer_amount = foreign(12, meta.decimals); - let alice_initial_balance = transfer_amount * 100; - - Karura::execute_with(|| { - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta.clone(), - Some(usdc_asset_id) - )); - assert_ok!(OrmlTokens::deposit( - usdc_asset_id, - &ALICE.into(), - alice_initial_balance - )); - assert_eq!( - OrmlTokens::free_balance(usdc_asset_id, &ALICE.into()), - alice_initial_balance - ); - assert_eq!(Balances::free_balance(&ALICE.into()), air(10)); - }); - - Altair::execute_with(|| { - // First, register the asset in centrifuge - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta.clone(), - Some(usdc_asset_id) - )); - }); - - Karura::execute_with(|| { - assert_ok!(XTokens::transfer( - RuntimeOrigin::signed(ALICE.into()), - usdc_asset_id, - transfer_amount, - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(parachains::kusama::altair::ID), - Junction::AccountId32 { - network: None, - id: BOB, - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000.into()), - )); - - // Confirm that Alice's balance is initial balance - amount transferred - assert_eq!( - OrmlTokens::free_balance(usdc_asset_id, &ALICE.into()), - alice_initial_balance - transfer_amount - ); - }); - - Altair::execute_with(|| { - let bob_balance = OrmlTokens::free_balance(usdc_asset_id, &BOB.into()); - - // Sanity check to ensure the calculated is what is expected - assert_eq!(bob_balance, 11991988); - }); -} - -#[test] -fn test_total_fee() { - assert_eq!(air_fee(), 8012800000000000); -} - -fn air_fee() -> Balance { - fee(currency_decimals::NATIVE) -} - -fn ausd_fee() -> Balance { - fee(currency_decimals::AUSD) -} - -fn fee(decimals: u32) -> Balance { - calc_fee(default_per_second(decimals)) -} - -// The fee associated with transferring KSM tokens -fn ksm_fee() -> Balance { - calc_fee(ksm_per_second()) -} - -fn calc_fee(fee_per_second: Balance) -> Balance { - // We divide the fee to align its unit and multiply by 4 as that seems to be the - // unit of time the tests take. - // NOTE: it is possible that in different machines this value may differ. We - // shall see. - fee_per_second.div_euclid(10_000) * 8 -} diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/mod.rs b/runtime/integration-tests/src/liquidity_pools/pallet/mod.rs index 2a807c35ca..40b4e588a2 100644 --- a/runtime/integration-tests/src/liquidity_pools/pallet/mod.rs +++ b/runtime/integration-tests/src/liquidity_pools/pallet/mod.rs @@ -13,14 +13,3 @@ use cfg_types::{tokens::CrossChainTransferability, xcm::XcmMetadata}; mod development; -mod kusama; -mod polkadot; - -/// Get the `XcmMetadata` for a given `CrossChainTransferability` value if -/// possible. -fn xcm_metadata(transferability: CrossChainTransferability) -> Option { - match transferability { - CrossChainTransferability::Xcm(x) | CrossChainTransferability::All(x) => Some(x), - _ => None, - } -} diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/mod.rs b/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/mod.rs deleted file mode 100644 index 34f13591a7..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod setup; -mod test_net; -mod tests; diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/setup.rs b/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/setup.rs deleted file mode 100644 index e6fa66adf9..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/setup.rs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2021 Centrifuge Foundation (centrifuge.io). -// -// This file is part of the Centrifuge chain project. -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -use altair_runtime::constants; -pub use centrifuge_runtime::{Runtime, RuntimeOrigin, System}; -use cfg_primitives::{constants::currency_decimals, parachains, AccountId, Balance}; -use cfg_types::tokens::{CurrencyId, CustomMetadata}; -use frame_support::traits::GenesisBuild; -use orml_traits::asset_registry::AssetMetadata; - -/// Accounts -pub const ALICE: [u8; 32] = [4u8; 32]; -pub const BOB: [u8; 32] = [5u8; 32]; - -/// A PARA ID used for a sibling parachain. -/// It must be one that doesn't collide with any other in use. -pub const PARA_ID_SIBLING: u32 = 3000; - -/// The test asset id attributed to DOT -pub const DOT_ASSET_ID: CurrencyId = CurrencyId::ForeignAsset(91); -/// An Asset that is NOT XCM transferable -pub const NO_XCM_ASSET_ID: CurrencyId = CurrencyId::ForeignAsset(401); - -pub struct ExtBuilder { - balances: Vec<(AccountId, CurrencyId, Balance)>, - parachain_id: u32, -} - -impl Default for ExtBuilder { - fn default() -> Self { - Self { - balances: vec![], - parachain_id: parachains::polkadot::centrifuge::ID, - } - } -} - -impl ExtBuilder { - pub fn balances(mut self, balances: Vec<(AccountId, CurrencyId, Balance)>) -> Self { - self.balances = balances; - self - } - - pub fn parachain_id(mut self, parachain_id: u32) -> Self { - self.parachain_id = parachain_id; - self - } - - pub fn build(self) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default() - .build_storage::() - .unwrap(); - let native_currency_id = centrifuge_runtime::NativeToken::get(); - pallet_balances::GenesisConfig:: { - balances: self - .balances - .clone() - .into_iter() - .filter(|(_, currency_id, _)| *currency_id == native_currency_id) - .map(|(account_id, _, initial_balance)| (account_id, initial_balance)) - .collect::>(), - } - .assimilate_storage(&mut t) - .unwrap(); - - orml_tokens::GenesisConfig:: { - balances: self - .balances - .into_iter() - .filter(|(_, currency_id, _)| *currency_id != native_currency_id) - .collect::>(), - } - .assimilate_storage(&mut t) - .unwrap(); - - >::assimilate_storage( - ¶chain_info::GenesisConfig { - parachain_id: self.parachain_id.into(), - }, - &mut t, - ) - .unwrap(); - - >::assimilate_storage( - &pallet_xcm::GenesisConfig { - safe_xcm_version: Some(2), - }, - &mut t, - ) - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext - } -} - -pub fn cfg(amount: Balance) -> Balance { - amount * dollar(currency_decimals::NATIVE) -} - -pub fn ausd(amount: Balance) -> Balance { - amount * dollar(currency_decimals::AUSD) -} - -pub fn dot(amount: Balance) -> Balance { - amount * dollar(10) -} - -pub fn foreign(amount: Balance, decimals: u32) -> Balance { - amount * dollar(decimals) -} - -pub fn dollar(decimals: u32) -> Balance { - 10u128.saturating_pow(decimals) -} - -pub fn sibling_account() -> AccountId { - parachain_account(PARA_ID_SIBLING) -} - -pub fn acala_account() -> AccountId { - parachain_account(parachains::polkadot::acala::ID) -} - -pub fn centrifuge_account() -> AccountId { - parachain_account(parachains::polkadot::centrifuge::ID) -} - -fn parachain_account(id: u32) -> AccountId { - use sp_runtime::traits::AccountIdConversion; - - polkadot_parachain::primitives::Sibling::from(id).into_account_truncating() -} diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/test_net.rs b/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/test_net.rs deleted file mode 100644 index 1f993c4913..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/test_net.rs +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2021 Centrifuge GmbH (centrifuge.io). -// This file is part of Centrifuge chain project. - -// Centrifuge 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 (see http://www.gnu.org/licenses). - -// Centrifuge 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. - -//! Relay chain and parachains emulation. - -use centrifuge_runtime::CurrencyId; -use cfg_primitives::{parachains, AccountId}; -use cumulus_primitives_core::ParaId; -use frame_support::{traits::GenesisBuild, weights::Weight}; -use polkadot_primitives::{BlockNumber, MAX_CODE_SIZE, MAX_POV_SIZE}; -use polkadot_runtime_parachains::configuration::HostConfiguration; -use sp_runtime::traits::AccountIdConversion; -use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; - -use super::setup::{cfg, dot, ExtBuilder, ALICE, BOB, DOT_ASSET_ID, PARA_ID_SIBLING}; - -decl_test_relay_chain! { - pub struct PolkadotNet { - Runtime = polkadot_runtime::Runtime, - RuntimeCall = polkadot_runtime::RuntimeCall, - RuntimeEvent = polkadot_runtime::RuntimeEvent, - XcmConfig = polkadot_runtime::xcm_config::XcmConfig, - MessageQueue = polkadot_runtime::MessageQueue, - System = polkadot_runtime::System, - new_ext = relay_ext(), - } -} - -decl_test_parachain! { - pub struct Centrifuge { - Runtime = centrifuge_runtime::Runtime, - XcmpMessageHandler = centrifuge_runtime::XcmpQueue, - DmpMessageHandler = centrifuge_runtime::DmpQueue, - new_ext = para_ext(parachains::polkadot::centrifuge::ID), - } -} - -decl_test_parachain! { - pub struct Sibling { - Runtime = centrifuge_runtime::Runtime, - XcmpMessageHandler = centrifuge_runtime::XcmpQueue, - DmpMessageHandler = centrifuge_runtime::DmpQueue, - new_ext = para_ext(PARA_ID_SIBLING), - } -} - -decl_test_parachain! { - pub struct Acala { - Runtime = centrifuge_runtime::Runtime, - XcmpMessageHandler = centrifuge_runtime::XcmpQueue, - DmpMessageHandler = centrifuge_runtime::DmpQueue, - new_ext = para_ext(parachains::polkadot::acala::ID), - } -} - -decl_test_network! { - pub struct TestNet { - relay_chain = PolkadotNet, - parachains = vec![ - // N.B: Ideally, we could use the defined para id constants but doing so - // fails with: "error: arbitrary expressions aren't allowed in patterns" - - // Be sure to use `parachains::polkadot::centrifuge::ID` - (2031, Centrifuge), - // Be sure to use `PARA_ID_SIBLING` - (3000, Sibling), - // Be sure to use `parachains::polkadot::acala::ID` - (2000, Acala), - ], - } -} - -pub fn relay_ext() -> sp_io::TestExternalities { - use polkadot_runtime::{Runtime, System}; - - let mut t = frame_system::GenesisConfig::default() - .build_storage::() - .unwrap(); - - pallet_balances::GenesisConfig:: { - balances: vec![(AccountId::from(ALICE), dot(10))], - } - .assimilate_storage(&mut t) - .unwrap(); - - polkadot_runtime_parachains::configuration::GenesisConfig:: { - config: default_parachains_host_configuration(), - } - .assimilate_storage(&mut t) - .unwrap(); - - >::assimilate_storage( - &pallet_xcm::GenesisConfig { - safe_xcm_version: Some(2), - }, - &mut t, - ) - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext -} - -pub fn para_ext(parachain_id: u32) -> sp_io::TestExternalities { - ExtBuilder::default() - .balances(vec![ - (AccountId::from(ALICE), CurrencyId::Native, cfg(10)), - (AccountId::from(BOB), CurrencyId::Native, cfg(10)), - ]) - .parachain_id(parachain_id) - .build() -} - -fn default_parachains_host_configuration() -> HostConfiguration { - HostConfiguration { - hrmp_channel_max_capacity: u32::MAX, - hrmp_channel_max_total_size: u32::MAX, - hrmp_max_parachain_inbound_channels: 10, - hrmp_max_parachain_outbound_channels: 10, - hrmp_channel_max_message_size: u32::MAX, - // Changed to avoid aritmetic errors within hrmp_close - max_downward_message_size: 100_000u32, - ..Default::default() - } -} diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/tests/asset_registry.rs b/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/tests/asset_registry.rs deleted file mode 100644 index e83ab08e1d..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/tests/asset_registry.rs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2021 Centrifuge GmbH (centrifuge.io). -// This file is part of Centrifuge chain project. -// -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -// Copyright 2021 Centrifuge GmbH (centrifuge.io). -// This file is part of Centrifuge chain project. -// -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -use centrifuge_runtime::{Balances, OrmlAssetRegistry, OrmlTokens, RuntimeOrigin, XTokens}; -use cfg_primitives::{constants::currency_decimals, parachains, Balance}; -use cfg_types::{ - tokens::{CrossChainTransferability, CurrencyId, CustomMetadata}, - xcm::XcmMetadata, -}; -use frame_support::{assert_err, assert_noop, assert_ok, error::BadOrigin}; -use orml_traits::{asset_registry::AssetMetadata, FixedConversionRateProvider, MultiCurrency}; -use runtime_common::{ - xcm::general_key, - xcm_fees::{default_per_second, ksm_per_second}, -}; -use xcm::{ - latest::{Junction, Junction::*, Junctions::*, MultiLocation, NetworkId}, - VersionedMultiLocation, -}; -use xcm_simulator::TestExt; - -use crate::liquidity_pools::pallet::polkadot::{ - setup::{ - acala_account, ausd, centrifuge_account, cfg, dot, foreign, sibling_account, ALICE, BOB, - DOT_ASSET_ID, PARA_ID_SIBLING, - }, - test_net::{Acala, Centrifuge, PolkadotNet, Sibling, TestNet}, -}; - -#[test] -fn register_cfg_works() { - Centrifuge::execute_with(|| { - let meta: AssetMetadata = AssetMetadata { - decimals: 18, - name: "Centrifuge".into(), - symbol: "CFG".into(), - existential_deposit: 1_000_000_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::new( - 0, - X1(general_key(parachains::polkadot::centrifuge::CFG_KEY)), - ))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta, - Some(CurrencyId::Native) - )); - }); -} - -#[test] -fn register_foreign_asset_works() { - Centrifuge::execute_with(|| { - let meta: AssetMetadata = AssetMetadata { - decimals: 12, - name: "Acala Dollar".into(), - symbol: "AUSD".into(), - existential_deposit: 1_000_000_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::new( - 1, - X2( - Parachain(parachains::polkadot::acala::ID), - general_key(parachains::polkadot::acala::AUSD_KEY), - ), - ))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta, - Some(CurrencyId::ForeignAsset(42)) - )); - }); -} - -#[test] -// Verify that registering tranche tokens is not allowed through extrinsics -fn register_tranche_asset_blocked() { - Centrifuge::execute_with(|| { - let meta: AssetMetadata = AssetMetadata { - decimals: 12, - name: "Tranche Token 1".into(), - symbol: "TRNCH".into(), - existential_deposit: 1_000_000_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::new( - 1, - X2(Parachain(2000), general_key(&[42])), - ))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - - // It fails with `BadOrigin` even when submitted with `Origin::root` since we - // only allow for tranche tokens to be registered through the pools pallet. - let asset_id = CurrencyId::Tranche(42, [42u8; 16]); - assert_noop!( - OrmlAssetRegistry::register_asset(RuntimeOrigin::root(), meta, Some(asset_id)), - BadOrigin - ); - }); -} diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/tests/currency_id_convert.rs b/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/tests/currency_id_convert.rs deleted file mode 100644 index a9e4dcbfe8..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/tests/currency_id_convert.rs +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2021 Centrifuge GmbH (centrifuge.io). -// This file is part of Centrifuge chain project. -// -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -// Copyright 2021 Centrifuge GmbH (centrifuge.io). -// This file is part of Centrifuge chain project. -// -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -use centrifuge_runtime::{ - Balances, CurrencyIdConvert, OrmlAssetRegistry, OrmlTokens, RuntimeOrigin, XTokens, -}; -use cfg_primitives::{constants::currency_decimals, parachains, Balance}; -use cfg_types::{ - tokens::{CurrencyId, CustomMetadata}, - xcm::XcmMetadata, -}; -use cfg_utils::vec_to_fixed_array; -use codec::Encode; -use frame_support::{assert_noop, assert_ok}; -use orml_traits::{asset_registry::AssetMetadata, FixedConversionRateProvider, MultiCurrency}; -use runtime_common::{ - xcm::general_key, - xcm_fees::{default_per_second, ksm_per_second}, -}; -use sp_runtime::{ - traits::{ConstU32, Convert as C2}, - WeakBoundedVec, -}; -use xcm::{ - latest::{Junction, Junction::*, Junctions::*, MultiLocation, NetworkId}, - VersionedMultiLocation, -}; -use xcm_executor::traits::Convert as C1; -use xcm_simulator::TestExt; - -use super::register_dot; -use crate::{ - liquidity_pools::pallet::polkadot::{ - setup::{ - acala_account, ausd, centrifuge_account, cfg, dot, foreign, sibling_account, ALICE, - BOB, DOT_ASSET_ID, NO_XCM_ASSET_ID, PARA_ID_SIBLING, - }, - test_net::{Acala, Centrifuge, PolkadotNet, Sibling, TestNet}, - tests::{register_ausd, register_cfg, register_cfg_v2, register_no_xcm_token}, - }, - utils::AUSD_CURRENCY_ID, -}; - -#[test] -fn convert_cfg() { - assert_eq!(parachains::polkadot::centrifuge::CFG_KEY, &[0, 1]); - - Centrifuge::execute_with(|| { - // The way CFG is represented relative within the Centrifuge runtime - let cfg_location_inner: MultiLocation = MultiLocation::new( - 0, - X1(general_key(parachains::polkadot::centrifuge::CFG_KEY)), - ); - - register_cfg(); - - assert_eq!( - >::convert(cfg_location_inner), - Ok(CurrencyId::Native), - ); - - // The canonical way CFG is represented out in the wild - let cfg_location_canonical: MultiLocation = MultiLocation::new( - 1, - X2( - Parachain(parachains::polkadot::centrifuge::ID), - general_key(parachains::polkadot::centrifuge::CFG_KEY), - ), - ); - - assert_eq!( - >::convert(CurrencyId::Native), - Some(cfg_location_canonical) - ) - }); -} - -/// Verify that even with CFG registered in the AssetRegistry with a XCM v2 -/// MultiLocation, that `CurrencyIdConvert` can look it up given an identical -/// location in XCM v3. -#[test] -fn convert_cfg_xcm_v2() { - assert_eq!(parachains::polkadot::centrifuge::CFG_KEY, &[0, 1]); - - Centrifuge::execute_with(|| { - // Registered as xcm v2 - register_cfg_v2(); - - // The way CFG is represented relative within the Centrifuge runtime in xcm v3 - let cfg_location_inner: MultiLocation = MultiLocation::new( - 0, - X1(general_key(parachains::polkadot::centrifuge::CFG_KEY)), - ); - - assert_eq!( - >::convert(cfg_location_inner), - Ok(CurrencyId::Native), - ); - - // The canonical way CFG is represented out in the wild - let cfg_location_canonical: MultiLocation = MultiLocation::new( - 1, - X2( - Parachain(parachains::polkadot::centrifuge::ID), - general_key(parachains::polkadot::centrifuge::CFG_KEY), - ), - ); - - assert_eq!( - >::convert(CurrencyId::Native), - Some(cfg_location_canonical) - ) - }); -} - -/// Verify that a registered token that is NOT XCM transferable is filtered out -/// by CurrencyIdConvert as expected. -#[test] -fn convert_no_xcm_token() { - Centrifuge::execute_with(|| { - register_no_xcm_token(); - - assert_eq!( - >::convert(NO_XCM_ASSET_ID), - None - ) - }); -} - -#[test] -fn convert_ausd() { - assert_eq!(parachains::polkadot::acala::AUSD_KEY, &[0, 1]); - - let ausd_location: MultiLocation = MultiLocation::new( - 1, - X2( - Parachain(parachains::polkadot::acala::ID), - general_key(parachains::polkadot::acala::AUSD_KEY), - ), - ); - - Centrifuge::execute_with(|| { - register_ausd(); - - assert_eq!( - >::convert(ausd_location), - Ok(AUSD_CURRENCY_ID), - ); - - assert_eq!( - >::convert(AUSD_CURRENCY_ID), - Some(ausd_location) - ) - }); -} - -#[test] -fn convert_dot() { - let dot_location: MultiLocation = MultiLocation::parent(); - - Centrifuge::execute_with(|| { - register_dot(); - - assert_eq!( - >::convert(dot_location), - Ok(DOT_ASSET_ID), - ); - - assert_eq!( - >::convert(DOT_ASSET_ID), - Some(dot_location) - ) - }); -} - -#[test] -fn convert_unknown_multilocation() { - let unknown_location: MultiLocation = MultiLocation::new( - 1, - X2( - Parachain(parachains::polkadot::centrifuge::ID), - general_key([42].as_ref()), - ), - ); - - Centrifuge::execute_with(|| { - assert!(>::convert(unknown_location).is_err()); - }); -} - -#[test] -fn convert_unsupported_currency() { - Centrifuge::execute_with(|| { - assert_eq!( - >::convert(CurrencyId::Tranche( - 0, - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] - )), - None - ) - }); -} diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/tests/mod.rs b/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/tests/mod.rs deleted file mode 100644 index 7b9a9348bf..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/tests/mod.rs +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2021 Centrifuge Foundation (centrifuge.io). -// -// This file is part of the Centrifuge chain project. -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -use centrifuge_runtime::{OrmlAssetRegistry, RuntimeOrigin}; -use cfg_primitives::{parachains, Balance}; -use cfg_types::{ - tokens::{CrossChainTransferability, CurrencyId, CustomMetadata}, - xcm::XcmMetadata, -}; -use frame_support::assert_ok; -use orml_traits::asset_registry::AssetMetadata; -use runtime_common::{xcm::general_key, xcm_fees::ksm_per_second}; -use sp_core::{bounded::WeakBoundedVec, ConstU32}; -use xcm::{ - latest::MultiLocation, - prelude::{Parachain, X2}, - VersionedMultiLocation, -}; - -use crate::{ - liquidity_pools::pallet::polkadot::setup::{DOT_ASSET_ID, NO_XCM_ASSET_ID}, - utils::AUSD_CURRENCY_ID, -}; - -mod asset_registry; -mod currency_id_convert; -mod restricted_calls; -mod transfers; - -/// Register DOT in the asset registry. -/// It should be executed within an externalities environment. -fn register_dot() { - let meta: AssetMetadata = AssetMetadata { - decimals: 10, - name: "Polkadot".into(), - symbol: "DOT".into(), - existential_deposit: 100_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::parent())), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta, - Some(DOT_ASSET_ID) - )); -} - -/// Register AUSD in the asset registry. -/// It should be executed within an externalities environment. -fn register_ausd() { - let meta: AssetMetadata = AssetMetadata { - decimals: 12, - name: "Acala Dollar".into(), - symbol: "AUSD".into(), - existential_deposit: 1_000_000_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::new( - 1, - X2( - Parachain(parachains::polkadot::acala::ID), - general_key(parachains::polkadot::acala::AUSD_KEY), - ), - ))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta, - Some(AUSD_CURRENCY_ID) - )); -} - -/// Register CFG in the asset registry. -/// It should be executed within an externalities environment. -fn register_cfg() { - let meta: AssetMetadata = AssetMetadata { - decimals: 18, - name: "Centrifuge".into(), - symbol: "CFG".into(), - existential_deposit: 1_000_000_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::new( - 1, - X2( - Parachain(parachains::polkadot::centrifuge::ID), - general_key(parachains::polkadot::centrifuge::CFG_KEY), - ), - ))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta, - Some(CurrencyId::Native) - )); -} - -/// Register CFG in the asset registry as XCM v2, just like it is in production. -/// It should be executed within an externalities environment. -fn register_cfg_v2() { - let meta: AssetMetadata = AssetMetadata { - decimals: 18, - name: "Centrifuge".into(), - symbol: "CFG".into(), - existential_deposit: 1_000_000_000_000, - location: Some(VersionedMultiLocation::V2(xcm::v2::MultiLocation::new( - 1, - xcm::v2::Junctions::X2( - xcm::v2::Junction::Parachain(parachains::polkadot::centrifuge::ID), - xcm::v2::Junction::GeneralKey(WeakBoundedVec::>::force_from( - parachains::polkadot::centrifuge::CFG_KEY.into(), - None, - )), - ), - ))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta, - Some(CurrencyId::Native) - )); -} - -/// Register a token whose `CrossChainTransferability` does NOT include XCM. -fn register_no_xcm_token() { - let meta: AssetMetadata = AssetMetadata { - decimals: 18, - name: "NO XCM".into(), - symbol: "NXCM".into(), - existential_deposit: 1_000_000_000_000, - location: None, - additional: CustomMetadata { - transferability: CrossChainTransferability::LiquidityPools, - ..CustomMetadata::default() - }, - }; - - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta, - Some(NO_XCM_ASSET_ID) - )); -} diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/tests/restricted_calls.rs b/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/tests/restricted_calls.rs deleted file mode 100644 index cdac5b02d0..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/tests/restricted_calls.rs +++ /dev/null @@ -1,196 +0,0 @@ -// This file is part of Centrifuge chain project. -// -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -// Copyright 2021 Centrifuge GmbH (centrifuge.io). -// This file is part of Centrifuge chain project. -// -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -use centrifuge_runtime::{Balances, Multisig, PolkadotXcm, RuntimeCall, RuntimeOrigin, XTokens}; -use cfg_primitives::{constants::currency_decimals, parachains, Balance}; -use cfg_types::{ - tokens::{CurrencyId, CustomMetadata}, - xcm::XcmMetadata, -}; -use codec::Encode; -use frame_support::{ - assert_err, assert_noop, assert_ok, dispatch::Dispatchable, traits::WrapperKeepOpaque, -}; -use orml_traits::{asset_registry::AssetMetadata, FixedConversionRateProvider, MultiCurrency}; -use runtime_common::xcm_fees::{default_per_second, ksm_per_second}; -use sp_runtime::{DispatchError, DispatchError::BadOrigin}; -use xcm::{ - latest::{ - AssetId, Fungibility, Junction, Junction::*, Junctions::*, MultiAsset, MultiLocation, - NetworkId, WeightLimit, - }, - v2::{Instruction::WithdrawAsset, Xcm}, - VersionedMultiLocation, -}; -use xcm_simulator::TestExt; - -use crate::liquidity_pools::pallet::polkadot::{ - setup::{ - acala_account, ausd, centrifuge_account, cfg, dot, foreign, sibling_account, ALICE, BOB, - DOT_ASSET_ID, PARA_ID_SIBLING, - }, - test_net::{Acala, Centrifuge, PolkadotNet, Sibling, TestNet}, -}; - -/// Verify that calls that would allow for Tranche token to be transferred -/// through XCM fail because the underlying CurrencyIdConvert doesn't handle -/// Tranche tokens. -pub mod blocked { - use cfg_utils::vec_to_fixed_array; - use frame_support::weights::Weight; - use sp_runtime::{traits::ConstU32, WeakBoundedVec}; - use xcm::{latest::MultiAssets, VersionedMultiAsset, VersionedMultiAssets}; - - use super::*; - - #[test] - fn xtokens_transfer() { - // For now, Tranche tokens are not supported in the XCM config so - // we just safe-guard that trying to transfer a tranche token fails. - Centrifuge::execute_with(|| { - assert_noop!( - XTokens::transfer( - RuntimeOrigin::signed(ALICE.into()), - CurrencyId::Tranche(401, [0; 16]), - 42, - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(PARA_ID_SIBLING), - Junction::AccountId32 { - network: None, - id: BOB, - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000_000.into()), - ), - orml_xtokens::Error::::NotCrossChainTransferableCurrency - ); - }); - } - - // Verify that trying to transfer Tranche tokens using their MultiLocation - // representation also fails. - #[test] - fn xtokens_transfer_multiasset() { - use codec::Encode; - - let tranche_currency = CurrencyId::Tranche(401, [0; 16]); - let tranche_id = - WeakBoundedVec::>::force_from(tranche_currency.encode(), None); - let tranche_location = MultiLocation { - parents: 1, - interior: X3( - Parachain(123), - PalletInstance(42), - GeneralKey { - length: tranche_id.len() as u8, - data: vec_to_fixed_array(tranche_id.to_vec()), - }, - ), - }; - let tranche_multi_asset = VersionedMultiAsset::from(MultiAsset::from(( - AssetId::Concrete(tranche_location), - Fungibility::Fungible(42), - ))); - - Centrifuge::execute_with(|| { - assert_noop!( - XTokens::transfer_multiasset( - RuntimeOrigin::signed(ALICE.into()), - Box::new(tranche_multi_asset), - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(PARA_ID_SIBLING), - Junction::AccountId32 { - network: None, - id: BOB, - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000_000.into()), - ), - orml_xtokens::Error::::XcmExecutionFailed - ); - }); - } - - #[test] - fn xtokens_transfer_multiassets() { - use codec::Encode; - - let tranche_currency = CurrencyId::Tranche(401, [0; 16]); - let tranche_id = - WeakBoundedVec::>::force_from(tranche_currency.encode(), None); - let tranche_location = MultiLocation { - parents: 1, - interior: X3( - Parachain(123), - PalletInstance(42), - GeneralKey { - length: tranche_id.len() as u8, - data: vec_to_fixed_array(tranche_id.to_vec()), - }, - ), - }; - let tranche_multi_asset = MultiAsset::from(( - AssetId::Concrete(tranche_location), - Fungibility::Fungible(42), - )); - - Centrifuge::execute_with(|| { - assert_noop!( - XTokens::transfer_multiassets( - RuntimeOrigin::signed(ALICE.into()), - Box::new(VersionedMultiAssets::from(MultiAssets::from(vec![ - tranche_multi_asset - ]))), - 0, - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(PARA_ID_SIBLING), - Junction::AccountId32 { - network: None, - id: BOB, - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000_000.into()), - ), - orml_xtokens::Error::::XcmExecutionFailed - ); - }); - } -} diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/tests/transfers.rs b/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/tests/transfers.rs deleted file mode 100644 index 1743c1f492..0000000000 --- a/runtime/integration-tests/src/liquidity_pools/pallet/polkadot/tests/transfers.rs +++ /dev/null @@ -1,591 +0,0 @@ -// Copyright 2021 Centrifuge GmbH (centrifuge.io). -// This file is part of Centrifuge chain project. -// -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -// Copyright 2021 Centrifuge GmbH (centrifuge.io). -// This file is part of Centrifuge chain project. -// -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -use centrifuge_runtime::{Balances, OrmlAssetRegistry, OrmlTokens, RuntimeOrigin, XTokens}; -use cfg_primitives::{constants::currency_decimals, parachains, Balance}; -use cfg_types::{ - tokens::{CrossChainTransferability, CurrencyId, CustomMetadata}, - xcm::XcmMetadata, -}; -use frame_support::assert_ok; -use orml_traits::{asset_registry::AssetMetadata, FixedConversionRateProvider, MultiCurrency}; -use runtime_common::{ - xcm::general_key, - xcm_fees::{default_per_second, ksm_per_second}, -}; -use sp_runtime::traits::BadOrigin; -use xcm::{ - latest::{Junction, Junction::*, Junctions::*, MultiLocation, NetworkId, WeightLimit}, - VersionedMultiLocation, -}; -use xcm_simulator::TestExt; - -use super::register_dot; -use crate::{ - liquidity_pools::pallet::{ - polkadot::{ - setup::{ - acala_account, ausd, centrifuge_account, cfg, dot, foreign, sibling_account, ALICE, - BOB, DOT_ASSET_ID, PARA_ID_SIBLING, - }, - test_net::{Acala, Centrifuge, PolkadotNet, Sibling, TestNet}, - tests::register_ausd, - }, - xcm_metadata, - }, - utils::AUSD_CURRENCY_ID, -}; - -/* - -NOTE: We hardcode the expected balances after an XCM operation given that the weights involved in -XCM execution often change slightly with each Polkadot update. We could simply test that the final -balance after some XCM operation is `initialBalance - amount - fee`, which would mean we would -never have to touch the tests again. However, by hard-coding these values we are forced to catch -an unexpectedly big change that would have a big impact on the weights and fees and thus balances, -which would go unnoticed and untreated otherwise. - - */ - -#[test] -fn transfer_cfg_to_sibling() { - TestNet::reset(); - - let alice_initial_balance = cfg(10); - let bob_initial_balance = cfg(10); - let transfer_amount = cfg(1); - let transfer_amount = cfg(5); - let cfg_in_sibling = CurrencyId::ForeignAsset(12); - - // CFG Metadata - let meta: AssetMetadata = AssetMetadata { - decimals: 18, - name: "Centrifuge".into(), - symbol: "CFG".into(), - existential_deposit: 1_000_000_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::new( - 1, - X2( - Parachain(parachains::polkadot::centrifuge::ID), - general_key(parachains::polkadot::centrifuge::CFG_KEY), - ), - ))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - - Centrifuge::execute_with(|| { - assert_eq!(Balances::free_balance(&ALICE.into()), alice_initial_balance); - assert_eq!(Balances::free_balance(&sibling_account()), 0); - - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta.clone(), - Some(CurrencyId::Native), - )); - }); - - Sibling::execute_with(|| { - assert_eq!(OrmlTokens::free_balance(cfg_in_sibling, &BOB.into()), 0); - - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta, - Some(cfg_in_sibling) - )); - }); - - Centrifuge::execute_with(|| { - assert_ok!(XTokens::transfer( - RuntimeOrigin::signed(ALICE.into()), - CurrencyId::Native, - transfer_amount, - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(PARA_ID_SIBLING), - Junction::AccountId32 { - network: None, - id: BOB, - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000_000.into()), - )); - - // Confirm that Alice's balance is initial balance - amount transferred - assert_eq!( - Balances::free_balance(&ALICE.into()), - alice_initial_balance - transfer_amount - ); - - // Verify that the amount transferred is now part of the sibling account here - assert_eq!(Balances::free_balance(&sibling_account()), transfer_amount); - }); - - Sibling::execute_with(|| { - let current_balance = OrmlTokens::free_balance(cfg_in_sibling, &BOB.into()); - - // Verify that BOB now has (amount transferred - fee) - assert_eq!(current_balance, transfer_amount - fee(18)); - - // Sanity check for the actual amount BOB ends up with - assert_eq!(current_balance, 4991987200000000000); - }); -} - -#[test] -fn transfer_cfg_sibling_to_centrifuge() { - TestNet::reset(); - - // In order to be able to transfer CFG from Sibling to Centrifuge, we need to - // first send CFG from Centrifuge to Sibling, or else it fails since it'd be - // like Sibling had minted CFG on their side. - transfer_cfg_to_sibling(); - - let alice_initial_balance = cfg(5); - let bob_initial_balance = cfg(5) - cfg_fee(); - let transfer_amount = cfg(1); - // Note: This asset was registered in `transfer_cfg_to_sibling` - let cfg_in_sibling = CurrencyId::ForeignAsset(12); - - Centrifuge::execute_with(|| { - assert_eq!(Balances::free_balance(&ALICE.into()), alice_initial_balance); - }); - - Sibling::execute_with(|| { - assert_eq!(Balances::free_balance(¢rifuge_account()), 0); - assert_eq!( - OrmlTokens::free_balance(cfg_in_sibling, &BOB.into()), - bob_initial_balance - ); - }); - - Sibling::execute_with(|| { - assert_ok!(XTokens::transfer( - RuntimeOrigin::signed(BOB.into()), - cfg_in_sibling, - transfer_amount, - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(parachains::polkadot::centrifuge::ID), - Junction::AccountId32 { - network: None, - id: ALICE, - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000_000.into()), - )); - - // Confirm that Bobs's balance is initial balance - amount transferred - assert_eq!( - OrmlTokens::free_balance(cfg_in_sibling, &BOB.into()), - bob_initial_balance - transfer_amount - ); - }); - - Centrifuge::execute_with(|| { - // Verify that ALICE now has initial balance + amount transferred - fee - assert_eq!( - Balances::free_balance(&ALICE.into()), - alice_initial_balance + transfer_amount - cfg_fee(), - ); - }); -} - -#[test] -fn transfer_ausd_to_centrifuge() { - TestNet::reset(); - - let alice_initial_balance = ausd(10); - let transfer_amount = ausd(7); - - Acala::execute_with(|| { - register_ausd(); - - assert_ok!(OrmlTokens::deposit( - AUSD_CURRENCY_ID, - &ALICE.into(), - alice_initial_balance - )); - - assert_eq!( - OrmlTokens::free_balance(AUSD_CURRENCY_ID, ¢rifuge_account()), - 0 - ); - }); - - Centrifuge::execute_with(|| { - register_ausd(); - - assert_eq!(OrmlTokens::free_balance(AUSD_CURRENCY_ID, &BOB.into()), 0,); - }); - - Acala::execute_with(|| { - assert_eq!( - OrmlTokens::free_balance(AUSD_CURRENCY_ID, &ALICE.into()), - ausd(10), - ); - assert_ok!(XTokens::transfer( - RuntimeOrigin::signed(ALICE.into()), - AUSD_CURRENCY_ID, - transfer_amount, - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(parachains::polkadot::centrifuge::ID), - Junction::AccountId32 { - network: None, - id: BOB, - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000_000.into()), - )); - - assert_eq!( - OrmlTokens::free_balance(AUSD_CURRENCY_ID, &ALICE.into()), - alice_initial_balance - transfer_amount - ); - - // Verify that the amount transferred is now part of the centrifuge parachain - // account here - assert_eq!( - OrmlTokens::free_balance(AUSD_CURRENCY_ID, ¢rifuge_account()), - transfer_amount - ); - }); - - Centrifuge::execute_with(|| { - // Verify that BOB now has initial balance + amount transferred - fee - assert_eq!( - OrmlTokens::free_balance(AUSD_CURRENCY_ID, &BOB.into()), - transfer_amount - ausd_fee() - ); - }); -} - -#[test] -fn transfer_dot_from_relay_chain() { - let alice_initial_dot = dot(10); - let transfer_amount: Balance = dot(3); - - Centrifuge::execute_with(|| { - register_dot(); - assert_eq!(OrmlTokens::free_balance(DOT_ASSET_ID, &ALICE.into()), 0); - }); - - PolkadotNet::execute_with(|| { - assert_eq!( - polkadot_runtime::Balances::free_balance(&ALICE.into()), - alice_initial_dot - ); - - assert_ok!(polkadot_runtime::XcmPallet::reserve_transfer_assets( - polkadot_runtime::RuntimeOrigin::signed(ALICE.into()), - Box::new(Parachain(parachains::polkadot::centrifuge::ID).into()), - Box::new( - Junction::AccountId32 { - network: None, - id: ALICE, - } - .into() - ), - Box::new((Here, transfer_amount).into()), - 0 - )); - - assert_eq!( - polkadot_runtime::Balances::free_balance(&ALICE.into()), - alice_initial_dot - transfer_amount - ); - }); - - Centrifuge::execute_with(|| { - assert_eq!( - OrmlTokens::free_balance(DOT_ASSET_ID, &ALICE.into()), - transfer_amount - dot_fee() - ); - }); -} - -#[test] -fn transfer_dot_to_relay_chain() { - transfer_dot_from_relay_chain(); - - Centrifuge::execute_with(|| { - let alice_initial_dot = OrmlTokens::free_balance(DOT_ASSET_ID, &ALICE.into()); - - assert_eq!(alice_initial_dot, dot(3) - dot_fee(),); - - assert_ok!(XTokens::transfer( - RuntimeOrigin::signed(ALICE.into()), - DOT_ASSET_ID, - dot(1), - Box::new( - MultiLocation::new( - 1, - X1(Junction::AccountId32 { - id: ALICE, - network: None, - }) - ) - .into() - ), - WeightLimit::Unlimited, - )); - - assert_eq!( - OrmlTokens::free_balance(DOT_ASSET_ID, &ALICE.into()), - alice_initial_dot - dot(1), - ); - }); - - PolkadotNet::execute_with(|| { - assert_eq!( - polkadot_runtime::Balances::free_balance(&ALICE.into()), - 79637471000 - ); - }); -} - -#[test] -fn transfer_foreign_sibling_to_centrifuge() { - TestNet::reset(); - - let alice_initial_balance = cfg(10); - let sibling_asset_id = CurrencyId::ForeignAsset(1); - let asset_location = - MultiLocation::new(1, X2(Parachain(PARA_ID_SIBLING), general_key(&[0, 1]))); - let meta: AssetMetadata = AssetMetadata { - decimals: 18, - name: "Sibling Native Token".into(), - symbol: "SBLNG".into(), - existential_deposit: 1_000_000_000_000, - location: Some(VersionedMultiLocation::V3(asset_location)), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(XcmMetadata { - // We specify a custom fee_per_second and verify below that this value is - // used when XCM transfer fees are charged for this token. - fee_per_second: Some(8420000000000000000), - }), - ..CustomMetadata::default() - }, - }; - let transfer_amount = foreign(1, meta.decimals); - - Sibling::execute_with(|| { - assert_eq!(OrmlTokens::free_balance(sibling_asset_id, &BOB.into()), 0); - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta.clone(), - Some(CurrencyId::Native), - )); - }); - - Centrifuge::execute_with(|| { - // First, register the asset in centrifuge - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta.clone(), - Some(sibling_asset_id) - )); - }); - - Sibling::execute_with(|| { - assert_ok!(XTokens::transfer( - RuntimeOrigin::signed(ALICE.into()), - CurrencyId::Native, - transfer_amount, - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(parachains::polkadot::centrifuge::ID), - Junction::AccountId32 { - network: None, - id: BOB, - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000_000.into()), - )); - - // Confirm that Alice's balance is initial balance - amount transferred - assert_eq!( - Balances::free_balance(&ALICE.into()), - alice_initial_balance - transfer_amount - ); - }); - - Centrifuge::execute_with(|| { - let bob_balance = OrmlTokens::free_balance(sibling_asset_id, &BOB.into()); - - // Verify that BOB now has initial balance + amount transferred - fee - assert_eq!( - bob_balance, - transfer_amount - - calc_fee( - xcm_metadata(meta.additional.transferability) - .unwrap() - .fee_per_second - .unwrap() - ) - ); - // Sanity check to ensure the calculated is what is expected - assert_eq!(bob_balance, 993264000000000000); - }); -} - -#[test] -fn transfer_wormhole_usdc_acala_to_centrifuge() { - TestNet::reset(); - - let usdc_asset_id = CurrencyId::ForeignAsset(39); - let asset_location = MultiLocation::new( - 1, - X2( - Parachain(parachains::polkadot::acala::ID), - general_key("0x02f3a00dd12f644daec907013b16eb6d14bf1c4cb4".as_bytes()), - ), - ); - let meta: AssetMetadata = AssetMetadata { - decimals: 6, - name: "Wormhole USDC".into(), - symbol: "WUSDC".into(), - existential_deposit: 1, - location: Some(VersionedMultiLocation::V3(asset_location)), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; - let transfer_amount = foreign(12, meta.decimals); - let alice_initial_balance = transfer_amount * 100; - - Acala::execute_with(|| { - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta.clone(), - Some(usdc_asset_id) - )); - assert_ok!(OrmlTokens::deposit( - usdc_asset_id, - &ALICE.into(), - alice_initial_balance - )); - assert_eq!( - OrmlTokens::free_balance(usdc_asset_id, &ALICE.into()), - alice_initial_balance - ); - assert_eq!(Balances::free_balance(&ALICE.into()), cfg(10)); - }); - - Centrifuge::execute_with(|| { - assert_ok!(OrmlAssetRegistry::register_asset( - RuntimeOrigin::root(), - meta.clone(), - Some(usdc_asset_id) - )); - }); - - Acala::execute_with(|| { - assert_ok!(XTokens::transfer( - RuntimeOrigin::signed(ALICE.into()), - usdc_asset_id, - transfer_amount, - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(parachains::polkadot::centrifuge::ID), - Junction::AccountId32 { - network: None, - id: BOB, - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000.into()), - )); - // Confirm that Alice's balance is initial balance - amount transferred - assert_eq!( - OrmlTokens::free_balance(usdc_asset_id, &ALICE.into()), - alice_initial_balance - transfer_amount - ); - }); - - Centrifuge::execute_with(|| { - let bob_balance = OrmlTokens::free_balance(usdc_asset_id, &BOB.into()); - - // Sanity check to ensure the calculated is what is expected - assert_eq!(bob_balance, 11991988); - }); -} - -#[test] -fn test_total_fee() { - assert_eq!(cfg_fee(), 8012800000000000); -} - -fn cfg_fee() -> Balance { - fee(currency_decimals::NATIVE) -} - -fn ausd_fee() -> Balance { - fee(currency_decimals::AUSD) -} - -fn fee(decimals: u32) -> Balance { - calc_fee(default_per_second(decimals)) -} - -// The fee associated with transferring DOT tokens -fn dot_fee() -> Balance { - fee(10) -} - -fn calc_fee(fee_per_second: Balance) -> Balance { - // We divide the fee to align its unit and multiply by 4 as that seems to be the - // unit of time the tests take. - // NOTE: it is possible that in different machines this value may differ. We - // shall see. - fee_per_second.div_euclid(10_000) * 8 -} diff --git a/runtime/integration-tests/src/runtime_apis/mod.rs b/runtime/integration-tests/src/runtime_apis/mod.rs index b6392368d8..26e584e939 100644 --- a/runtime/integration-tests/src/runtime_apis/mod.rs +++ b/runtime/integration-tests/src/runtime_apis/mod.rs @@ -25,7 +25,7 @@ use fudge::{ }; use sc_client_api::{HeaderBackend, StorageProof}; use sc_executor::WasmExecutor; -use sc_service::TFullClient; +use sc_service::{TFullBackend, TFullClient}; use sp_api::ProvideRuntimeApi as _; use sp_consensus_slots::SlotDuration; use sp_core::{ @@ -33,13 +33,20 @@ use sp_core::{ sr25519::{Pair, Public}, Pair as TraitPair, H256, }; -use sp_inherents::CreateInherentDataProviders; -use sp_runtime::{generic::BlockId, traits::IdentifyAccount, BuildStorage, Storage}; +use sp_inherents::{CreateInherentDataProviders, InherentData}; +use sp_runtime::{ + generic::BlockId, + traits::{BlakeTwo256, IdentifyAccount}, + BuildStorage, Storage, +}; use tokio::runtime::Handle; -use crate::chain::{ - centrifuge, - centrifuge::{Runtime, PARA_ID}, +use crate::{ + chain::{ + centrifuge, + centrifuge::{Runtime, PARA_ID}, + }, + utils::accounts::Keyring, }; /// Start date used for timestamps in test-enviornments @@ -70,9 +77,13 @@ type ApiRef<'a> = sp_api::ApiRef<'a, , + genesis: Option, ) -> StandaloneBuilder { - let mut state = StateProvider::new(centrifuge::WASM_BINARY.expect("Wasm is build. Qed.")); + let mut state = + StateProvider::, centrifuge::Block>::empty_default(Some( + centrifuge::WASM_BINARY.expect("Wasm is build. Qed."), + )) + .expect("ESSENTIAL: State provider can be created."); state.insert_storage( pallet_aura::GenesisConfig:: { authorities: vec![AuraId::from(sr25519::Public([0u8; 32]))], @@ -83,15 +94,7 @@ fn create_builder( state.insert_storage( pallet_balances::GenesisConfig:: { - balances: vec![( - sp_runtime::AccountId32::from( - ::from_string("//Alice", None) - .unwrap() - .public() - .into_account(), - ), - 10000 * CFG, - )], + balances: vec![(Keyring::Alice.to_account_id(), 10000 * CFG)], } .build_storage() .expect("ESSENTIAL: GenesisBuild must not fail at this stage."), @@ -108,7 +111,8 @@ fn create_builder( let instance_id = FudgeInherentTimestamp::create_instance( std::time::Duration::from_secs(12), Some(std::time::Duration::from_millis(START_DATE)), - ); + ) + .expect("ESSENTIAL: Instance ID can be created."); let cidp = Box::new(move |_parent: H256, ()| { async move { @@ -131,29 +135,30 @@ fn create_builder( Ok((timestamp, slot, relay_para_inherent)) } }); + let dp = |clone_client: Arc< - sc_service::TFullClient, + TFullClient, >| { - Box::new(move |parent, inherents| { - let client = clone_client.clone(); + Box::new( + move |parent: sp_runtime::generic::Header, inherents| { + let client = clone_client.clone(); - async move { - let aura = FudgeAuraDigest::< - centrifuge::Block, - sc_service::TFullClient< + async move { + let aura = FudgeAuraDigest::< centrifuge::Block, - centrifuge::RuntimeApi, - TWasmExecutor, - >, - >::new(&*client); - - let digest = aura.build_digest(&parent, &inherents).await?; - Ok(digest) - } - }) + TFullClient, + >::new(&*client) + .expect("ESSENTIAL: Aura digest can be created."); + + let digest = aura.build_digest(parent, &inherents).await?; + Ok(digest) + } + }, + ) }; StandaloneBuilder::<_, _, Cidp, Dp>::new(init, |client| (cidp, dp(client))) + .expect("ESSENTIAL: Standalone builder can be created.") } pub struct ApiEnv { @@ -167,10 +172,9 @@ impl ApiEnv { } } - pub fn new_with_genesis(handle: Handle, genesis: impl BuildStorage) -> Self { - // TODO: Actually make a lot of the utils in pools not specific to pools - // testing. Like init logs, creating builder and so on. + pub fn new_with_genesis(handle: Handle, genesis: Storage) -> Self { crate::utils::logs::init_logs(); + Self { builder: create_builder(handle, Some(genesis)), } diff --git a/runtime/integration-tests/src/runtime_apis/rewards.rs b/runtime/integration-tests/src/runtime_apis/rewards.rs index 7ea9ef6024..058fa2c647 100644 --- a/runtime/integration-tests/src/runtime_apis/rewards.rs +++ b/runtime/integration-tests/src/runtime_apis/rewards.rs @@ -13,9 +13,9 @@ use cfg_primitives::{AccountId, Balance, CFG}; use cfg_traits::rewards::{AccountRewards, CurrencyGroupChange, DistributedRewards, GroupRewards}; use cfg_types::tokens::CurrencyId; -use development_runtime::{apis::RewardsApi, BlockId}; +use development_runtime::BlockId; use frame_support::assert_ok; -use runtime_common::apis::RewardDomain; +use runtime_common::apis::{RewardDomain, RewardsApi}; use sp_core::{sr25519, Pair}; use sp_runtime::traits::IdentifyAccount; use tokio::runtime::Handle; @@ -23,11 +23,11 @@ use tokio::runtime::Handle; use super::ApiEnv; use crate::utils::accounts::Keyring; -#[tokio::test] -async fn liquidity_rewards_runtime_api_works() { - rewards_runtime_api_works::(RewardDomain::Liquidity) - .await; -} +// #[tokio::test] +// async fn liquidity_rewards_runtime_api_works() { +// rewards_runtime_api_works::(RewardDomain::Liquidity) +// .await; +// } #[tokio::test] async fn block_rewards_runtime_api_works() { @@ -75,8 +75,13 @@ where /// we need another distribution to allow the participant claim /// rewards for (group_id, amount) in &rewards { - ::distribute_reward(*amount, [*group_id]) - .expect("Distributing rewards should work"); + let res = + ::distribute_reward(*amount, [*group_id]) + .expect("Distributing rewards should work"); + + res.iter().for_each(|item| { + item.expect("Rewards distribution error"); + }); } } }) diff --git a/runtime/integration-tests/src/utils/env.rs b/runtime/integration-tests/src/utils/env.rs index d36b39575a..738011bf65 100644 --- a/runtime/integration-tests/src/utils/env.rs +++ b/runtime/integration-tests/src/utils/env.rs @@ -11,10 +11,7 @@ // GNU General Public License for more details. //! Utilities to create a relay-chain-parachain setup -use std::{ - collections::HashMap, - sync::{Arc, Mutex}, -}; +use std::collections::HashMap; use cfg_primitives::{AuraId, BlockNumber, Index}; use codec::{Decode, Encode}; @@ -28,19 +25,24 @@ use fudge::{ }, primitives::{Chain, PoolState}, state::StateProvider, - ParachainBuilder, RelaychainBuilder, TWasmExecutor, + InherentBuilder, ParachainBuilder, RelaychainBuilder, TWasmExecutor, }; use lazy_static::lazy_static; //pub use macros::{assert_events, events, run}; pub use macros::*; use polkadot_core_primitives::{Block as RelayBlock, Header as RelayHeader}; use polkadot_parachain::primitives::Id as ParaId; +use polkadot_runtime_parachains::{configuration, configuration::HostConfiguration, dmp}; use sc_executor::{WasmExecutionMethod, WasmExecutor}; -use sc_service::{TFullClient, TaskManager}; +use sc_service::{TFullBackend, TFullClient, TaskManager}; use sp_consensus_babe::digests::CompatibleDigestItem; use sp_consensus_slots::SlotDuration; use sp_core::H256; -use sp_runtime::{generic::BlockId, traits::Extrinsic, DigestItem, Storage}; +use sp_runtime::{ + generic::BlockId, + traits::{BlakeTwo256, Extrinsic}, + DigestItem, Storage, +}; use tokio::runtime::Handle; use crate::{ @@ -435,6 +437,8 @@ pub enum EventRange { Latest, } +pub(crate) const PARA_ID_SIBLING: u32 = 2001; + #[fudge::companion] pub struct TestEnv { #[fudge::relaychain] @@ -442,6 +446,8 @@ pub struct TestEnv { #[fudge::parachain(PARA_ID)] pub centrifuge: ParachainBuilder, + #[fudge::parachain(PARA_ID_SIBLING)] + pub sibling: ParachainBuilder, nonce_manager: Arc>, pub events: Arc>, } @@ -450,10 +456,12 @@ pub type Header = cfg_primitives::Header; pub type Block = cfg_primitives::Block; pub type UncheckedExtrinsic = centrifuge::UncheckedExtrinsic; +type EnvError = Box; + // NOTE: Nonce management is a known issue when interacting with a chain and // wanting to submit a lot of extrinsic. This interface eases this issues. impl TestEnv { - pub fn events(&self, chain: Chain, range: EventRange) -> Result>, ()> + pub fn events(&self, chain: Chain, range: EventRange) -> Result>, EnvError> where sp_runtime::generic::Block: sp_runtime::traits::Block, { @@ -461,8 +469,7 @@ impl TestEnv { Chain::Relay => { let latest = self .centrifuge - .with_state(|| frame_system::Pallet::::block_number()) - .map_err(|_| ())?; + .with_state(|| frame_system::Pallet::::block_number())?; match range { EventRange::Latest => self.events_relay(latest), @@ -491,8 +498,7 @@ impl TestEnv { _ if id == PARA_ID => { let latest = self .centrifuge - .with_state(|| frame_system::Pallet::::block_number()) - .map_err(|_| ())?; + .with_state(|| frame_system::Pallet::::block_number())?; match range { EventRange::Latest => self.events_centrifuge(latest), @@ -517,26 +523,26 @@ impl TestEnv { EventRange::One(at) => self.events_centrifuge(at), } } - _ => Err(()), + _ => Err(EnvError::from("parachain not found")), }, } } - fn events_centrifuge(&self, at: BlockNumber) -> Result>, ()> { + fn events_centrifuge(&self, at: BlockNumber) -> Result>, EnvError> { self.centrifuge .with_state_at(BlockId::Number(at), || { frame_system::Pallet::::events() }) - .map_err(|_| ()) + .map_err(|e| e.into()) .map(|records| records.into_iter().map(|record| record.encode()).collect()) } - fn events_relay(&self, at: BlockNumber) -> Result>, ()> { + fn events_relay(&self, at: BlockNumber) -> Result>, EnvError> { self.relay .with_state_at(BlockId::Number(at), || { frame_system::Pallet::::events() }) - .map_err(|_| ()) + .map_err(|e| e.into()) .map(|records| records.into_iter().map(|record| record.encode()).collect()) } @@ -594,56 +600,48 @@ impl TestEnv { /// Signs a given call for the given chain. Should only be used if the /// extrinsic really should be submitted afterwards. /// **NOTE: This will increase the stored nonce of an account** - pub fn sign(&mut self, chain: Chain, who: Keyring, call: Vec) -> Result, ()> { + pub fn sign(&mut self, chain: Chain, who: Keyring, call: Vec) -> Result, EnvError> { let nonce = self.fetch_add_nonce(chain, who); match chain { - Chain::Relay => Ok(xt_relay( - self, - who, - nonce, - Decode::decode(&mut call.as_slice()).map_err(|_| ())?, - )? - .encode()), + Chain::Relay => { + Ok(xt_relay(self, who, nonce, Decode::decode(&mut call.as_slice())?)?.encode()) + } Chain::Para(id) => match id { - _ if id == PARA_ID => Ok(xt_centrifuge( - self, - who, - nonce, - Decode::decode(&mut call.as_slice()).map_err(|_| ())?, - )? - .encode()), - _ => Err(()), + _ if id == PARA_ID => { + Ok( + xt_centrifuge(self, who, nonce, Decode::decode(&mut call.as_slice())?)? + .encode(), + ) + } + _ => Err(EnvError::from("parachain not found")), }, } } /// Submits a previously signed extrinsics to the pool of the respective /// chain. - pub fn submit(&mut self, chain: Chain, xt: Vec) -> Result<(), ()> { - self.append_extrinsic(chain, xt) + pub fn submit(&mut self, chain: Chain, xt: Vec) -> Result<(), EnvError> { + self.append_extrinsic(chain, xt).map_err(|e| e.into()) } /// Signs and submits an extrinsic to the given chain. Will take the nonce /// for the account from the `NonceManager`. - pub fn sign_and_submit(&mut self, chain: Chain, who: Keyring, call: Vec) -> Result<(), ()> { + pub fn sign_and_submit( + &mut self, + chain: Chain, + who: Keyring, + call: Vec, + ) -> Result<(), EnvError> { let nonce = self.nonce(chain, who); let xt = match chain { - Chain::Relay => xt_relay( - self, - who, - nonce, - Decode::decode(&mut call.as_slice()).map_err(|_| ())?, - )? - .encode(), + Chain::Relay => { + xt_relay(self, who, nonce, Decode::decode(&mut call.as_slice())?)?.encode() + } Chain::Para(id) => match id { - _ if id == PARA_ID => xt_centrifuge( - self, - who, - nonce, - Decode::decode(&mut call.as_slice()).map_err(|_| ())?, - )? - .encode(), - _ => return Err(()), + _ if id == PARA_ID => { + xt_centrifuge(self, who, nonce, Decode::decode(&mut call.as_slice())?)?.encode() + } + _ => return Err(EnvError::from("parachain not found")), }, }; @@ -661,7 +659,7 @@ impl TestEnv { chain: Chain, who: Keyring, calls: Vec>, - ) -> Result<(), ()> { + ) -> Result<(), EnvError> { for call in calls { self.sign_and_submit(chain, who, call)?; } @@ -669,7 +667,7 @@ impl TestEnv { Ok(()) } - pub fn evolve_till(&mut self, chain: Chain, till_state: ChainState) -> Result<(), ()> { + pub fn evolve_till(&mut self, chain: Chain, till_state: ChainState) -> Result<(), EnvError> { match chain { Chain::Relay => match till_state { ChainState::EvolvedBy(blocks) => pass_n(self, blocks / 2), @@ -687,7 +685,7 @@ impl TestEnv { } } - fn evolve_till_pool_xts_centrifuge(&mut self, xts: usize) -> Result<(), ()> { + fn evolve_till_pool_xts_centrifuge(&mut self, xts: usize) -> Result<(), EnvError> { let state = self.centrifuge.pool_state(); let mut curr_xts = match state { PoolState::Empty => return Ok(()), @@ -720,7 +718,7 @@ impl TestEnv { Ok(()) } - fn evolve_till_pool_xts_relay(&mut self, xts: usize) -> Result<(), ()> { + fn evolve_till_pool_xts_relay(&mut self, xts: usize) -> Result<(), EnvError> { let state = self.relay.pool_state(); let mut curr_xts = match state { PoolState::Empty => return Ok(()), @@ -777,13 +775,31 @@ fn test_env( // Build relay-chain builder let relay = { sp_tracing::enter_span!(sp_tracing::Level::INFO, "Relay - StartUp"); - let mut state = StateProvider::new(RelayCode.expect("Wasm is build. Qed.")); - // We need to HostConfiguration and use the default here. + //TODO(cdamian): Use RelayBlock + let mut state = + StateProvider::, centrifuge::Block>::empty_default( + Some(RelayCode.expect("Wasm is build. Qed.")), + ) + .expect("ESSENTIAL: State provider can be created"); + + let mut configuration = configuration::GenesisConfig::::default(); + + let mut host_config = HostConfiguration::::default(); + host_config.max_downward_message_size = 1024; + host_config.hrmp_channel_max_capacity = 100; + host_config.hrmp_channel_max_message_size = 1024; + host_config.hrmp_channel_max_total_size = 1024; + host_config.hrmp_max_parachain_outbound_channels = 10; + host_config.hrmp_max_parachain_inbound_channels = 10; + host_config.hrmp_max_message_num_per_candidate = 100; + + configuration.config = host_config; + state.insert_storage( - polkadot_runtime_parachains::configuration::GenesisConfig::::default() + configuration .build_storage() - .expect("ESSENTIAL: GenesisBuild must not fail at this stage."), + .expect("ESSENTIAL: Host Configuration GenesisBuild must not fail at this stage."), ); state.insert_storage( @@ -791,7 +807,7 @@ fn test_env( code: RelayCode.expect("ESSENTIAL: Relay WASM is some.").to_vec(), } .build_storage::() - .expect("ESSENTIAL: GenesisBuild must not fail at this stage."), + .expect("ESSENTIAL: Frame System GenesisBuild must not fail at this stage."), ); if let Some(storage) = relay_storage { @@ -807,7 +823,8 @@ fn test_env( let instance_id = FudgeInherentTimestamp::create_instance( std::time::Duration::from_secs(6), Some(std::time::Duration::from_millis(START_DATE)), - ); + ) + .expect("ESSENTIAL: Instance ID can be created."); Box::new(move |parent: H256, ()| { let client = clone_client.clone(); @@ -832,103 +849,146 @@ fn test_env( }) }; - let dp: RelayDp = Box::new(move |parent, inherents| async move { - let babe = FudgeBabeDigest::::new(); - let digest = babe.build_digest(&parent, &inherents).await?; - Ok(digest) - }); + let dp: RelayDp = Box::new( + move |parent: sp_runtime::generic::Header, inherents| async move { + let babe = FudgeBabeDigest::::new(); + let digest = babe.build_digest(parent, &inherents).await?; + Ok(digest) + }, + ); RelaychainBuilder::<_, _, RelayRt, RelayCidp, RelayDp>::new(init, |client| { (cidp(client), dp) }) + .expect("ESSENTIAL: Relay chain builder can be created.") }; - // Build parachain-builder - let centrifuge = { - sp_tracing::enter_span!(sp_tracing::Level::INFO, "Centrifuge - StartUp"); - let mut state = StateProvider::new(CentrifugeCode.expect("Wasm is build. Qed.")); + let para_inherent_builder = relay.inherent_builder(ParaId::from(PARA_ID)); + let sibling_inherent_builder = relay.inherent_builder(ParaId::from(PARA_ID_SIBLING)); - state.insert_storage( - frame_system::GenesisConfig { - code: CentrifugeCode - .expect("ESSENTIAL: Centrifuge WASM is some.") - .to_vec(), - } - .build_storage::() - .expect("ESSENTIAL: GenesisBuild must not fail at this stage."), - ); - state.insert_storage( - pallet_aura::GenesisConfig:: { - authorities: vec![AuraId::from(sp_core::sr25519::Public([0u8; 32]))], - } - .build_storage() - .expect("ESSENTIAL: GenesisBuild must not fail at this stage."), - ); + let para_builder = get_parachain_builder( + handle.clone(), + para_inherent_builder, + PARA_ID, + centrifuge_storage.clone(), + ); - if let Some(storage) = centrifuge_storage { - state.insert_storage(storage); - } + let sibling_builder = get_parachain_builder( + handle, + sibling_inherent_builder, + PARA_ID_SIBLING, + centrifuge_storage, + ); - let mut init = fudge::initiator::default(handle); - init.with_genesis(Box::new(state)); + TestEnv::new( + relay, + para_builder, + sibling_builder, + Arc::new(Mutex::new(NonceManager::new())), + Arc::new(Mutex::new(EventsStorage::new())), + ) + .expect("ESSENTIAL: Creating new TestEnv instance must not fail.") +} - let para_id = ParaId::from(PARA_ID); - let inherent_builder = relay.inherent_builder(para_id.clone()); - let instance_id = FudgeInherentTimestamp::create_instance( - std::time::Duration::from_secs(12), - Some(std::time::Duration::from_millis(START_DATE)), - ); +fn get_parachain_builder( + handle: Handle, + inherent_builder: InherentBuilder< + TFullClient, + TFullBackend, + >, + para_id: u32, + centrifuge_storage: Option, +) -> ParachainBuilder { + sp_tracing::enter_span!(sp_tracing::Level::INFO, "Parachain - StartUp"); + + let mut state = + StateProvider::, centrifuge::Block>::empty_default(Some( + CentrifugeCode.expect("Wasm is build. Qed."), + )) + .expect("ESSENTIAL: State provider can be created."); + + state.insert_storage( + frame_system::GenesisConfig { + code: CentrifugeCode + .expect("ESSENTIAL: Centrifuge WASM is some.") + .to_vec(), + } + .build_storage::() + .expect("ESSENTIAL: Frame System GenesisBuild must not fail at this stage."), + ); + state.insert_storage( + pallet_aura::GenesisConfig:: { + authorities: vec![AuraId::from(sp_core::sr25519::Public([0u8; 32]))], + } + .build_storage() + .expect("ESSENTIAL: Pallet Aura GenesisBuild must not fail at this stage."), + ); + state.insert_storage( + >::build_storage( + ¶chain_info::GenesisConfig { + parachain_id: ParaId::from(para_id), + }, + ) + .expect("ESSENTIAL: Parachain Info GenesisBuild must not fail at this stage."), + ); - let cidp = Box::new(move |_parent: H256, ()| { - let inherent_builder_clone = inherent_builder.clone(); - async move { - let timestamp = FudgeInherentTimestamp::get_instance(instance_id) - .expect("Instances is initialized"); - - let slot = - sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( - timestamp.current_time(), - SlotDuration::from_millis(std::time::Duration::from_secs(12).as_millis() as u64), - ); - let inherent = inherent_builder_clone.parachain_inherent().await.unwrap(); - let relay_para_inherent = FudgeInherentParaParachain::new(inherent); - Ok((timestamp, slot, relay_para_inherent)) - } - }); - let dp = |clone_client: Arc< - sc_service::TFullClient, - >| { - Box::new(move |parent, inherents| { - let client = clone_client.clone(); + if let Some(storage) = centrifuge_storage { + state.insert_storage(storage); + } - async move { - let aura = FudgeAuraDigest::< - CentrifugeBlock, - sc_service::TFullClient, - >::new(&*client); + let mut init = fudge::initiator::default(handle); + init.with_genesis(Box::new(state)); - let digest = aura.build_digest(&parent, &inherents).await?; - Ok(digest) - } - }) - }; + let para_id = ParaId::from(para_id); + let instance_id = FudgeInherentTimestamp::create_instance( + std::time::Duration::from_secs(12), + Some(std::time::Duration::from_millis(START_DATE)), + ) + .expect("ESSENTIAL: Instance ID can be created."); + + let cidp = Box::new(move |_parent: H256, ()| { + let inherent_builder_clone = inherent_builder.clone(); + async move { + let timestamp = FudgeInherentTimestamp::get_instance(instance_id) + .expect("Instances is initialized"); + + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + timestamp.current_time(), + SlotDuration::from_millis(std::time::Duration::from_secs(12).as_millis() as u64), + ); + let inherent = inherent_builder_clone.parachain_inherent().await.unwrap(); + let relay_para_inherent = FudgeInherentParaParachain::new(inherent); + Ok((timestamp, slot, relay_para_inherent)) + } + }); + let dp = |clone_client: Arc< + sc_service::TFullClient, + >| { + Box::new(move |parent, inherents| { + let client = clone_client.clone(); - ParachainBuilder::<_, _, CentrifugeCidp, CentrifugeDp>::new(init, |client| { - (cidp, dp(client)) + async move { + let aura = FudgeAuraDigest::< + CentrifugeBlock, + sc_service::TFullClient, + >::new(&*client) + .expect("ESSENTIAL: Aura digest can be created."); + + let digest = aura.build_digest(parent, &inherents).await?; + Ok(digest) + } }) }; - TestEnv::new( - relay, - centrifuge, - Arc::new(Mutex::new(NonceManager::new())), - Arc::new(Mutex::new(EventsStorage::new())), - ) - .expect("ESSENTIAL: Creating new TestEnv instance must not fail.") + ParachainBuilder::<_, _, CentrifugeCidp, CentrifugeDp>::new(para_id, init, |client| { + (cidp, dp(client)) + }) + .expect("ESSENTIAL: Parachain builder can be created.") } /// Pass n_blocks on the parachain-side! -pub fn pass_n(env: &mut TestEnv, n: u64) -> Result<(), ()> { +pub fn pass_n(env: &mut TestEnv, n: u64) -> Result<(), EnvError> { for _ in 0..n { env.evolve()?; } diff --git a/runtime/integration-tests/src/utils/extrinsics.rs b/runtime/integration-tests/src/utils/extrinsics.rs index 034a033b70..84ec5819c8 100644 --- a/runtime/integration-tests/src/utils/extrinsics.rs +++ b/runtime/integration-tests/src/utils/extrinsics.rs @@ -51,7 +51,7 @@ pub fn xt_centrifuge( who: Keyring, nonce: cfg_primitives::Index, call: centrifuge::RuntimeCall, -) -> Result { +) -> Result> { let client = env.centrifuge.client(); let genesis_hash = client @@ -67,7 +67,7 @@ pub fn xt_centrifuge( env.centrifuge .with_state(|| sign_centrifuge(who, nonce, call, spec_version, tx_version, genesis_hash)) - .map_err(|_| ()) + .map_err(|e| e.into()) } /// Generates an signed-extrinisc for relay-chain. @@ -79,7 +79,7 @@ pub fn xt_relay( who: Keyring, nonce: RelayIndex, call: relay::RuntimeCall, -) -> Result { +) -> Result> { let client = env.relay.client(); let genesis_hash = client @@ -95,7 +95,7 @@ pub fn xt_relay( env.relay .with_state(|| sign_relay(who, nonce, call, spec_version, tx_version, genesis_hash)) - .map_err(|_| ()) + .map_err(|e| e.into()) } fn signed_extra_centrifuge(nonce: cfg_primitives::Index) -> CentrifugeSignedExtra { diff --git a/runtime/integration-tests/src/utils/genesis.rs b/runtime/integration-tests/src/utils/genesis.rs index 529d998ad0..0462b69930 100644 --- a/runtime/integration-tests/src/utils/genesis.rs +++ b/runtime/integration-tests/src/utils/genesis.rs @@ -19,7 +19,8 @@ use sp_runtime::{AccountId32, Storage}; use crate::utils::{ accounts::{default_accounts, Keyring}, tokens::{DECIMAL_BASE_12, DECIMAL_BASE_18}, - AUSD_CURRENCY_ID, RELAY_ASSET_ID, + AUSD_CURRENCY_ID, AUSD_ED, GLMR_CURRENCY_ID, GLMR_ED, RELAY_ASSET_ID, USDT_CURRENCY_ID, + USDT_ED, }; /// Provides 100_000 * DECIMAL_BASE_18 native tokens to the @@ -42,12 +43,12 @@ where .collect(), } .assimilate_storage(storage) - .expect("ESSENTIAL: Genesisbuild is not allowed to fail."); + .expect("ESSENTIAL: Balances Genesisbuild is not allowed to fail."); } -/// Provides 100_000 * DECIMAL_BASE_12 AUSD tokens to the -/// `accounts::default_accounts()` -pub fn default_ausd_balances(storage: &mut Storage) +/// Sets the balances for default accounts in the Orml Tokens genesis config +/// with the provided amount for a specific currency. +pub fn default_orml_balances(storage: &mut Storage, currency_id: CurrencyId, amount: u128) where Runtime: orml_tokens::Config, Runtime::Balance: From, @@ -60,14 +61,14 @@ where .map(|acc| { ( AccountId32::from(acc).into(), - AUSD_CURRENCY_ID.into(), - (100_000 * DECIMAL_BASE_12).into(), + currency_id.into(), + amount.into(), ) }) .collect(), } .assimilate_storage(storage) - .expect("ESSENTIAL: Genesisbuild is not allowed to fail."); + .expect("ESSENTIAL: Orml Tokens Genesisbuild is not allowed to fail."); } /// Provides 100_000 * DECIMAL_BASE_18 and Provides 100_000 * DECIMAL_BASE_12 @@ -81,7 +82,9 @@ where Runtime::CurrencyId: From, { default_native_balances::(storage); - default_ausd_balances::(storage); + default_orml_balances::(storage, AUSD_CURRENCY_ID, AUSD_ED); + default_orml_balances::(storage, USDT_CURRENCY_ID, USDT_ED); + default_orml_balances::(storage, GLMR_CURRENCY_ID, GLMR_ED); } /// Register the Relay chain token and AUSD_CURRENCY_ID in the asset registry diff --git a/runtime/integration-tests/src/utils/logs.rs b/runtime/integration-tests/src/utils/logs.rs index 16a6c2ffdc..fec9837e5a 100644 --- a/runtime/integration-tests/src/utils/logs.rs +++ b/runtime/integration-tests/src/utils/logs.rs @@ -12,12 +12,17 @@ //! Utilities to initialize logging subscriber use std::sync::atomic::{AtomicUsize, Ordering}; + +use tracing_subscriber::filter::LevelFilter; + static GLOBAL_INIT: AtomicUsize = AtomicUsize::new(UNINITIALIZED); const UNINITIALIZED: usize = 0; const INITIALIZING: usize = 1; const INITIALIZED: usize = 2; +const LOG_LEVEL: LevelFilter = LevelFilter::INFO; + pub fn init_logs() { if GLOBAL_INIT .compare_exchange( @@ -29,6 +34,8 @@ pub fn init_logs() { .is_ok() { GLOBAL_INIT.store(INITIALIZED, Ordering::SeqCst); - tracing_subscriber::fmt::init(); + tracing_subscriber::fmt::fmt() + .with_max_level(LOG_LEVEL) + .init(); } }