Skip to content

Commit

Permalink
Show total USD stake banner on SNS neurons page (#6042)
Browse files Browse the repository at this point in the history
# Motivation

We want to show people the total value for their SNS neurons in USD and
ICP.

Analogous to #6038

# Changes

1. Add `UsdValueBanner` on `SnsNeurons` if
`ENABLE_USD_VALUES_FOR_NEURONS` is enabled.
2. Show `-/-` instead of `0.00` if the token price for this SNS is not
known.

# Tests

1. Unit tests added.
2. Tested manually at
https://qsgjb-riaaa-aaaaa-aaaga-cai.dskloet-ingress.devenv.dfinity.network/neurons/?u=bd3sg-teaaa-aaaaa-qaaba-cai

# Todos

- [ ] Add entry to changelog (if necessary).
not yet
  • Loading branch information
dskloetd authored Dec 19, 2024
1 parent 832d650 commit 4a67c9a
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 3 deletions.
37 changes: 34 additions & 3 deletions frontend/src/lib/pages/SnsNeurons.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import TestIdWrapper from "$lib/components/common/TestIdWrapper.svelte";
import NeuronsTable from "$lib/components/neurons/NeuronsTable/NeuronsTable.svelte";
import EmptyMessage from "$lib/components/ui/EmptyMessage.svelte";
import UsdValueBanner from "$lib/components/ui/UsdValueBanner.svelte";
import { icpSwapUsdPricesStore } from "$lib/derived/icp-swap.derived";
import { pageStore } from "$lib/derived/page.derived";
import {
Expand All @@ -13,12 +13,15 @@
import { claimNextNeuronIfNeeded } from "$lib/services/sns-neurons-check-balances.services";
import { syncSnsNeurons } from "$lib/services/sns-neurons.services";
import { authStore } from "$lib/stores/auth.store";
import { ENABLE_USD_VALUES_FOR_NEURONS } from "$lib/stores/feature-flags.store";
import { i18n } from "$lib/stores/i18n";
import { snsNeuronsStore } from "$lib/stores/sns-neurons.store";
import type { TableNeuron } from "$lib/types/neurons-table";
import type { SnsSummary } from "$lib/types/sns";
import { replacePlaceholders } from "$lib/utils/i18n.utils";
import { tableNeuronsFromSnsNeurons } from "$lib/utils/neurons-table.utils";
import { getTotalStakeInUsd } from "$lib/utils/staking.utils";
import { IconNeuronsPage } from "@dfinity/gix-components";
import { Spinner } from "@dfinity/gix-components";
import type { Principal } from "@dfinity/principal";
import { nonNullish } from "@dfinity/utils";
Expand Down Expand Up @@ -56,6 +59,18 @@
})
: [];
let isTokenPriceKnown: boolean;
$: isTokenPriceKnown =
nonNullish($icpSwapUsdPricesStore) &&
$icpSwapUsdPricesStore !== "error" &&
nonNullish(summary) &&
summary.ledgerCanisterId.toText() in $icpSwapUsdPricesStore;
let totalStakeInUsd: number | undefined;
$: totalStakeInUsd = isTokenPriceKnown
? getTotalStakeInUsd(tableNeurons)
: undefined;
$: claimNextNeuronIfNeeded({
rootCanisterId: $snsOnlyProjectStore,
neurons:
Expand All @@ -64,10 +79,18 @@
});
</script>

<TestIdWrapper testId="sns-neurons-component">
<div class="wrapper" data-tid="sns-neurons-component">
{#if loading}
<Spinner />
{:else if tableNeurons.length > 0}
{#if $ENABLE_USD_VALUES_FOR_NEURONS}
<UsdValueBanner
usdAmount={totalStakeInUsd}
hasUnpricedTokens={!isTokenPriceKnown}
>
<IconNeuronsPage slot="icon" />
</UsdValueBanner>
{/if}
<NeuronsTable neurons={tableNeurons} />
{:else if nonNullish(summary)}
<EmptyMessage
Expand All @@ -77,4 +100,12 @@
})}</EmptyMessage
>
{/if}
</TestIdWrapper>
</div>

