Skip to content

Commit dac901d

Browse files
addtional tests
1 parent 9c84cb5 commit dac901d

1 file changed

Lines changed: 113 additions & 0 deletions

File tree

integration-tests/src/gigahdx.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,119 @@ fn realize_yield_should_fold_accrued_into_principal_when_rate_increased() {
348348
});
349349
}
350350

351+
/// Setup for the realize-yield equivalence test with a deliberately
352+
/// non-terminating rate. Alice stakes 100 HDX and Bob 70 HDX (so Alice's
353+
/// GIGAHDX is *not* the whole supply and her per-account conversion actually
354+
/// floor-rounds), then 100 HDX of yield is injected into the gigapot. The
355+
/// resulting rate is 270/170 = 27/17 — `Ratio::new` does not reduce, and 17
356+
/// divides neither 100·UNITS nor the pot evenly. Returns Alice and her GIGAHDX.
357+
fn stake_two_then_set_rounding_rate() -> (AccountId, Balance) {
358+
init_gigahdx();
359+
reset_giga_state_for_fixture();
360+
361+
let alice: AccountId = ALICE.into();
362+
let bob: AccountId = BOB.into();
363+
fund(&bob, 1_000 * UNITS);
364+
365+
assert_ok!(GigaHdx::giga_stake(RuntimeOrigin::signed(alice.clone()), 100 * UNITS));
366+
assert_ok!(GigaHdx::giga_stake(RuntimeOrigin::signed(bob.clone()), 70 * UNITS));
367+
assert_ok!(Balances::force_set_balance(
368+
RawOrigin::Root.into(),
369+
GigaHdx::gigapot_account_id(),
370+
100 * UNITS,
371+
));
372+
// total_staked_hdx = TotalLocked(170) + gigapot(100) = 270; supply = 170.
373+
assert_rate_eq(GigaHdx::exchange_rate(), 270 * UNITS, 170 * UNITS);
374+
375+
let gigahdx = pallet_gigahdx::Stakes::<Runtime>::get(&alice)
376+
.expect("stake exists")
377+
.gigahdx;
378+
assert_eq!(gigahdx, 100 * UNITS);
379+
(alice, gigahdx)
380+
}
381+
382+
#[test]
383+
fn realize_yield_should_match_direct_unstake_exactly_when_rate_causes_rounding() {
384+
// Alice's 100·UNITS GIGAHDX at rate 27/17 converts to
385+
// floor(10^14 · 27 / 17) = 158_823_529_411_764 (remainder 12/17 — a real
386+
// floor-round, not a clean boundary). The three paths must still agree to
387+
// the atomic unit: realize_yield leaves total_staked_hdx and the supply
388+
// untouched, so all three evaluate the *same* floored conversion.
389+
let expected: Balance = 158_823_529_411_764;
390+
// Yield Alice consumes from the pot; the rest backs Bob's still-live stake.
391+
let alice_accrued: Balance = expected - 100 * UNITS; // 58_823_529_411_764
392+
let pot_residual: Balance = 100 * UNITS - alice_accrued; // 41_176_470_588_236
393+
394+
// Scenario 1: unstake everything directly — payout folds in the yield
395+
// (100·UNITS principal + `alice_accrued` pulled from the gigapot).
396+
let x1 = {
397+
TestNet::reset();
398+
hydra_live_ext(PATH_TO_SNAPSHOT).execute_with(|| {
399+
let (alice, gigahdx) = stake_two_then_set_rounding_rate();
400+
401+
assert_ok!(GigaHdx::giga_unstake(RuntimeOrigin::signed(alice.clone()), gigahdx));
402+
403+
let entry = only_pending_position(&alice);
404+
assert_eq!(
405+
Balances::free_balance(GigaHdx::gigapot_account_id()),
406+
pot_residual,
407+
"only Alice's share leaves the pot; Bob's backing stays"
408+
);
409+
entry.amount
410+
})
411+
};
412+
413+
// Scenario 2: realize yield — accrued HDX is folded into the locked
414+
// principal, GIGAHDX and the rate left unchanged.
415+
let x2 = {
416+
TestNet::reset();
417+
hydra_live_ext(PATH_TO_SNAPSHOT).execute_with(|| {
418+
let (alice, _) = stake_two_then_set_rounding_rate();
419+
420+
assert_ok!(GigaHdx::realize_yield(RuntimeOrigin::signed(alice.clone())));
421+
422+
let stake = pallet_gigahdx::Stakes::<Runtime>::get(&alice).expect("stake exists");
423+
assert_eq!(stake.hdx, locked_under_ghdx(&alice), "realized principal fully locked");
424+
assert_eq!(
425+
Balances::free_balance(GigaHdx::gigapot_account_id()),
426+
pot_residual,
427+
"only Alice's share leaves the pot; Bob's backing stays"
428+
);
429+
stake.hdx
430+
})
431+
};
432+
433+
// Scenario 3: realize yield, then unstake everything — Alice's pot share
434+
// is already folded in, so the full payout comes from principal alone and
435+
// the pot is untouched by the unstake.
436+
let x3 = {
437+
TestNet::reset();
438+
hydra_live_ext(PATH_TO_SNAPSHOT).execute_with(|| {
439+
let (alice, gigahdx) = stake_two_then_set_rounding_rate();
440+
441+
assert_ok!(GigaHdx::realize_yield(RuntimeOrigin::signed(alice.clone())));
442+
assert_ok!(GigaHdx::giga_unstake(RuntimeOrigin::signed(alice.clone()), gigahdx));
443+
444+
let entry = only_pending_position(&alice);
445+
assert_eq!(
446+
Balances::free_balance(GigaHdx::gigapot_account_id()),
447+
pot_residual,
448+
"only Alice's share leaves the pot; Bob's backing stays"
449+
);
450+
entry.amount
451+
})
452+
};
453+
454+
// The conversion really floor-rounds — otherwise the test is vacuous.
455+
assert_ne!(expected % UNITS, 0, "rate must actually floor-round");
456+
457+
assert_eq!(x1, expected, "direct-unstake pending payout");
458+
assert_eq!(x2, expected, "realized locked principal");
459+
assert_eq!(x3, expected, "realize-then-unstake pending payout");
460+
assert_eq!(x1, x2, "realize_yield must match direct unstake");
461+
assert_eq!(x1, x3, "realize-then-unstake must match direct unstake");
462+
}
463+
351464
#[test]
352465
fn unlock_should_release_lock_when_cooldown_elapsed() {
353466
TestNet::reset();

0 commit comments

Comments
 (0)