difftreelog
Merge pull request #727 from UniqueNetwork/feature/shortTimingsAppPromotion
in: master
Feature/short timings app promotion
2 files changed
runtime/common/config/pallets/app_promotion.rsdiffbeforeafterboth29#[cfg(all(not(feature = "unique-runtime"), not(feature = "quartz-runtime")))]29#[cfg(all(not(feature = "unique-runtime"), not(feature = "quartz-runtime")))]30parameter_types! {30parameter_types! {31 pub const AppPromotionId: PalletId = PalletId(*b"appstake");31 pub const AppPromotionId: PalletId = PalletId(*b"appstake");32 pub const RecalculationInterval: BlockNumber = 20;32 pub const RecalculationInterval: BlockNumber = 8;33 pub const PendingInterval: BlockNumber = 10;33 pub const PendingInterval: BlockNumber = 4;34 pub const Nominal: Balance = UNIQUE;34 pub const Nominal: Balance = UNIQUE;35 // pub const Day: BlockNumber = DAYS;35 // pub const Day: BlockNumber = DAYS;36 pub IntervalIncome: Perbill = Perbill::from_rational(RecalculationInterval::get(), RELAY_DAYS) * Perbill::from_rational(5u32, 10_000);36 pub IntervalIncome: Perbill = Perbill::from_rational(RecalculationInterval::get(), RELAY_DAYS) * Perbill::from_rational(5u32, 10_000);tests/src/app-promotion.test.tsdiffbeforeafterboth24let nominal: bigint;24let nominal: bigint;25let palletAddress: string;25let palletAddress: string;26let accounts: IKeyringPair[];26let accounts: IKeyringPair[];27const LOCKING_PERIOD = 20n; // 20 blocks of relay27const LOCKING_PERIOD = 8n; // 8 blocks of relay28const UNLOCKING_PERIOD = 10n; // 10 blocks of parachain28const UNLOCKING_PERIOD = 4n; // 4 blocks of parachain29const rewardAvailableInBlock = (stakedInBlock: bigint) => {30 if (stakedInBlock % LOCKING_PERIOD === 0n) return stakedInBlock + 20n;31 return (stakedInBlock - stakedInBlock % LOCKING_PERIOD) + (LOCKING_PERIOD * 2n);32};332934describe('App promotion', () => {30describe('App promotion', () => {35 before(async function () {31 before(async function () {548 expect(totalStakedAfter).to.equal(totalStakedBefore + (100n * nominal) + totalPayout);544 expect(totalStakedAfter).to.equal(totalStakedBefore + (100n * nominal) + totalPayout);549 // staker can unstake545 // staker can unstake550 await helper.staking.unstake(staker);546 await helper.staking.unstake(staker);551 expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedAfter - calculateIncome(100n * nominal, 10n));547 expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedAfter - calculateIncome(100n * nominal));552 });548 });553 549 554 itSub('should credit 0.05% for staking period', async ({helper}) => { 550 itSub('should credit 0.05% for staking period', async ({helper}) => { 564 await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake2.block));560 await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake2.block));565 561 566 const payoutToStaker = (await helper.admin.payoutStakers(palletAdmin, 100)).find((payout) => payout.staker === staker.address)?.payout;562 const payoutToStaker = (await helper.admin.payoutStakers(palletAdmin, 100)).find((payout) => payout.staker === staker.address)?.payout;567 expect(payoutToStaker + 300n * nominal).to.equal(calculateIncome(300n * nominal, 10n));563 expect(payoutToStaker + 300n * nominal).to.equal(calculateIncome(300n * nominal));568 564 569 const totalStakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});565 const totalStakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});570 expect(totalStakedPerBlock[0].amount).to.equal(calculateIncome(100n * nominal, 10n));566 expect(totalStakedPerBlock[0].amount).to.equal(calculateIncome(100n * nominal));571 expect(totalStakedPerBlock[1].amount).to.equal(calculateIncome(200n * nominal, 10n));567 expect(totalStakedPerBlock[1].amount).to.equal(calculateIncome(200n * nominal));572 });568 });573 569 574 itSub('shoud be paid for more than one period if payments was missed', async ({helper}) => {570 itSub('shoud be paid for more than one period if payments was missed', async ({helper}) => {581 577 582 await helper.admin.payoutStakers(palletAdmin, 100);578 await helper.admin.payoutStakers(palletAdmin, 100);583 [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});579 [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});584 const frozenBalanceShouldBe = calculateIncome(100n * nominal, 10n, 2);580 const frozenBalanceShouldBe = calculateIncome(100n * nominal, 2);585 expect(stake.amount).to.be.equal(frozenBalanceShouldBe);581 expect(stake.amount).to.be.equal(frozenBalanceShouldBe);586 582 587 const stakerFullBalance = await helper.balance.getSubstrateFull(staker.address);583 const stakerFullBalance = await helper.balance.getSubstrateFull(staker.address);615 611 616 await helper.admin.payoutStakers(palletAdmin, 100);612 await helper.admin.payoutStakers(palletAdmin, 100);617 [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});613 [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});618 expect(stake.amount).to.equal(calculateIncome(100n * nominal, 10n));614 expect(stake.amount).to.equal(calculateIncome(100n * nominal));619 615 620 await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD);616 await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD);621 await helper.admin.payoutStakers(palletAdmin, 100);617 await helper.admin.payoutStakers(palletAdmin, 100);622 [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});618 [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});623 expect(stake.amount).to.equal(calculateIncome(100n * nominal, 10n, 2));619 expect(stake.amount).to.equal(calculateIncome(100n * nominal, 2));624 });620 });625 621 626 itSub.skip('can be paid 1000 rewards in a time', async ({helper}) => {622 itSub.skip('can be paid 1000 rewards in a time', async ({helper}) => {653 });649 });654});650});655651656function calculateIncome(base: bigint, calcPeriod: bigint, iter = 0): bigint {652function calculateIncome(base: bigint, iter = 0, calcPeriod: bigint = UNLOCKING_PERIOD): bigint {657 const DAY = 7200n;653 const DAY = 7200n;658 const ACCURACY = 1_000_000_000n;654 const ACCURACY = 1_000_000_000n;655 // 5n / 10_000n = 0.05% p/day659 const income = base + base * (ACCURACY * (calcPeriod * 5n) / (10_000n * DAY)) / ACCURACY ;656 const income = base + base * (ACCURACY * (calcPeriod * 5n) / (10_000n * DAY)) / ACCURACY ;660 657 661 if (iter > 1) {658 if (iter > 1) {662 return calculateIncome(income, calcPeriod, iter - 1);659 return calculateIncome(income, iter - 1, calcPeriod);663 } else return income;660 } else return income;664}661}662663function rewardAvailableInBlock(stakedInBlock: bigint) {664 if (stakedInBlock % LOCKING_PERIOD === 0n) return stakedInBlock + LOCKING_PERIOD;665 return (stakedInBlock - stakedInBlock % LOCKING_PERIOD) + (LOCKING_PERIOD * 2n);666}665667666// Wait while promotion period less than specified block, to avoid boundary cases668// Wait while promotion period less than specified block, to avoid boundary cases667// 0 if this should be the beginning of the period.669// 0 if this should be the beginning of the period.