From 64e731fb14ec88f78a5a2d3da6a7dd31f355c15b Mon Sep 17 00:00:00 2001 From: cpl121 <cpeon@boxfish.studio> Date: Wed, 15 Jan 2025 13:35:26 +0100 Subject: [PATCH] feat(wallet-dashboard): improve tx history & details --- .../transaction/TransactionIcon.tsx | 13 +++++++- apps/core/src/constants/timelock.constants.ts | 5 +-- .../src/interfaces/transactions.interfaces.ts | 2 ++ .../checkIfIsMigrationTransaction.ts | 32 +++++++++++++++++++ ...SupplyIncreaseVestingCollectTransaction.ts | 31 ++++++++++++++++++ .../utils/transaction/getTransactionAction.ts | 23 ++++++++++--- apps/core/src/utils/transaction/index.ts | 2 ++ apps/wallet-dashboard/lib/interfaces/index.ts | 1 - .../lib/interfaces/transactions.interfaces.ts | 29 ----------------- 9 files changed, 101 insertions(+), 37 deletions(-) create mode 100644 apps/core/src/utils/transaction/checkIfIsMigrationTransaction.ts create mode 100644 apps/core/src/utils/transaction/checkIfIsSupplyIncreaseVestingCollectTransaction.ts delete mode 100644 apps/wallet-dashboard/lib/interfaces/transactions.interfaces.ts diff --git a/apps/core/src/components/transaction/TransactionIcon.tsx b/apps/core/src/components/transaction/TransactionIcon.tsx index 55b54f0fc5b..02a547bf25f 100644 --- a/apps/core/src/components/transaction/TransactionIcon.tsx +++ b/apps/core/src/components/transaction/TransactionIcon.tsx @@ -2,7 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 import { LoadingIndicator } from '@iota/apps-ui-kit'; -import { ArrowBottomLeft, ArrowTopRight, Info, IotaLogoMark, Person, Stake } from '@iota/ui-icons'; +import { + ArrowBottomLeft, + ArrowTopRight, + Info, + IotaLogoMark, + Migration, + Person, + Stake, + Vesting, +} from '@iota/ui-icons'; const ICON_COLORS = { primary: 'text-primary-30', @@ -21,6 +30,8 @@ const icons = { PersonalMessage: <Person className={ICON_COLORS.primary} />, ['Timelocked Staked']: <Stake className={ICON_COLORS.primary} />, ['Timelocked Unstaked']: <Stake className={ICON_COLORS.primary} />, + Migration: <Migration className={ICON_COLORS.primary} />, + ['Timelocked Collect']: <Vesting className={ICON_COLORS.primary} />, }; interface TransactionIconProps { diff --git a/apps/core/src/constants/timelock.constants.ts b/apps/core/src/constants/timelock.constants.ts index 6b9ab9c2627..21c7999aef0 100644 --- a/apps/core/src/constants/timelock.constants.ts +++ b/apps/core/src/constants/timelock.constants.ts @@ -1,5 +1,6 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -export const TIMELOCK_IOTA_TYPE = '0x2::timelock::TimeLock<0x2::balance::Balance<0x2::iota::IOTA>>'; -export const TIMELOCK_STAKED_TYPE = '0x3::timelocked_staking::TimelockedStakedIota'; +export const TIMELOCK_MODULE = 'timelock'; +export const TIMELOCK_IOTA_TYPE = `0x2::${TIMELOCK_MODULE}::TimeLock<0x2::balance::Balance<0x2::iota::IOTA>>`; +export const TIMELOCK_STAKED_TYPE = `0x3::timelocked_staking::TimelockedStakedIota`; diff --git a/apps/core/src/interfaces/transactions.interfaces.ts b/apps/core/src/interfaces/transactions.interfaces.ts index 4c4ea3c1962..9262652ee42 100644 --- a/apps/core/src/interfaces/transactions.interfaces.ts +++ b/apps/core/src/interfaces/transactions.interfaces.ts @@ -18,6 +18,8 @@ export enum TransactionAction { Unstaked = 'Unstaked', TimelockedStaked = 'Timelocked Staked', TimelockedUnstaked = 'Timelocked Unstaked', + TimelockedCollect = 'Timelocked Collect', + Migration = 'Migration', Rewards = 'Rewards', PersonalMessage = 'PersonalMessage', } diff --git a/apps/core/src/utils/transaction/checkIfIsMigrationTransaction.ts b/apps/core/src/utils/transaction/checkIfIsMigrationTransaction.ts new file mode 100644 index 00000000000..c3ddf762c85 --- /dev/null +++ b/apps/core/src/utils/transaction/checkIfIsMigrationTransaction.ts @@ -0,0 +1,32 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import type { + IotaTransaction, + IotaTransactionBlockResponse, + MoveCallIotaTransaction, +} from '@iota/iota-sdk/client'; +import { STARDUST_PACKAGE_ID } from '../../constants'; + +export function checkIfIsMigrationTransaction( + transaction: IotaTransactionBlockResponse['transaction'], +) { + if (!transaction || transaction.data.transaction.kind !== 'ProgrammableTransaction') + return { isMigration: false }; + const moveCallTxs = transaction.data.transaction.transactions.filter(isMoveCall); + const isMigration = moveCallTxs.some( + (tx) => + tx.MoveCall.package === STARDUST_PACKAGE_ID && + tx.MoveCall.function === 'extract_assets', + ); + + return { + isMigration, + }; +} + +function isMoveCall( + transaction: IotaTransaction, +): transaction is { MoveCall: MoveCallIotaTransaction } { + return 'MoveCall' in transaction; +} diff --git a/apps/core/src/utils/transaction/checkIfIsSupplyIncreaseVestingCollectTransaction.ts b/apps/core/src/utils/transaction/checkIfIsSupplyIncreaseVestingCollectTransaction.ts new file mode 100644 index 00000000000..aa3f47a307c --- /dev/null +++ b/apps/core/src/utils/transaction/checkIfIsSupplyIncreaseVestingCollectTransaction.ts @@ -0,0 +1,31 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import type { + IotaTransaction, + IotaTransactionBlockResponse, + MoveCallIotaTransaction, +} from '@iota/iota-sdk/client'; +import { TIMELOCK_MODULE } from '../../'; + +export function checkIfIsSupplyIncreaseVestingCollectTransaction( + transaction: IotaTransactionBlockResponse['transaction'], +) { + if (!transaction || transaction.data.transaction.kind !== 'ProgrammableTransaction') + return { isSupplyIncreaseVestingCollect: false }; + const moveCallTxs = transaction.data.transaction.transactions + .filter(isMoveCall) + .filter((tx) => tx.MoveCall.module === TIMELOCK_MODULE); + const isSupplyIncreaseVestingCollect = + moveCallTxs.length > 0 && moveCallTxs.every((tx) => tx.MoveCall.function === 'unlock'); + + return { + isSupplyIncreaseVestingCollect, + }; +} + +function isMoveCall( + transaction: IotaTransaction, +): transaction is { MoveCall: MoveCallIotaTransaction } { + return 'MoveCall' in transaction; +} diff --git a/apps/core/src/utils/transaction/getTransactionAction.ts b/apps/core/src/utils/transaction/getTransactionAction.ts index 5eeb25a4946..c781a42bcce 100644 --- a/apps/core/src/utils/transaction/getTransactionAction.ts +++ b/apps/core/src/utils/transaction/getTransactionAction.ts @@ -4,12 +4,17 @@ import { IotaTransactionBlockResponse } from '@iota/iota-sdk/client'; import { TransactionAction } from '../../interfaces'; -import { checkIfIsTimelockedStaking } from '../stake/checkIfIsTimelockedStaking'; +import { checkIfIsTimelockedStaking } from '../stake'; +import { + checkIfIsMigrationTransaction, + checkIfIsSupplyIncreaseVestingCollectTransaction, +} from '..'; export const getTransactionAction = ( transaction: IotaTransactionBlockResponse, currentAddress?: string, ) => { + const sender = transaction.transaction?.data.sender; const { isTimelockedStaking, isTimelockedUnstaking, @@ -17,14 +22,24 @@ export const getTransactionAction = ( unstakeTypeTransaction, } = checkIfIsTimelockedStaking(transaction?.events); - if (stakeTypeTransaction) { + const { isMigration } = checkIfIsMigrationTransaction(transaction.transaction); + const { isSupplyIncreaseVestingCollect } = checkIfIsSupplyIncreaseVestingCollectTransaction( + transaction.transaction, + ); + + if (isMigration) { + return TransactionAction.Migration; + } else if (isSupplyIncreaseVestingCollect) { + return TransactionAction.TimelockedCollect; + } else if (stakeTypeTransaction) { return isTimelockedStaking ? TransactionAction.TimelockedStaked : TransactionAction.Staked; } else if (unstakeTypeTransaction) { return isTimelockedUnstaking ? TransactionAction.TimelockedUnstaked : TransactionAction.Unstaked; + } else if (!!sender) { + return sender === currentAddress ? TransactionAction.Send : TransactionAction.Receive; } else { - const isSender = transaction.transaction?.data.sender === currentAddress; - return isSender ? TransactionAction.Transaction : TransactionAction.Receive; + return TransactionAction.Transaction; } }; diff --git a/apps/core/src/utils/transaction/index.ts b/apps/core/src/utils/transaction/index.ts index 1951221f6e4..f0f235f6823 100644 --- a/apps/core/src/utils/transaction/index.ts +++ b/apps/core/src/utils/transaction/index.ts @@ -13,3 +13,5 @@ export * from './createTokenTransferTransaction'; export * from './getObjectDisplayLookup'; export * from './createNftSendValidationSchema'; export * from './createUnlockTimelockedObjectsTransaction'; +export * from './checkIfIsMigrationTransaction'; +export * from './checkIfIsSupplyIncreaseVestingCollectTransaction'; diff --git a/apps/wallet-dashboard/lib/interfaces/index.ts b/apps/wallet-dashboard/lib/interfaces/index.ts index 27ca0836eb4..8d652795abd 100644 --- a/apps/wallet-dashboard/lib/interfaces/index.ts +++ b/apps/wallet-dashboard/lib/interfaces/index.ts @@ -1,7 +1,6 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -export * from './transactions.interfaces'; export * from './timelock.interfaces'; export * from './vesting.interfaces'; export * from './appRoute.interfaces'; diff --git a/apps/wallet-dashboard/lib/interfaces/transactions.interfaces.ts b/apps/wallet-dashboard/lib/interfaces/transactions.interfaces.ts deleted file mode 100644 index 4c4ea3c1962..00000000000 --- a/apps/wallet-dashboard/lib/interfaces/transactions.interfaces.ts +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { IotaTransactionBlockResponse } from '@iota/iota-sdk/client'; - -export interface ExtendedTransaction { - action: TransactionAction; - timestamp?: number; - state: TransactionState; - raw: IotaTransactionBlockResponse; -} - -export enum TransactionAction { - Send = 'Send', - Receive = 'Receive', - Transaction = 'Transaction', - Staked = 'Staked', - Unstaked = 'Unstaked', - TimelockedStaked = 'Timelocked Staked', - TimelockedUnstaked = 'Timelocked Unstaked', - Rewards = 'Rewards', - PersonalMessage = 'PersonalMessage', -} - -export enum TransactionState { - Successful = 'successful', - Failed = 'failed', - Pending = 'pending', -}