Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added an integration test for the full mint to redeem process #1689

Merged
merged 2 commits into from
Dec 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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