From 3eb8d987758d25ead0f6052d607c073c2a919105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lloren=C3=A7=20Muntaner?= Date: Wed, 13 Sep 2023 17:20:24 +0200 Subject: [PATCH] Feat: Add amount of maturity to NeuronSelectPercentage (#3314) # Motivation Improving the disburse maturity flow. This PR is one of the few I'll do to split #3284 into smaller PRs. In this PR, we add the amount of maturity in the component where a percentage of maturity is selected. # Changes * New sns neuron util `maturityPercentageToE8s`. * Use new sns neuron util in NeuronSelectPercantage to show the amount of maturity related to the percentage. # Tests * Test new util. * Test that the amount of maturity is visible in the DisburseMaturityModal. # Todos - [x] Add entry to changelog (if necessary). --- CHANGELOG-Nns-Dapp.md | 2 + .../NeuronSelectPercentage.svelte | 28 ++++++++--- frontend/src/lib/i18n/en.json | 1 + frontend/src/lib/types/i18n.d.ts | 1 + frontend/src/lib/utils/neuron.utils.ts | 15 +++++- .../sns/SnsDisburseMaturityModal.spec.ts | 46 +++++++++++-------- .../src/tests/lib/utils/neuron.utils.spec.ts | 21 +++++++++ .../DisburseMaturityModal.page-object.ts | 4 ++ .../NeuronSelectPercentage.page-object.ts | 4 ++ 9 files changed, 95 insertions(+), 27 deletions(-) diff --git a/CHANGELOG-Nns-Dapp.md b/CHANGELOG-Nns-Dapp.md index cf4e0b9bff9..9f6792341eb 100644 --- a/CHANGELOG-Nns-Dapp.md +++ b/CHANGELOG-Nns-Dapp.md @@ -12,6 +12,8 @@ The NNS Dapp is released through proposals in the Network Nervous System. Theref #### Added +* Add the amount of maturity related to a selected percentage. + #### Changed * Show the token selector also when not signed in. diff --git a/frontend/src/lib/components/neuron-detail/NeuronSelectPercentage.svelte b/frontend/src/lib/components/neuron-detail/NeuronSelectPercentage.svelte index 5ae705ca6cc..66f845b4498 100644 --- a/frontend/src/lib/components/neuron-detail/NeuronSelectPercentage.svelte +++ b/frontend/src/lib/components/neuron-detail/NeuronSelectPercentage.svelte @@ -4,12 +4,21 @@ import { InputRange, KeyValuePair } from "@dfinity/gix-components"; import { createEventDispatcher } from "svelte"; import TestIdWrapper from "$lib/components/common/TestIdWrapper.svelte"; + import { maturityPercentageToE8s } from "$lib/utils/neuron.utils"; + import { formatMaturity } from "$lib/utils/neuron.utils"; + import { replacePlaceholders } from "$lib/utils/i18n.utils"; export let formattedMaturity: string; export let percentage: number; export let buttonText: string; export let disabled = false; + let maturityE8s: bigint; + $: maturityE8s = maturityPercentageToE8s({ + percentage, + total: Number(formattedMaturity), + }); + const dispatcher = createEventDispatcher(); const selectPercentage = () => dispatcher("nnsSelectPercentage"); @@ -34,10 +43,17 @@ bind:value={percentage} />
- {formatPercentage(percentage / 100, { - minFraction: 0, - maxFraction: 0, - })} + {replacePlaceholders($i18n.neuron_detail.amount_maturity, { + $amount: formatMaturity(maturityE8s), + })} + {formatPercentage(percentage / 100, { + minFraction: 0, + maxFraction: 0, + })}
@@ -58,7 +74,7 @@ diff --git a/frontend/src/lib/i18n/en.json b/frontend/src/lib/i18n/en.json index 6f84a102865..5fff149e010 100644 --- a/frontend/src/lib/i18n/en.json +++ b/frontend/src/lib/i18n/en.json @@ -667,6 +667,7 @@ "advanced_settings_title": "Advanced details & settings", "neuron_account": "Neuron Account", "dissolve_date": "Dissolve Date", + "amount_maturity": "$amount maturity", "created": "Date created" }, "sns_launchpad": { diff --git a/frontend/src/lib/types/i18n.d.ts b/frontend/src/lib/types/i18n.d.ts index 228cfa3749f..fdc2549550d 100644 --- a/frontend/src/lib/types/i18n.d.ts +++ b/frontend/src/lib/types/i18n.d.ts @@ -690,6 +690,7 @@ interface I18nNeuron_detail { advanced_settings_title: string; neuron_account: string; dissolve_date: string; + amount_maturity: string; created: string; } diff --git a/frontend/src/lib/utils/neuron.utils.ts b/frontend/src/lib/utils/neuron.utils.ts index 1994116d1c8..620a62a74f4 100644 --- a/frontend/src/lib/utils/neuron.utils.ts +++ b/frontend/src/lib/utils/neuron.utils.ts @@ -56,7 +56,7 @@ import { nowInSeconds } from "./date.utils"; import { formatNumber } from "./format.utils"; import { getVotingBallot, getVotingPower } from "./proposals.utils"; import { toNnsVote } from "./sns-proposals.utils"; -import { formatToken } from "./token.utils"; +import { formatToken, numberToE8s } from "./token.utils"; import { isDefined } from "./utils"; export type StateInfo = { @@ -954,3 +954,16 @@ export const maturityLastDistribution = ({ export const neuronDashboardUrl = ({ neuronId }: NeuronInfo): string => `https://dashboard.internetcomputer.org/neuron/${neuronId.toString()}`; + +export const maturityPercentageToE8s = ({ + total, + percentage, +}: { + total: number; + percentage: number; +}): bigint => + numberToE8s( + // Use toFixed to avoid Token validation error "Number X has more than 8 decimals" + // due to `numberToE8s` validation of floating-point approximation issues of IEEE 754 (e.g. 0.1 + 0.2 = 0.30000000000000004) + Number(((percentage / 100) * total).toFixed(8)) + ); diff --git a/frontend/src/tests/lib/modals/sns/SnsDisburseMaturityModal.spec.ts b/frontend/src/tests/lib/modals/sns/SnsDisburseMaturityModal.spec.ts index c714e4b6baf..9d555e14f40 100644 --- a/frontend/src/tests/lib/modals/sns/SnsDisburseMaturityModal.spec.ts +++ b/frontend/src/tests/lib/modals/sns/SnsDisburseMaturityModal.spec.ts @@ -7,9 +7,13 @@ import SnsDisburseMaturityModal from "$lib/modals/sns/neurons/SnsDisburseMaturit import { authStore } from "$lib/stores/auth.store"; import { mockIdentity, mockPrincipal } from "$tests/mocks/auth.store.mock"; import { renderModal } from "$tests/mocks/modal.mock"; -import { mockSnsNeuron } from "$tests/mocks/sns-neurons.mock"; +import { + createMockSnsNeuron, + mockSnsNeuron, +} from "$tests/mocks/sns-neurons.mock"; import { DisburseMaturityModalPo } from "$tests/page-objects/DisburseMaturityModal.page-object"; import { JestPageObjectElement } from "$tests/page-objects/jest.page-object"; +import type { SnsNeuron } from "@dfinity/sns"; import { waitFor } from "@testing-library/svelte"; jest.mock("$lib/api/sns-governance.api"); @@ -17,24 +21,21 @@ jest.mock("$lib/api/sns-governance.api"); describe("SnsDisburseMaturityModal", () => { const reloadNeuron = jest.fn(); - const props = { - neuronId: mockSnsNeuron.id, - neuron: mockSnsNeuron, - rootCanisterId: mockPrincipal, - reloadNeuron, + const renderSnsDisburseMaturityModal = async ( + neuron: SnsNeuron = mockSnsNeuron + ): Promise => { + const { container } = await renderModal({ + component: SnsDisburseMaturityModal, + props: { + neuronId: neuron.id, + neuron, + rootCanisterId: mockPrincipal, + reloadNeuron, + }, + }); + return DisburseMaturityModalPo.under(new JestPageObjectElement(container)); }; - const renderSnsDisburseMaturityModal = - async (): Promise => { - const { container } = await renderModal({ - component: SnsDisburseMaturityModal, - props, - }); - return DisburseMaturityModalPo.under( - new JestPageObjectElement(container) - ); - }; - beforeEach(() => { authStore.setForTesting(mockIdentity); }); @@ -51,11 +52,16 @@ describe("SnsDisburseMaturityModal", () => { expect(await po.isNextButtonDisabled()).toBe(false); }); - it("should display selected percentage", async () => { - const po = await renderSnsDisburseMaturityModal(); + it("should display selected percentage and total maturity", async () => { + const neuron = createMockSnsNeuron({ + id: [1], + maturity: 1_000_000_000n, + }); + const po = await renderSnsDisburseMaturityModal(neuron); await po.setPercentage(13); - await po.clickNextButton(); + expect(await po.getAmountMaturityToDisburse()).toBe("1.30 maturity"); + await po.clickNextButton(); expect(await po.getText()).toContain(`13%`); }); diff --git a/frontend/src/tests/lib/utils/neuron.utils.spec.ts b/frontend/src/tests/lib/utils/neuron.utils.spec.ts index cd7488c3163..a8ef7db7497 100644 --- a/frontend/src/tests/lib/utils/neuron.utils.spec.ts +++ b/frontend/src/tests/lib/utils/neuron.utils.spec.ts @@ -55,6 +55,7 @@ import { mapMergeableNeurons, mapNeuronIds, maturityLastDistribution, + maturityPercentageToE8s, minNeuronSplittable, neuronAge, neuronCanBeSplit, @@ -2421,4 +2422,24 @@ describe("neuron-utils", () => { ).toBe(false); }); }); + + describe("maturityPercentageToE8s", () => { + it("calculates percents ", () => { + expect( + maturityPercentageToE8s({ + total: 100, + percentage: 50, + }) + ).toEqual(5_000_000_000n); + }); + + it("handles more than 8 decimals results", () => { + expect( + maturityPercentageToE8s({ + total: 1.00001, + percentage: 1, + }) + ).toEqual(1_000_010n); + }); + }); }); diff --git a/frontend/src/tests/page-objects/DisburseMaturityModal.page-object.ts b/frontend/src/tests/page-objects/DisburseMaturityModal.page-object.ts index c799120e7e3..aeb6b37a854 100644 --- a/frontend/src/tests/page-objects/DisburseMaturityModal.page-object.ts +++ b/frontend/src/tests/page-objects/DisburseMaturityModal.page-object.ts @@ -35,4 +35,8 @@ export class DisburseMaturityModalPo extends BasePageObject { setPercentage(percentage: number): Promise { return this.getNeuronSelectPercentagePo().setPercentage(percentage); } + + getAmountMaturityToDisburse(): Promise { + return this.getNeuronSelectPercentagePo().getAmountMaturity(); + } } diff --git a/frontend/src/tests/page-objects/NeuronSelectPercentage.page-object.ts b/frontend/src/tests/page-objects/NeuronSelectPercentage.page-object.ts index 1247eca4467..a1463432d82 100644 --- a/frontend/src/tests/page-objects/NeuronSelectPercentage.page-object.ts +++ b/frontend/src/tests/page-objects/NeuronSelectPercentage.page-object.ts @@ -34,4 +34,8 @@ export class NeuronSelectPercentagePo extends BasePageObject { setPercentage(percentage: number): Promise { return this.getInputRangePo().setValue(percentage); } + + getAmountMaturity(): Promise { + return this.getText("amount-maturity"); + } }