<style lang="scss">
.wrapper {
display: flex;
flex-direction: column;
gap: var(--padding-2x);
}
</style>
65 changes: 65 additions & 0 deletions frontend/src/tests/lib/pages/SnsNeurons.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,71 @@ describe("SnsNeurons", () => {
expect(await rows[0].getStakeInUsd()).toBe("$0.40");
expect(await rows[1].getStakeInUsd()).toBe("$0.20");
});

it("should not show total USD value banner when feature flag is disabled", async () => {
overrideFeatureFlagsStore.setFlag("ENABLE_USD_VALUES_FOR_NEURONS", false);

const po = await renderComponent();

expect(await po.getUsdValueBannerPo().isPresent()).toBe(false);
});

it("should show total USD value banner when feature flag is enabled", async () => {
overrideFeatureFlagsStore.setFlag("ENABLE_USD_VALUES_FOR_NEURONS", true);

const po = await renderComponent();

expect(await po.getUsdValueBannerPo().isPresent()).toBe(true);
});

it("should show total stake in USD", async () => {
overrideFeatureFlagsStore.setFlag("ENABLE_USD_VALUES_FOR_NEURONS", true);

icpSwapTickersStore.set([
{
...mockIcpSwapTicker,
base_id: CKUSDC_UNIVERSE_CANISTER_ID.toText(),
last_price: "10.00",
},
{
...mockIcpSwapTicker,
base_id: ledgerCanisterId.toText(),
last_price: "100.00",
},
]);

const po = await renderComponent();

expect(await po.getUsdValueBannerPo().isPresent()).toBe(true);
// We have a stake of 4 and 2 in the neurons, for a total of 6.
// There are 10 USD in 1 ICP and 100 SNS tokens in 1 ICP.
// So each token is $0.10.
expect(await po.getUsdValueBannerPo().getPrimaryAmount()).toBe("$0.60");
expect(
await po.getUsdValueBannerPo().getTotalsTooltipIconPo().isPresent()
).toBe(false);
});

it("should show absent total stake in USD if token price is unknown", async () => {
overrideFeatureFlagsStore.setFlag("ENABLE_USD_VALUES_FOR_NEURONS", true);

icpSwapTickersStore.set([
{
...mockIcpSwapTicker,
base_id: CKUSDC_UNIVERSE_CANISTER_ID.toText(),
last_price: "10.00",
},
// No price for the SNS token.
]);

const po = await renderComponent();

expect(await po.getUsdValueBannerPo().isPresent()).toBe(true);
expect(await po.getUsdValueBannerPo().getPrimaryAmount()).toBe("$-/-");
expect(
await po.getUsdValueBannerPo().getTotalsTooltipIconPo().isPresent()
).toBe(true);
});
});

describe("no neurons", () => {
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/tests/page-objects/SnsNeurons.page-object.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { NeuronsTablePo } from "$tests/page-objects/NeuronsTable.page-object";
import { UsdValueBannerPo } from "$tests/page-objects/UsdValueBanner.page-object";
import { BasePageObject } from "$tests/page-objects/base.page-object";
import type { PageObjectElement } from "$tests/types/page-object.types";

Expand All @@ -9,6 +10,10 @@ export class SnsNeuronsPo extends BasePageObject {
return new SnsNeuronsPo(element.byTestId(SnsNeuronsPo.TID));
}

getUsdValueBannerPo(): UsdValueBannerPo {
return UsdValueBannerPo.under(this.root);
}

getNeuronsTablePo(): NeuronsTablePo {
return NeuronsTablePo.under(this.root);
}
Expand Down

0 comments on commit 4a67c9a

Please sign in to comment.