From 1f67787d1e6f15d07c48dbd486983f19f2a487bf Mon Sep 17 00:00:00 2001 From: Branko Bosnic Date: Thu, 31 Oct 2024 13:27:49 +0100 Subject: [PATCH] feat: organize stardust objects on migratable and unmigratable --- .../app/(protected)/migrations/page.tsx | 75 ++++++++++--------- apps/wallet-dashboard/lib/interfaces/index.ts | 1 + .../lib/interfaces/migration.interface.ts | 47 ++++++++++++ apps/wallet-dashboard/lib/utils/index.ts | 1 + apps/wallet-dashboard/lib/utils/migration.ts | 55 ++++++++++++++ 5 files changed, 144 insertions(+), 35 deletions(-) create mode 100644 apps/wallet-dashboard/lib/interfaces/migration.interface.ts create mode 100644 apps/wallet-dashboard/lib/utils/migration.ts diff --git a/apps/wallet-dashboard/app/(protected)/migrations/page.tsx b/apps/wallet-dashboard/app/(protected)/migrations/page.tsx index 3549f4d2080..37d89caaec7 100644 --- a/apps/wallet-dashboard/app/(protected)/migrations/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/migrations/page.tsx @@ -3,48 +3,43 @@ 'use client'; import { VirtualList } from '@/components'; +import { useGetCurrentEpochStartTimestamp } from '@/hooks'; import { STARDUST_BASIC_OUTPUT_TYPE, STARDUST_NFT_OUTPUT_TYPE, } from '@/lib/constants/migration.constants'; -import { useGetOwnedObjects } from '@iota/core'; +import { groupStardustObjectsByMigrationStatus } from '@/lib/utils'; +import { useGetAllOwnedObjects } from '@iota/core'; import { useCurrentAccount, useIotaClientContext } from '@iota/dapp-kit'; import { getNetwork, IotaObjectData } from '@iota/iota-sdk/client'; function MigrationDashboardPage(): JSX.Element { const account = useCurrentAccount(); + const address = account?.address || ''; const { network } = useIotaClientContext(); const { explorer } = getNetwork(network); + const { data: currentEpochMs } = useGetCurrentEpochStartTimestamp(); - const { - data: basicOutputObjects, - fetchNextPage: fetchNextPageBasic, - hasNextPage: hasNextPageBasic, - isFetchingNextPage: isFetchingNextPageBasic, - } = useGetOwnedObjects(account?.address || '', { + const { data: basicOutputObjects } = useGetAllOwnedObjects(address, { StructType: STARDUST_BASIC_OUTPUT_TYPE, }); - const { - data: nftOutputObjects, - fetchNextPage: fetchNextPageNft, - hasNextPage: hasNextPageNft, - isFetchingNextPage: isFetchingNextPageNft, - } = useGetOwnedObjects(account?.address || '', { + const { data: nftOutputObjects } = useGetAllOwnedObjects(address, { StructType: STARDUST_NFT_OUTPUT_TYPE, }); - const basicOutputs = - basicOutputObjects?.pages - .flatMap((page) => page.data) - .filter((asset) => asset.data && asset.data.objectId) - .map((response) => response.data!) ?? []; - - const nftOutputs = - nftOutputObjects?.pages - .flatMap((page) => page.data) - .filter((asset) => asset.data && asset.data.objectId) - .map((response) => response.data!) ?? []; + const { migratable: migratableBasicOutputs, unmigratable: unmigratableBasicOutputs } = + groupStardustObjectsByMigrationStatus( + basicOutputObjects ?? [], + Number(currentEpochMs), + address, + ); + const { migratable: migratableNftOutputs, unmigratable: unmigratableNftOutputs } = + groupStardustObjectsByMigrationStatus( + nftOutputObjects ?? [], + Number(currentEpochMs), + address, + ); const virtualItem = (asset: IotaObjectData): JSX.Element => ( {asset.objectId} @@ -52,25 +47,35 @@ function MigrationDashboardPage(): JSX.Element { ); return ( -
+
+
+

Migratable Basic Outputs: {migratableBasicOutputs.length}

+ 30} + render={virtualItem} + /> +
+
+

Unmigratable Basic Outputs: {unmigratableBasicOutputs.length}

+ 30} + render={virtualItem} + /> +
-

Basic Outputs

+

Migratable NFT Outputs: {migratableNftOutputs.length}

30} render={virtualItem} />
-

Nft Outputs

+

Unmigratable NFT Outputs: {unmigratableNftOutputs.length}

30} render={virtualItem} /> diff --git a/apps/wallet-dashboard/lib/interfaces/index.ts b/apps/wallet-dashboard/lib/interfaces/index.ts index eeab2cb4f5c..679bc45212f 100644 --- a/apps/wallet-dashboard/lib/interfaces/index.ts +++ b/apps/wallet-dashboard/lib/interfaces/index.ts @@ -3,5 +3,6 @@ export * from './transactions.interface'; export * from './timelock.interface'; +export * from './migration.interface'; export * from './vesting.interface'; export * from './appRoute.interface'; diff --git a/apps/wallet-dashboard/lib/interfaces/migration.interface.ts b/apps/wallet-dashboard/lib/interfaces/migration.interface.ts new file mode 100644 index 00000000000..df35ae29112 --- /dev/null +++ b/apps/wallet-dashboard/lib/interfaces/migration.interface.ts @@ -0,0 +1,47 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +type ExpirationUnlockCondition = { + owner: string; + return_address: string; + unix_time: number; +}; +type StorageDepositReturnUnlockCondition = { + return_address: string; + return_amount: string; +}; +type TimelockUnlockCondition = { + unix_time: number; +}; + +export type CommonOutputObject = { + id: { id: string }; + balance: string; + native_tokens: { + type: string; + fields: { id: { id: string }; size: string }; + }; +}; + +export interface CommonOutputObjectWithUc extends CommonOutputObject { + expiration_uc?: { + type: string; + fields: ExpirationUnlockCondition; + }; + storage_deposit_return_uc?: { + type: string; + fields: StorageDepositReturnUnlockCondition; + }; + timelock_uc?: { + type: string; + fields: TimelockUnlockCondition; + }; +} + +export interface BasicOutputObject extends CommonOutputObjectWithUc { + metadata?: number[]; + tag?: number[]; + sender?: string; +} + +export interface NftOutputObject extends CommonOutputObjectWithUc {} diff --git a/apps/wallet-dashboard/lib/utils/index.ts b/apps/wallet-dashboard/lib/utils/index.ts index 78b0f2eb9c5..27ddc5bc265 100644 --- a/apps/wallet-dashboard/lib/utils/index.ts +++ b/apps/wallet-dashboard/lib/utils/index.ts @@ -5,5 +5,6 @@ export * from './indexGenerator'; export * from './vesting'; export * from './time'; export * from './timelock'; +export * from './migration'; export * from './transaction'; export * from './growthbook'; diff --git a/apps/wallet-dashboard/lib/utils/migration.ts b/apps/wallet-dashboard/lib/utils/migration.ts new file mode 100644 index 00000000000..c069c266da9 --- /dev/null +++ b/apps/wallet-dashboard/lib/utils/migration.ts @@ -0,0 +1,55 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { IotaObjectData } from '@iota/iota-sdk/client'; +import { CommonOutputObjectWithUc } from '../interfaces/migration.interface'; + +export type StardustMigrationGroupedObjects = { + migratable: IotaObjectData[]; + unmigratable: IotaObjectData[]; +}; + +export function groupStardustObjectsByMigrationStatus( + stardustOutputObjects: IotaObjectData[], + epochTimestamp: number, + address: string, +): StardustMigrationGroupedObjects { + const migratable: IotaObjectData[] = []; + const unmigratable: IotaObjectData[] = []; + + const epochUnix = epochTimestamp / 1000; + + for (const outputObject of stardustOutputObjects) { + if (!outputObject) { + unmigratable.push(outputObject); + continue; + } + const outputObjectFields = ( + outputObject.content as unknown as { + fields: CommonOutputObjectWithUc; + } + ).fields; + + if (outputObjectFields.expiration_uc) { + const unlockableAddress = + outputObjectFields.expiration_uc.fields.unix_time <= epochUnix + ? outputObjectFields.expiration_uc.fields.return_address + : outputObjectFields.expiration_uc.fields.owner; + if (unlockableAddress !== address) { + unmigratable.push(outputObject); + continue; + } + } + if ( + outputObjectFields.timelock_uc && + outputObjectFields.timelock_uc.fields.unix_time > epochUnix + ) { + unmigratable.push(outputObject); + continue; + } + + migratable.push(outputObject); + } + + return { migratable, unmigratable }; +}