diff --git a/CHANGELOG-Nns-Dapp-unreleased.md b/CHANGELOG-Nns-Dapp-unreleased.md index 598ee07f60a..e202a6c3362 100644 --- a/CHANGELOG-Nns-Dapp-unreleased.md +++ b/CHANGELOG-Nns-Dapp-unreleased.md @@ -22,7 +22,7 @@ proposal is successful, the changes it released will be moved from this file to #### Fixed -- Adds an override for the name and token.symbol values for abandoned SNS +- Handles abandoned SNSs by adding an override for the name and token.symbol values as well as sorting them down on the lists. #### Security diff --git a/frontend/src/lib/stores/sns-aggregator.store.ts b/frontend/src/lib/stores/sns-aggregator.store.ts index 2f3bb5b9457..209968d3ad6 100644 --- a/frontend/src/lib/stores/sns-aggregator.store.ts +++ b/frontend/src/lib/stores/sns-aggregator.store.ts @@ -3,7 +3,7 @@ import type { CachedSnsTokenMetadataDto, } from "$lib/types/sns-aggregator"; import { SnsSwapLifecycle } from "@dfinity/sns"; -import { nonNullish } from "@dfinity/utils"; +import { isNullish, nonNullish } from "@dfinity/utils"; import { derived, writable, type Readable } from "svelte/store"; /** @@ -37,35 +37,52 @@ export const snsAggregatorIncludingAbortedProjectsStore = export const snsAggregatorStore: SnsAggregatorStore = derived( snsAggregatorIncludingAbortedProjectsStore, - (store) => ({ - data: store.data - ?.filter( - (sns) => - nonNullish(sns.lifecycle) && - sns.lifecycle.lifecycle !== SnsSwapLifecycle.Aborted - ) - .map(fixBrokenSnsMetadataBasedOnId), - }) + (store) => { + const data = store.data?.filter( + (sns) => + nonNullish(sns.lifecycle) && + sns.lifecycle.lifecycle !== SnsSwapLifecycle.Aborted + ); + + if (isNullish(data)) return { data: undefined }; + + // TODO: Find a better way to fix broken SNS metadata. These transformations will be remove once we have a better solution. + const handledAbandonedSnsData = data?.map(fixBrokenSnsMetadataBasedOnId); + const sortedAbandonesSnsData = sortedListBasedOnAbandoned( + handledAbandonedSnsData + ); + const cachedSnsData = sortedAbandonesSnsData.map( + ({ isAbandoned: _, ...sns }) => ({ ...sns }) + ); + + return { + data: cachedSnsData, + }; + } ); -// TODO: Find a better way to fix broken SNS metadata. const brokenSnsOverrides: Record< string, { name: string; tokenSymbol: string } > = { // Overrided for CYCLES_TRANSFER_STATION as discussed in https://dfinity.slack.com/archives/C039M7YS6F6/p1733302975333649 "ibahq-taaaa-aaaaq-aadna-cai": { - name: "CYCLES_TRANSFER_STATION", + name: "CYCLES-TRANSFER-STATION", tokenSymbol: "CTS", }, }; -const fixBrokenSnsMetadataBasedOnId = (sns: CachedSnsDto): CachedSnsDto => { +const fixBrokenSnsMetadataBasedOnId = ( + sns: CachedSnsDto +): CachedSnsDto & { isAbandoned?: boolean } => { const override = brokenSnsOverrides[sns.list_sns_canisters.root]; + + // Required for the tokens and staking routes as they apply their own sort logic + const hiddenCharacterToPushSnsToEndOfList = "\u200B"; if (!nonNullish(override)) return sns; const newMeta = { ...sns.meta, - name: `${sns.meta.name} (formerly ${override.name})`, + name: `${hiddenCharacterToPushSnsToEndOfList}${sns.meta.name} (formerly ${override.name})`, }; const newIcrc1Metadata = sns.icrc1_metadata.map< @@ -86,5 +103,18 @@ const fixBrokenSnsMetadataBasedOnId = (sns: CachedSnsDto): CachedSnsDto => { ...sns, meta: { ...newMeta }, icrc1_metadata: [...newIcrc1Metadata], + isAbandoned: true, }; }; + +// Required for the proposals route as it doesnt apply sort logic +const sortedListBasedOnAbandoned = ( + list: (CachedSnsDto & { isAbandoned?: boolean })[] +) => [ + ...list.sort((a, b) => { + if (a.isAbandoned && !b.isAbandoned) return 1; + if (!a.isAbandoned && b.isAbandoned) return -1; + + return 0; + }), +]; diff --git a/frontend/src/tests/lib/stores/sns-aggregator.store.spec.ts b/frontend/src/tests/lib/stores/sns-aggregator.store.spec.ts index cce86257403..12586b5a1ab 100644 --- a/frontend/src/tests/lib/stores/sns-aggregator.store.spec.ts +++ b/frontend/src/tests/lib/stores/sns-aggregator.store.spec.ts @@ -153,30 +153,30 @@ describe("sns-aggregator store", () => { }, }); - it("should override information for SNS with rootCanisterId ibahq-taaaa-aaaaq-aadna-cai", () => { - const mockedSns = aggregatorMockSnsesDataDto[0]; - const brokenSns = withBrokenSns({ - sns: { - ...mockedSns, - meta: { - ...mockedSns.meta, - name: "---", - }, - icrc1_metadata: [...mockedSns.icrc1_metadata].map(([name, value]) => { - if (name === "icrc1:symbol" && "Text" in value) { - return [ - name, - { - Text: "---", - }, - ]; - } - return [name, value]; - }), + const mockedSns = aggregatorMockSnsesDataDto[0]; + const brokenSns = withBrokenSns({ + sns: { + ...mockedSns, + meta: { + ...mockedSns.meta, + name: "---", }, - rootCanisterId: "ibahq-taaaa-aaaaq-aadna-cai", - }); + icrc1_metadata: [...mockedSns.icrc1_metadata].map(([name, value]) => { + if (name === "icrc1:symbol" && "Text" in value) { + return [ + name, + { + Text: "---", + }, + ]; + } + return [name, value]; + }), + }, + rootCanisterId: "ibahq-taaaa-aaaaq-aadna-cai", + }); + it("should override information for SNS with rootCanisterId ibahq-taaaa-aaaaq-aadna-cai", () => { const data = [brokenSns]; snsAggregatorIncludingAbortedProjectsStore.setData(data); expect( @@ -188,7 +188,28 @@ describe("sns-aggregator store", () => { ).toEqual({ Text: "---" }); const result = get(snsAggregatorStore).data[0]; - expect(result.meta.name).toBe("--- (formerly CYCLES_TRANSFER_STATION)"); + expect(result.meta.name).toBe( + "\u200B--- (formerly CYCLES-TRANSFER-STATION)" + ); + expect(result.icrc1_metadata[3][1]).toEqual({ Text: "--- (CTS)" }); + }); + + it("should sort sns by temporary isAbandoded property", () => { + const data = [brokenSns, ...aggregatorMockSnsesDataDto]; + snsAggregatorIncludingAbortedProjectsStore.setData(data); + expect( + get(snsAggregatorIncludingAbortedProjectsStore).data[0].meta.name + ).toBe("---"); + expect( + get(snsAggregatorIncludingAbortedProjectsStore).data[0] + .icrc1_metadata[3][1] + ).toEqual({ Text: "---" }); + + const result = + get(snsAggregatorStore).data[get(snsAggregatorStore).data.length - 1]; + expect(result.meta.name).toBe( + "\u200B--- (formerly CYCLES-TRANSFER-STATION)" + ); expect(result.icrc1_metadata[3][1]).toEqual({ Text: "--- (CTS)" }); }); });