git.delta.rocks / unique-network / refs/commits / 78a9ca040ab8

difftreelog

Merge pull request #727 from UniqueNetwork/feature/shortTimingsAppPromotion

ut-akuznetsov2022-11-22parents: #17cf786 #d6ab0fe.patch.diff
in: master
Feature/short timings app promotion

2 files changed

modifiedruntime/common/config/pallets/app_promotion.rsdiffbeforeafterboth
29#[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);
modifiedtests/src/app-promotion.test.tsdiffbeforeafterboth
24let 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 relay
28const UNLOCKING_PERIOD = 10n; // 10 blocks of parachain28const UNLOCKING_PERIOD = 4n; // 4 blocks of parachain
29const rewardAvailableInBlock = (stakedInBlock: bigint) => {
30 if (stakedInBlock % LOCKING_PERIOD === 0n) return stakedInBlock + 20n;
31 return (stakedInBlock - stakedInBlock % LOCKING_PERIOD) + (LOCKING_PERIOD * 2n);
32};
3329
34describe('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 unstake
550 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});
655651
656function 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/day
659 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}
662
663function rewardAvailableInBlock(stakedInBlock: bigint) {
664 if (stakedInBlock % LOCKING_PERIOD === 0n) return stakedInBlock + LOCKING_PERIOD;
665 return (stakedInBlock - stakedInBlock % LOCKING_PERIOD) + (LOCKING_PERIOD * 2n);
666}
665667
666// Wait while promotion period less than specified block, to avoid boundary cases668// Wait while promotion period less than specified block, to avoid boundary cases
667// 0 if this should be the beginning of the period.669// 0 if this should be the beginning of the period.