Skip to content

Commit

Permalink
Added an integration test for the full mint to redeem process (#1689)
Browse files Browse the repository at this point in the history
* Added an integration test for the full mint to redeem process

* Corrected some typos

Co-authored-by: Roy Yang <[email protected]>
  • Loading branch information
syan095 and Roy Yang authored Dec 10, 2021
1 parent 096902a commit 38f8927
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 25 deletions.
38 changes: 19 additions & 19 deletions modules/homa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ pub mod module {
/// Redeem request is redeemed by unbond on relaychain. \[redeemer,
/// era_index_when_unbond, liquid_amount, unbonding_staking_amount\]
RedeemedByUnbond(T::AccountId, EraIndex, Balance, Balance),
/// The redeemer withdraw expired redemption. \[redeemer, redeption_amount\]
/// The redeemer withdraw expired redemption. \[redeemer, redemption_amount\]
WithdrawRedemption(T::AccountId, Balance),
/// The current era has been bumped. \[new_era_index\]
CurrentEraBumped(EraIndex),
Expand Down Expand Up @@ -234,8 +234,8 @@ pub mod module {
/// The total amount of void liquid currency. It's will not be issued,
/// used to avoid newly issued LDOT to obtain the incoming staking income from relaychain.
/// And it is guaranteed that the current exchange rate between liquid currency and staking
/// currency will not change. It will be reset to 0 at the beginning of the rebalance when new
/// era.
/// currency will not change. It will be reset to 0 at the beginning of the `rebalance` when new
/// era starts.
///
/// TotalVoidLiquid value: LiquidCurrencyAmount
#[pallet::storage]
Expand All @@ -258,7 +258,7 @@ pub mod module {

/// The records of unbonding by AccountId.
///
/// Unbondings: double_map AccountId, ExpireEraIndex => UnboundingStakingCurrencyAmount
/// Unbondings: double_map AccountId, ExpireEraIndex => UnbondingStakingCurrencyAmount
#[pallet::storage]
#[pallet::getter(fn unbondings)]
pub type Unbondings<T: Config> =
Expand Down Expand Up @@ -293,7 +293,7 @@ pub mod module {
#[pallet::getter(fn redeem_threshold)]
pub type RedeemThreshold<T: Config> = StorageValue<_, Balance, ValueQuery>;

/// The rate of Homa drawn from the staking reward as commision.
/// The rate of Homa drawn from the staking reward as commission.
/// The draw will be transfer to TreasuryAccount of Homa in liquid currency.
///
/// CommissionRate: value: Rate
Expand Down Expand Up @@ -506,7 +506,7 @@ pub mod module {
/// Parameters:
/// - `soft_bonded_cap_per_sub_account`: soft cap of staking amount for a single nominator
/// on relaychain to obtain the best staking rewards.
/// - `estimated_reward_rate_per_era`: the esstaking yield of each era on the current relay
/// - `estimated_reward_rate_per_era`: the estimated staking yield of each era on the current relay
/// chain.
/// - `mint_threshold`: the staking currency amount of threshold when mint.
/// - `redeem_threshold`: the liquid currency amount of threshold when request redeem.
Expand Down Expand Up @@ -732,11 +732,11 @@ pub mod module {
.saturating_mul_int(liquid_currency_limit);
let module_account = Self::account_id();

// calculate the acutal liquid currency to be used to redeem
// calculate the actual liquid currency to be used to redeem
let actual_liquid_to_redeem = if liquid_limit_at_fee_rate >= request_amount {
request_amount
} else {
// if cannot fast match the request amount fully, at least keep RedeemThreshold as remainer.
// if cannot fast match the request amount fully, at least keep RedeemThreshold as remainder.
liquid_limit_at_fee_rate.min(request_amount.saturating_sub(Self::redeem_threshold()))
};

Expand Down Expand Up @@ -769,17 +769,17 @@ pub mod module {
}

// update request amount
let remainer_request_amount = request_amount.saturating_sub(actual_liquid_to_redeem);
if !remainer_request_amount.is_zero() {
*maybe_request = Some((remainer_request_amount, allow_fast_match));
let remainder_request_amount = request_amount.saturating_sub(actual_liquid_to_redeem);
if !remainder_request_amount.is_zero() {
*maybe_request = Some((remainder_request_amount, allow_fast_match));
}
}

Ok(())
})
}

/// Accumulate staking rewards according to EstimatedRewardRatePerEra and era interal.
/// Accumulate staking rewards according to EstimatedRewardRatePerEra and era internally.
/// And draw commission from estimated staking rewards by issuing liquid currency to
/// TreasuryAccount. Note: This will cause some losses to the minters in previous_era,
/// because they have been already deducted some liquid currency amount when mint in
Expand Down Expand Up @@ -843,7 +843,7 @@ pub mod module {
if !expired_unlocking.is_zero() {
T::HomaXcm::withdraw_unbonded_from_sub_account(sub_account_index, expired_unlocking)?;

// udpate ledger
// update ledger
Self::do_update_ledger(sub_account_index, |before| -> DispatchResult {
*before = new_ledger;
Ok(())
Expand Down Expand Up @@ -876,7 +876,7 @@ pub mod module {
.iter()
.map(|index| (*index, Self::staking_ledgers(index).unwrap_or_default().bonded))
.collect();
let (distribution, remainer) = distribute_increment::<u16>(
let (distribution, remainder) = distribute_increment::<u16>(
bonded_list,
to_bond_pool,
Some(Self::soft_bonded_cap_per_sub_account().saturating_add(xcm_transfer_fee)),
Expand All @@ -891,7 +891,7 @@ pub mod module {
let bond_amount = amount.saturating_sub(xcm_transfer_fee);
T::HomaXcm::bond_extra_on_sub_account(sub_account_index, bond_amount)?;

// udpate ledger
// update ledger
Self::do_update_ledger(sub_account_index, |ledger| -> DispatchResult {
ledger.bonded = ledger.bonded.saturating_add(bond_amount);
Ok(())
Expand All @@ -900,7 +900,7 @@ pub mod module {
}

// update pool
ToBondPool::<T>::mutate(|pool| *pool = remainer);
ToBondPool::<T>::mutate(|pool| *pool = remainder);
}

Ok(())
Expand Down Expand Up @@ -949,7 +949,7 @@ pub mod module {
if !unbond_amount.is_zero() {
T::HomaXcm::unbond_on_sub_account(sub_account_index, unbond_amount)?;

// udpate ledger
// update ledger
Self::do_update_ledger(sub_account_index, |ledger| -> DispatchResult {
ledger.bonded = ledger.bonded.saturating_sub(unbond_amount);
ledger.unlocking.push(UnlockChunk {
Expand Down Expand Up @@ -1047,7 +1047,7 @@ pub fn distribute_increment<Index>(
pub fn distribute_decrement<Index>(
mut amount_list: Vec<(Index, Balance)>,
total_decrement: Balance,
amount_remainer: Option<Balance>,
amount_remainder: Option<Balance>,
minimum_decrement: Option<Balance>,
) -> (Vec<(Index, Balance)>, Balance) {
let mut remain_decrement = total_decrement;
Expand All @@ -1062,7 +1062,7 @@ pub fn distribute_decrement<Index>(
}

let decrement_distribution = amount
.saturating_sub(amount_remainer.unwrap_or_else(Bounded::min_value))
.saturating_sub(amount_remainder.unwrap_or_else(Bounded::min_value))
.min(remain_decrement);
if decrement_distribution.is_zero()
|| decrement_distribution < minimum_decrement.unwrap_or_else(Bounded::min_value)
Expand Down
10 changes: 5 additions & 5 deletions modules/homa/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ fn claim_redemption_works() {
assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 0);
assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0);

// no available expired redemption, nothing happend.
// no available expired redemption, nothing happened.
assert_ok!(Homa::claim_redemption(Origin::signed(BOB), ALICE));
assert_eq!(Homa::unbondings(&ALICE, 1), 1_000_000);
assert_eq!(Homa::unbondings(&ALICE, 2), 2_000_000);
Expand All @@ -209,7 +209,7 @@ fn claim_redemption_works() {
assert_eq!(Homa::unclaimed_redemption(), 0);
assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0);

// there is available expired redemption, but UnclaimedRedemption is not enought.
// there is available expired redemption, but UnclaimedRedemption is not enough.
RelayChainCurrentEra::<Runtime>::put(2);
assert_noop!(
Homa::claim_redemption(Origin::signed(BOB), ALICE),
Expand Down Expand Up @@ -630,7 +630,7 @@ fn do_fast_match_redeem_works() {
);

// Bob's redeem request is able to be fast matched partially,
// because must remain `RedeemThreshold` even if `ToBondPool` is enought.
// because must remain `RedeemThreshold` even if `ToBondPool` is enough.
assert_ok!(Homa::do_fast_match_redeem(&BOB));
System::assert_last_event(Event::Homa(crate::Event::RedeemedByFastMatch(
BOB, 5_500_000, 550_000, 500_499,
Expand Down Expand Up @@ -1026,7 +1026,7 @@ fn process_redeem_requests_works() {
})
);

// total_bonded is enought to process all redeem requests
// total_bonded is enough to process all redeem requests
assert_ok!(Homa::process_redeem_requests(1));
System::assert_has_event(Event::Homa(crate::Event::RedeemedByUnbond(
ALICE, 1, 20_000_000, 2_000_000,
Expand Down Expand Up @@ -1068,7 +1068,7 @@ fn process_redeem_requests_works() {
40_000_000
);

// total_bonded is not enought to process all redeem requests
// total_bonded is not enough to process all redeem requests
assert_ok!(Homa::process_redeem_requests(2));
System::assert_has_event(Event::Homa(crate::Event::RedeemedByUnbond(
BOB, 2, 20_000_000, 2_000_000,
Expand Down
203 changes: 203 additions & 0 deletions runtime/integration-tests/src/homa_xcm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,3 +414,206 @@ fn homa_xcm_unbond_on_sub_account_works() {
);
});
}

// Test the entire process from Mint to Redeem.
#[test]
fn homa_mint_and_redeem_works() {
let homa_lite_sub_account: AccountId =
hex_literal::hex!["d7b8926b326dd349355a9a7cca6606c1e0eb6fd2b506066b518c7155ff0d8297"].into();
let mut parachain_account: AccountId = AccountId::default();
Karura::execute_with(|| {
parachain_account = ParachainAccount::get();
});
KusamaNet::execute_with(|| {
// Transfer some KSM into the parachain.
assert_ok!(kusama_runtime::XcmPallet::reserve_transfer_assets(
kusama_runtime::Origin::signed(ALICE.into()),
Box::new(Parachain(2000).into().into()),
Box::new(
Junction::AccountId32 {
id: alice().into(),
network: NetworkId::Any
}
.into()
.into()
),
Box::new((Here, 2001 * dollar(RELAY_CHAIN_CURRENCY)).into()),
0
));

// Transfer some KSM into the parachain.
assert_ok!(kusama_runtime::Balances::transfer(
kusama_runtime::Origin::signed(ALICE.into()),
MultiAddress::Id(homa_lite_sub_account.clone()),
dollar(RELAY_CHAIN_CURRENCY)
));

assert_ok!(kusama_runtime::Staking::bond(
kusama_runtime::Origin::signed(homa_lite_sub_account.clone()),
MultiAddress::Id(homa_lite_sub_account.clone()),
dollar(RELAY_CHAIN_CURRENCY),
pallet_staking::RewardDestination::<AccountId>::Staked,
));
assert_eq!(
kusama_runtime::Balances::free_balance(&parachain_account),
2003 * dollar(RELAY_CHAIN_CURRENCY)
);
assert_eq!(
kusama_runtime::Balances::free_balance(&homa_lite_sub_account),
dollar(RELAY_CHAIN_CURRENCY),
);
});

Karura::execute_with(|| {
assert_ok!(Tokens::set_balance(
Origin::root(),
MultiAddress::Id(AccountId::from(alice())),
RELAY_CHAIN_CURRENCY,
1_000 * dollar(RELAY_CHAIN_CURRENCY),
0
));
assert_ok!(Tokens::set_balance(
Origin::root(),
MultiAddress::Id(AccountId::from(bob())),
RELAY_CHAIN_CURRENCY,
1_000 * dollar(RELAY_CHAIN_CURRENCY),
0
));

configure_homa_and_homa_xcm();

// Test mint works
// Amount bonded = $1000 - XCM_FEE = 999_990_000_000_000
assert_ok!(Homa::mint(Origin::signed(alice()), 1_000 * dollar(RELAY_CHAIN_CURRENCY)));
assert_ok!(Homa::mint(Origin::signed(bob()), 1_000 * dollar(RELAY_CHAIN_CURRENCY)));

assert_eq!(Homa::get_total_bonded(), 0);
assert_eq!(Homa::get_total_staking_currency(), 2_000 * dollar(RELAY_CHAIN_CURRENCY));

// Synchronize with Relay chain via Xcm messages. Also update internal storage.
assert_ok!(Homa::bump_current_era());

assert_eq!(Tokens::free_balance(LIQUID_CURRENCY, &AccountId::from(alice())), 10_000 * dollar(LIQUID_CURRENCY));
assert_eq!(Tokens::free_balance(LIQUID_CURRENCY, &AccountId::from(bob())), 10_000 * dollar(LIQUID_CURRENCY));
assert_eq!(Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(alice())), 0);
assert_eq!(Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(bob())), 0);

assert_eq!(Homa::get_total_bonded(), 2_000 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE);
assert_eq!(Homa::get_total_staking_currency(), 2_000 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE);
});

KusamaNet::execute_with(|| {
// Ensure the correct amount is bonded.
let ledger = kusama_runtime::Staking::ledger(&homa_lite_sub_account).expect("record should exist");
assert_eq!(ledger.total, 2001 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE);
assert_eq!(ledger.active, 2001 * dollar(RELAY_CHAIN_CURRENCY)- XCM_FEE);

// 2 x XCM fee is paid: for Mint and Redeem
assert_eq!(
kusama_runtime::Balances::free_balance(&parachain_account),
3 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310
);
});

Karura::execute_with(|| {
assert_ok!(Tokens::set_balance(
Origin::root(),
MultiAddress::Id(AccountId::from(alice())),
RELAY_CHAIN_CURRENCY,
0,
0
));
assert_ok!(Tokens::set_balance(
Origin::root(),
MultiAddress::Id(AccountId::from(bob())),
RELAY_CHAIN_CURRENCY,
0,
0
));

// Redeem the liquid currency.
assert_ok!(Homa::request_redeem(
Origin::signed(alice()),
10_000 * dollar(LIQUID_CURRENCY),
false,
));
assert_ok!(Homa::request_redeem(
Origin::signed(bob()),
10_000 * dollar(LIQUID_CURRENCY),
false,
));

// Unbonds the tokens on the Relay chain.
assert_ok!(Homa::bump_current_era());
let unbonding_era = Homa::relay_chain_current_era() + KusamaBondingDuration::get();
assert_eq!(unbonding_era, 9);

assert_eq!(Homa::unbondings(&alice(), unbonding_era), 999_995_000_000_000);
assert_eq!(Homa::unbondings(&bob(), unbonding_era), 999_995_000_000_000);

assert_eq!(Homa::get_total_bonded(), 0);
assert_eq!(Homa::get_total_staking_currency(), 0);
assert_eq!(Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(alice())), 0);
assert_eq!(Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(bob())), 0);
});

KusamaNet::execute_with(|| {
// Some bonds are being unlocked via Xcm from the parachain.
let ledger = kusama_runtime::Staking::ledger(&homa_lite_sub_account).expect("record should exist");
assert_eq!(ledger.total, 2001 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE);
assert_eq!(ledger.active, dollar(RELAY_CHAIN_CURRENCY));

// Fast forward the era until unlocking period ends.
kusama_runtime::System::set_block_number(101_000);
for _i in 0..29 {
kusama_runtime::Staking::trigger_new_era(0, vec![]);
}
});

Karura::execute_with(|| {
assert_ok!(Tokens::set_balance(
Origin::root(),
MultiAddress::Id(AccountId::from(alice())),
RELAY_CHAIN_CURRENCY,
0,
0
));
assert_ok!(Tokens::set_balance(
Origin::root(),
MultiAddress::Id(AccountId::from(bob())),
RELAY_CHAIN_CURRENCY,
0,
0
));

// Wait for the chunk to unlock
for _ in 0..KusamaBondingDuration::get() + 1 {
assert_ok!(Homa::bump_current_era());
}

// Claim the unlocked chunk
assert_ok!(Homa::claim_redemption(
Origin::signed(alice()),
alice(),
));
assert_ok!(Homa::claim_redemption(
Origin::signed(alice()),
bob(),
));

// Redeem process is completed.
assert_eq!(Homa::get_total_bonded(), 0);
assert_eq!(Homa::get_total_staking_currency(), 0);
assert_eq!(Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(alice())), 999_995_000_000_000);
assert_eq!(Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(bob())), 999_995_000_000_000);
assert_eq!(Tokens::free_balance(LIQUID_CURRENCY, &AccountId::from(alice())), 0);
assert_eq!(Tokens::free_balance(LIQUID_CURRENCY, &AccountId::from(bob())), 0);
});

KusamaNet::execute_with(|| {
// Unbonded chunks are withdrew.
let ledger = kusama_runtime::Staking::ledger(&homa_lite_sub_account).expect("record should exist");
assert_eq!(ledger.total, dollar(RELAY_CHAIN_CURRENCY));
assert_eq!(ledger.active, dollar(RELAY_CHAIN_CURRENCY));
});
}
Loading

0 comments on commit 38f8927

Please sign in to comment.