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

Draft: vp from network economics #6165

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
88407bf
Add getNetworkEconomicsParameters api
mstrasinskis Jan 15, 2025
e93002e
New networkEconomicsParametersStore
mstrasinskis Jan 15, 2025
b89fc21
Add networkEconomicsStore to debug store
mstrasinskis Jan 15, 2025
118f24d
Load network economics on app load
mstrasinskis Jan 15, 2025
ffc2829
Add voting power economics derived stores
mstrasinskis Jan 15, 2025
41ea854
Switch to economics in ConfirmFollowingBanner
mstrasinskis Jan 15, 2025
c7e984d
Switch to economics in ConfirmFollowingBanner
mstrasinskis Jan 15, 2025
3c21b86
reset network economics store
mstrasinskis Jan 15, 2025
dea54fb
Switch to economics in NnsNeuronRewardStatusAction
mstrasinskis Jan 15, 2025
240508e
Switch to economics in LosingRewardsBanner
mstrasinskis Jan 15, 2025
3e31a89
Switch to economics in LosingRewardNeuronsModal
mstrasinskis Jan 15, 2025
c9b6a78
Merge branch 'main' into get-network-economics-parameters
mstrasinskis Jan 16, 2025
e6affe1
Update utils to use derived stores
mstrasinskis Jan 17, 2025
8d238cb
Update tests
mstrasinskis Jan 17, 2025
b1d4eba
Merge branch 'main' into get-network-economics-parameters
mstrasinskis Jan 17, 2025
cd61a3b
Merge branch 'get-network-economics-parameters' of github.com:dfinity…
mstrasinskis Jan 17, 2025
16bbe25
Extend utility tests
mstrasinskis Jan 17, 2025
770bb73
Merge branch 'main' into get-network-economics-parameters
mstrasinskis Jan 20, 2025
78c83c2
Typo
mstrasinskis Jan 20, 2025
4688ccf
Merge branch 'get-network-economics-parameters' of github.com:dfinity…
mstrasinskis Jan 20, 2025
ae5f1b2
Merge branch 'main' into get-network-economics-parameters
mstrasinskis Jan 21, 2025
6bc3127
Merge branch 'main' into get-network-economics-parameters
mstrasinskis Jan 22, 2025
1a6cdf8
Merge branch 'main' into get-network-economics-parameters
mstrasinskis Jan 23, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,26 @@
import Banner from "$lib/components/ui/Banner.svelte";
import BannerIcon from "$lib/components/ui/BannerIcon.svelte";
import { replacePlaceholders } from "$lib/utils/i18n.utils";
import { START_REDUCING_VOTING_POWER_AFTER_SECONDS } from "$lib/constants/neurons.constants";
import { secondsToDissolveDelayDuration } from "$lib/utils/date.utils";
import { startReducingVotingPowerAfterSecondsStore } from "$lib/derived/network-economics.derived";
import { nonNullish } from "@dfinity/utils";

let title: string;
$: title = $i18n.losing_rewards_banner.confirm_title;

const text = replacePlaceholders($i18n.losing_rewards.description, {
$period: secondsToDissolveDelayDuration(
BigInt(START_REDUCING_VOTING_POWER_AFTER_SECONDS)
),
});
</script>

<Banner testId="confirm-following-banner-component" {title} {text}>
<BannerIcon slot="icon" status="error">
<IconErrorOutline />
</BannerIcon>
</Banner>
{#if nonNullish($startReducingVotingPowerAfterSecondsStore)}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<Banner
testId="confirm-following-banner-component"
{title}
text={replacePlaceholders($i18n.losing_rewards.description, {
$period: secondsToDissolveDelayDuration(
$startReducingVotingPowerAfterSecondsStore
),
})}
>
<BannerIcon slot="icon" status="error">
<IconErrorOutline />
</BannerIcon>
</Banner>
{/if}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
} from "$lib/utils/neuron.utils";
import type { NeuronInfo } from "@dfinity/nns";
import { ICPToken, TokenAmountV2 } from "@dfinity/utils";
import { startReducingVotingPowerAfterSecondsStore } from "$lib/derived/network-economics.derived";

export let neuron: NeuronInfo;

Expand All @@ -39,6 +40,8 @@
identity: $authStore.identity,
accounts: $icpAccountsStore,
i18n: $i18n,
startReducingVotingPowerAfterSeconds:
$startReducingVotingPowerAfterSecondsStore,
});
</script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import ConfirmFollowingActionButton from "$lib/components/neuron-detail/actions/ConfirmFollowingActionButton.svelte";
import FollowNeuronsButton from "$lib/components/neuron-detail/actions/FollowNeuronsButton.svelte";
import CommonItemAction from "$lib/components/ui/CommonItemAction.svelte";
import { START_REDUCING_VOTING_POWER_AFTER_SECONDS } from "$lib/constants/neurons.constants";
import { i18n } from "$lib/stores/i18n";
import { secondsToDissolveDelayDuration } from "$lib/utils/date.utils";
import { replacePlaceholders } from "$lib/utils/i18n.utils";
Expand All @@ -18,19 +17,37 @@
IconWarning,
} from "@dfinity/gix-components";
import { type NeuronInfo } from "@dfinity/nns";
import { secondsToDuration } from "@dfinity/utils";
import { nonNullish, secondsToDuration } from "@dfinity/utils";
import {
clearFollowingAfterSecondsStore,
startReducingVotingPowerAfterSecondsStore,
} from "$lib/derived/network-economics.derived";

export let neuron: NeuronInfo;

let isFollowingReset = false;
$: isFollowingReset = isNeuronFollowingReset(neuron);
$: isFollowingReset = isNeuronFollowingReset({
neuron,
startReducingVotingPowerAfterSeconds:
$startReducingVotingPowerAfterSecondsStore,
clearFollowingAfterSeconds: $clearFollowingAfterSecondsStore,
});

let isLosingRewards = false;
$: isLosingRewards = isNeuronLosingRewards(neuron);
$: isLosingRewards = isNeuronLosingRewards({
neuron,
startReducingVotingPowerAfterSeconds:
$startReducingVotingPowerAfterSecondsStore,
});

let isLosingRewardsSoon = false;
$: isLosingRewardsSoon =
!isLosingRewards && shouldDisplayRewardLossNotification(neuron);
!isLosingRewards &&
shouldDisplayRewardLossNotification({
neuron,
startReducingVotingPowerAfterSeconds:
$startReducingVotingPowerAfterSecondsStore,
});

let icon: typeof IconError | typeof IconWarning | typeof IconCheckCircleFill;
$: icon =
Expand All @@ -48,15 +65,26 @@
? $i18n.neuron_detail.reward_status_losing_soon
: $i18n.neuron_detail.reward_status_active;

const getDescription = (neuron: NeuronInfo): string => {
const getDescription = ({
neuron,
startReducingVotingPowerAfterSeconds,
}: {
neuron: NeuronInfo;
startReducingVotingPowerAfterSeconds: bigint;
}): string => {
if (isFollowingReset)
return $i18n.neuron_detail.reward_status_inactive_reset_description;

if (isLosingRewards)
return $i18n.neuron_detail.reward_status_inactive_description;

const timeUntilLoss = secondsToDuration({
seconds: BigInt(secondsUntilLosingRewards(neuron)),
seconds: BigInt(
secondsUntilLosingRewards({
neuron,
startReducingVotingPowerAfterSeconds,
})
),
i18n: $i18n.time,
});
return replacePlaceholders(
Expand All @@ -66,46 +94,50 @@
}
);
};

const tooltipText = replacePlaceholders($i18n.losing_rewards.description, {
$period: secondsToDissolveDelayDuration(
BigInt(START_REDUCING_VOTING_POWER_AFTER_SECONDS)
),
});
</script>

<CommonItemAction
testId="nns-neuron-reward-status-action-component"
{tooltipText}
tooltipId="neuron-reward-status-icon"
>
<span
slot="icon"
class="icon"
class:isLosingRewards
class:isLosingRewardsSoon
>
<svelte:component this={icon} />
</span>
<span slot="title" data-tid="state-title">
{title}
</span>

<span
slot="subtitle"
class="description"
class:negative={isLosingRewards || isLosingRewardsSoon}
data-tid="state-description"
{#if nonNullish($startReducingVotingPowerAfterSecondsStore) && nonNullish($clearFollowingAfterSecondsStore)}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<CommonItemAction
testId="nns-neuron-reward-status-action-component"
tooltipText={replacePlaceholders($i18n.losing_rewards.description, {
$period: secondsToDissolveDelayDuration(
$startReducingVotingPowerAfterSecondsStore
),
})}
tooltipId="neuron-reward-status-icon"
>
{getDescription(neuron)}
</span>

{#if isFollowingReset}
<FollowNeuronsButton variant="secondary" />
{:else}
<ConfirmFollowingActionButton {neuron} />
{/if}
</CommonItemAction>
<span
slot="icon"
class="icon"
class:isLosingRewards
class:isLosingRewardsSoon
>
<svelte:component this={icon} />
</span>
<span slot="title" data-tid="state-title">
{title}
</span>

<span
slot="subtitle"
class="description"
class:negative={isLosingRewards || isLosingRewardsSoon}
data-tid="state-description"
>
{getDescription({
neuron,
startReducingVotingPowerAfterSeconds:
$startReducingVotingPowerAfterSecondsStore,
})}
</span>

{#if isFollowingReset}
<FollowNeuronsButton variant="secondary" />
{:else}
<ConfirmFollowingActionButton {neuron} />
{/if}
</CommonItemAction>
{/if}

<style lang="scss">
.icon {
Expand Down
40 changes: 28 additions & 12 deletions frontend/src/lib/components/neurons/LosingRewardsBanner.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,37 +11,53 @@
import { replacePlaceholders } from "$lib/utils/i18n.utils";
import { nonNullish, secondsToDuration } from "@dfinity/utils";
import type { NeuronInfo } from "@dfinity/nns";
import { START_REDUCING_VOTING_POWER_AFTER_SECONDS } from "$lib/constants/neurons.constants";
import { secondsToDissolveDelayDuration } from "$lib/utils/date.utils";
import TestIdWrapper from "$lib/components/common/TestIdWrapper.svelte";
import LosingRewardNeuronsModal from "$lib/modals/neurons/LosingRewardNeuronsModal.svelte";
import { startReducingVotingPowerAfterSecondsStore } from "$lib/derived/network-economics.derived";

// The neurons in the store are sorted by the time they will lose rewards.
let mostInactiveNeuron: NeuronInfo | undefined;
$: mostInactiveNeuron = $soonLosingRewardNeuronsStore[0];

const getTitle = (neuron: NeuronInfo) =>
isNeuronLosingRewards(neuron)
const getTitle = ({
neuron,
startReducingVotingPowerAfterSeconds,
}: {
neuron: NeuronInfo;
startReducingVotingPowerAfterSeconds: bigint;
}) =>
isNeuronLosingRewards({ neuron, startReducingVotingPowerAfterSeconds })
? $i18n.losing_rewards_banner.rewards_missing_title
: replacePlaceholders($i18n.losing_rewards_banner.days_left_title, {
$timeLeft: secondsToDuration({
seconds: BigInt(secondsUntilLosingRewards(neuron)),
seconds: BigInt(
secondsUntilLosingRewards({
neuron,
startReducingVotingPowerAfterSeconds,
})
),
i18n: $i18n.time,
}),
});
const text = replacePlaceholders($i18n.losing_rewards.description, {
// TODO(mstr): Rename to secondsToRoundedDuration
$period: secondsToDissolveDelayDuration(
BigInt(START_REDUCING_VOTING_POWER_AFTER_SECONDS)
),
});

let isModalVisible = false;
</script>

<TestIdWrapper testId="losing-rewards-banner-component">
{#if nonNullish(mostInactiveNeuron)}
<Banner title={getTitle(mostInactiveNeuron)} {text}>
{#if nonNullish(mostInactiveNeuron) && nonNullish($startReducingVotingPowerAfterSecondsStore)}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<Banner
title={getTitle({
neuron: mostInactiveNeuron,
startReducingVotingPowerAfterSeconds:
$startReducingVotingPowerAfterSecondsStore,
})}
text={replacePlaceholders($i18n.losing_rewards.description, {
$period: secondsToDissolveDelayDuration(
$startReducingVotingPowerAfterSecondsStore
),
})}
>
<BannerIcon slot="icon" status="error">
<IconInfo />
</BannerIcon>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import { Card, IconRight } from "@dfinity/gix-components";
import type { NeuronInfo } from "@dfinity/nns";
import { nonNullish } from "@dfinity/utils";
import { startReducingVotingPowerAfterSecondsStore } from "$lib/derived/network-economics.derived";

export let neuron: NeuronInfo;
export let onClick: (() => void) | undefined;
Expand All @@ -26,6 +27,8 @@
identity: $authStore.identity,
accounts: $icpAccountsStore,
i18n: $i18n,
startReducingVotingPowerAfterSeconds:
$startReducingVotingPowerAfterSecondsStore,
});

let followees: FolloweesNeuron[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { i18n } from "$lib/stores/i18n";
import { getNeuronTags, type NeuronTagData } from "$lib/utils/neuron.utils";
import type { NeuronInfo } from "@dfinity/nns";
import { startReducingVotingPowerAfterSecondsStore } from "$lib/derived/network-economics.derived";

export let neuron: NeuronInfo;
export let tagName: "p" | "h3" = "p";
Expand All @@ -15,6 +16,8 @@
identity: $authStore.identity,
accounts: $icpAccountsStore,
i18n: $i18n,
startReducingVotingPowerAfterSeconds:
$startReducingVotingPowerAfterSecondsStore,
});
</script>

Expand Down
14 changes: 0 additions & 14 deletions frontend/src/lib/constants/neurons.constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
SECONDS_IN_7_DAYS,
SECONDS_IN_HALF_YEAR,
SECONDS_IN_MONTH,
} from "$lib/constants/constants";
import { enumValues } from "$lib/utils/enum.utils";
import { Topic } from "@dfinity/nns";
Expand Down Expand Up @@ -37,18 +36,5 @@ export const TOPICS_TO_FOLLOW_NNS = [
...LAST_TOPICS,
];

// A neuron's voting power begins to decrease linearly if it remains inactive for this duration.
// Inactivity means no manual votes cast and no updates to the list of followed neurons (followees).
// Draft ic pr: https://github.com/dfinity/ic/blob/c2da5aca97a07bae4fcbf5d72a8c0448b40599d7/rs/nns/governance/canister/governance.did#L582)
// TODO(mstr): replace with the actual ic link.
export const START_REDUCING_VOTING_POWER_AFTER_SECONDS = SECONDS_IN_HALF_YEAR;

// After a neuron has experienced voting power reduction for this amount of time,
// a couple of things happen:
// 1. Deciding voting power reaches 0.
// 2. Its following on topics other than NeuronManagement are cleared.
// https://github.com/dfinity/ic/blob/c2da5aca97a07bae4fcbf5d72a8c0448b40599d7/rs/nns/governance/canister/governance.did#L584
export const CLEAR_FOLLOWING_AFTER_SECONDS = SECONDS_IN_MONTH;

// To notify users that their rewards will start decreasing in 30 days.
export const NOTIFICATION_PERIOD_BEFORE_REWARD_LOSS_STARTS_DAYS = 30;
21 changes: 15 additions & 6 deletions frontend/src/lib/derived/neurons.derived.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { startReducingVotingPowerAfterSecondsStore } from "$lib/derived/network-economics.derived";
import { neuronsStore } from "$lib/stores/neurons.store";
import {
hasValidStake,
Expand All @@ -6,7 +7,7 @@ import {
sortNeuronsByVotingPowerRefreshedTimeout,
} from "$lib/utils/neuron.utils";
import type { NeuronInfo } from "@dfinity/nns";
import { nonNullish } from "@dfinity/utils";
import { isNullish, nonNullish } from "@dfinity/utils";
import { derived, type Readable } from "svelte/store";

export const definedNeuronsStore: Readable<NeuronInfo[]> = derived(
Expand All @@ -20,11 +21,19 @@ export const sortedNeuronStore: Readable<NeuronInfo[]> = derived(
);

export const soonLosingRewardNeuronsStore: Readable<NeuronInfo[]> = derived(
definedNeuronsStore,
($definedNeuronsStore) =>
sortNeuronsByVotingPowerRefreshedTimeout(
$definedNeuronsStore.filter(shouldDisplayRewardLossNotification)
)
[definedNeuronsStore, startReducingVotingPowerAfterSecondsStore],
([$definedNeuronsStore, $startReducingVotingPowerAfterSecondsStore]) =>
isNullish($startReducingVotingPowerAfterSecondsStore)
? []
: sortNeuronsByVotingPowerRefreshedTimeout(
$definedNeuronsStore.filter((neuron) =>
shouldDisplayRewardLossNotification({
neuron,
startReducingVotingPowerAfterSeconds:
$startReducingVotingPowerAfterSecondsStore,
})
)
)
);

export const neuronAccountsStore: Readable<Set<string>> = derived(
Expand Down
Loading
Loading