From 221f58839d558a0a956a1fd3292f6769ca272c77 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Wed, 28 Feb 2024 17:20:43 +0100 Subject: [PATCH 1/4] refactor activies --- .../core/wallet/enums/inclusion-state.enum.ts | 7 +- ...ctivity-generation-parameters.interface.ts | 8 +- .../processed-transaction.interface.ts | 112 ++++++++++++- .../types/activities/base-activity.type.ts | 151 ++++++++++++++++++ .../types/activities/nft-activity.type.ts | 63 +++++++- .../types/activities/send-activity.class.ts | 105 ++++++++++++ .../generateActivitiesFromBasicOutputs.ts | 2 +- 7 files changed, 440 insertions(+), 8 deletions(-) create mode 100644 packages/shared/lib/core/wallet/types/activities/send-activity.class.ts diff --git a/packages/shared/lib/core/wallet/enums/inclusion-state.enum.ts b/packages/shared/lib/core/wallet/enums/inclusion-state.enum.ts index c4f6e494f34..37c7f5797f7 100644 --- a/packages/shared/lib/core/wallet/enums/inclusion-state.enum.ts +++ b/packages/shared/lib/core/wallet/enums/inclusion-state.enum.ts @@ -1,6 +1,7 @@ export enum InclusionState { Pending = 'Pending', + Accepted = 'Accepted', Confirmed = 'Confirmed', - Conflicting = 'Conflicting', - UnknownPruned = 'UnknownPruned', -} + Finalized = 'Finalized', + Failed = 'Failed' +} \ No newline at end of file diff --git a/packages/shared/lib/core/wallet/interfaces/activity-generation-parameters.interface.ts b/packages/shared/lib/core/wallet/interfaces/activity-generation-parameters.interface.ts index 69382e6f98e..29ba93c540f 100644 --- a/packages/shared/lib/core/wallet/interfaces/activity-generation-parameters.interface.ts +++ b/packages/shared/lib/core/wallet/interfaces/activity-generation-parameters.interface.ts @@ -1,5 +1,5 @@ import { ActivityAction } from '../enums' -import { IProcessedTransaction } from './processed-transaction.interface' +import { IProcessedTransaction, ProcessedTransaction } from './processed-transaction.interface' import { IWrappedOutput } from './wrapped-output.interface' export interface IActivityGenerationParameters { @@ -7,3 +7,9 @@ export interface IActivityGenerationParameters { processedTransaction: IProcessedTransaction wrappedOutput: IWrappedOutput } + +export interface ActivityGenerationParameters { + action: ActivityAction + processedTransaction: ProcessedTransaction + wrappedOutput: IWrappedOutput +} diff --git a/packages/shared/lib/core/wallet/interfaces/processed-transaction.interface.ts b/packages/shared/lib/core/wallet/interfaces/processed-transaction.interface.ts index 70add6741e7..7fa9d5f3df9 100644 --- a/packages/shared/lib/core/wallet/interfaces/processed-transaction.interface.ts +++ b/packages/shared/lib/core/wallet/interfaces/processed-transaction.interface.ts @@ -1,6 +1,9 @@ import { ActivityDirection, InclusionState } from '@core/wallet/enums' import { IWrappedOutput } from './wrapped-output.interface' -import { UTXOInput } from '@iota/sdk/out/types' +import { CommonOutput, NftOutput, OutputType, UTXOInput } from '@iota/sdk/out/types' +import { getNativeTokenFromOutput, getNftId, getRecipientFromOutput, getSenderAddressFromInputs, getSenderFromTransaction, getSubjectFromAddress, isSubjectInternal } from '../utils' +import { IWalletState } from './wallet-state.interface' +import { SenderInfo } from '../types' export interface IProcessedTransaction { outputs: IWrappedOutput[] @@ -17,3 +20,110 @@ export interface IClaimData { claimedDate: Date claimingTransactionId: string } + +export class ProcessedTransaction { + constructor(public outputs: IWrappedOutput[], + public transactionId: string, + public direction: ActivityDirection, + public time: Date, + public inclusionState: InclusionState, + public utxoInputs: UTXOInput[], + public wrappedInputs: IWrappedOutput[], + public claimingData?: IClaimData) { } + + +getSendingInformation( + wallet: IWalletState, + output: CommonOutput, +): SenderInfo { + + const recipient = getRecipientFromOutput(output) + const sender = this.wrappedInputs?.length + ? getSubjectFromAddress(getSenderAddressFromInputs(wrappedInputs)) // TODO: Fix this + : getSenderFromTransaction(this.direction === ActivityDirection.Incoming, wallet.depositAddress, output) + + const subject = this.direction === ActivityDirection.Incoming ? sender : recipient + const isInternal = isSubjectInternal(subject) + + return { + subject, + isInternal, + } +} + + getBurnedNftInputs(): IWrappedOutput[] { + return this.wrappedInputs.filter((wrappedInput) => { + const input = wrappedInput.output + if (input.type === OutputType.Nft) { + const nftInput = input as NftOutput + const nftId = getNftId(nftInput.nftId, wrappedInput.outputId) + + const isIncludedInOutputs = this.outputs.some((output) => { + if (output.output.type === OutputType.Nft) { + const nftOutput = output.output as NftOutput + return getNftId(nftOutput.nftId, output.outputId) === nftId + } else { + return false + } + }) + + return !isIncludedInOutputs + } else { + return false + } + }) + } + + + getBurnedNativeTokens(): { assetId: string; amount: number } | undefined { + // If the transaction is unblanced and there is a surplus of native tokens on the + // input side of the transaction: the transaction destroys tokens. + if (this.direction !== ActivityDirection.SelfTransaction) { + return + } + + const inputNativeTokens: { [key: string]: number } = ProcessedTransaction.getAllNativeTokensFromOutputs( + this.wrappedInputs + ) + // No burned native tokens if input doesn't contain any native tokens + if (Object.keys(inputNativeTokens).length === 0) { + return + } + + const outputNativeTokens: { [key: string]: number } = ProcessedTransaction.getAllNativeTokensFromOutputs(this.outputs) + // Find missing native tokens in outputNativeTokens (ex. input native tokens count === 3, output native tokens count === 2) + // TO DO: adjust UI to account for burining entire amounts of multiple native tokens in one transaction. + // We assume here that transaction burns entire amount of only one token. + // There may be transactions created outside of FF that burn entire amount for multiple tokens from the input side + // (ex.input native tokens count === 3, output native tokens count === 0) + let burnedTokenKeys: string[] = Object.keys(inputNativeTokens).filter((key) => !(key in outputNativeTokens)) + if (Object.keys(burnedTokenKeys).length > 0) { + return { assetId: burnedTokenKeys[0], amount: inputNativeTokens[burnedTokenKeys[0]] } + } + // Check if the amount of output native token was larger on the input side (partially burned native tokens) + burnedTokenKeys = Object.keys(outputNativeTokens).filter((key) => outputNativeTokens[key] < inputNativeTokens[key]) + if (Object.keys(burnedTokenKeys).length > 0) { + const burnedAmount = inputNativeTokens[burnedTokenKeys[0]] - Number(outputNativeTokens[burnedTokenKeys[0]]) + return { assetId: burnedTokenKeys[0], amount: burnedAmount } + } + } + + + static getAllNativeTokensFromOutputs(outputs: IWrappedOutput[]): { [key: string]: number } { + const nativeTokens: { [key: string]: number } = {} + for (const output of outputs) { + if (output.output.type === OutputType.Foundry || output.output.type === OutputType.Basic) { + const commonOutput = output.output as CommonOutput + const nativeToken = getNativeTokenFromOutput(commonOutput) + if (nativeToken) { + if (!nativeTokens[nativeToken.id]) { + nativeTokens[nativeToken.id] = 0 + } + + nativeTokens[nativeToken.id] += Number(nativeToken.amount) + } + } + } + return nativeTokens + } +} \ No newline at end of file diff --git a/packages/shared/lib/core/wallet/types/activities/base-activity.type.ts b/packages/shared/lib/core/wallet/types/activities/base-activity.type.ts index 99de5e6d85e..58b9f2f60c1 100644 --- a/packages/shared/lib/core/wallet/types/activities/base-activity.type.ts +++ b/packages/shared/lib/core/wallet/types/activities/base-activity.type.ts @@ -1,6 +1,15 @@ +import { NftOutput, OutputType } from '@iota/sdk/out/types' import { ActivityAsyncStatus, ActivityDirection, InclusionState, ActivityAction } from '../../enums' +import { IProcessedTransaction, IWalletState, ProcessedTransaction } from '../../interfaces' import { Subject } from '../subject.type' import { Layer2Metadata } from '@core/layer-2' +import { isParticipationOutput } from 'shared/lib/contexts/governance' +import { getNftId, getNonRemainderBasicOutputsFromTransaction } from '../../utils' +import { ActivityNft } from './nft-activity.type' +import { addOrUpdateNftInAllWalletNfts, buildNftFromNftOutput } from 'shared/lib/core/nfts' // TODO: Fix imports +import { generateSingleBasicActivity } from '../../utils/generateActivity/generateSingleBasicActivity' +import { isConsolidation } from '../../utils/generateActivity/generateActivitiesFromBasicOutputs' +import { AcitivitySend } from './send-activity.class' export type BaseActivity = { id: string @@ -34,3 +43,145 @@ export type AsyncData = { claimingTransactionId: string claimedDate: Date } + +export enum SpecialStatus { + Unclaimed = "Unclaimed", + Claimed = "Claimed", + Expired = "Expired", + TimeLocked = "TimeLocked" +} + +export class ActivityBase { + constructor( + id: string, + inclusionState: InclusionState, + specialStatus: SpecialStatus, + time: number, + from: string[], + to: string[], + + ) { } + + /** + * Generate a group of activies given a processed transaction + * @returns + */ + static async generateActiviesFromProcessedTransaction(wallet: IWalletState, processedTransaction: ProcessedTransaction): Promise> { + let activities: Array = [] + + const { wrappedInputs, outputs, direction } = processedTransaction; + + if (wrappedInputs?.length > 0) { + + const containsFoundryActivity = outputs.some((output) => output.output.type === OutputType.Foundry) + if (containsFoundryActivity) { + // const foundryActivities = await generateActivitiesFromFoundryOutputs(processedTransaction, wallet) + // activities.push(...foundryActivities) + } + + const containsNftActivity = outputs.some((output) => output.output.type === OutputType.Nft) + if (containsNftActivity) { + // const nftActivities = await generateActivitiesFromNftOutputs(processedTransaction, wallet) + // activities.push(...nftActivities) + } + + const containsAccountActivity = + outputs.some((output) => output.output.type === OutputType.Account) && !containsFoundryActivity + if (containsAccountActivity) { + // const accountActivities = await generateActivitiesFromAccountOutputs(processedTransaction, wallet) + // activities.push(...accountActivities) + } + + const hasParticipationInputs = wrappedInputs?.some((input) => isParticipationOutput(input.output)) + const governanceOutput = hasParticipationInputs + ? outputs[0] + : outputs.find((output) => isParticipationOutput(output.output)) + if (governanceOutput) { + // const governanceActivity = await generateSingleGovernanceActivity(wallet, { + // processedTransaction, + // wrappedOutput: governanceOutput, + // action: null, + // }) + // activities.push(governanceActivity) + } + + const containsAnchorActivity = outputs.some((output) => output.output.type === OutputType.Anchor) + if (containsAnchorActivity) { + // const anchorActivities = await generateActivitiesFromAnchorOutputs(processedTransaction, wallet) + // activities.push(...anchorActivities) + } + if (!containsFoundryActivity && !containsNftActivity && !containsAccountActivity && !governanceOutput) { + + const basicOutputs = getNonRemainderBasicOutputsFromTransaction( + outputs, + wallet.depositAddress, + direction + ) + const burnedNftInputs = processedTransaction.getBurnedNftInputs(); + for (const basicOutput of basicOutputs) { + let activity: ActivityBase + + const isSelfTransaction = processedTransaction.direction === ActivityDirection.SelfTransaction + const burnedNftInputIndex = burnedNftInputs.findIndex( + (input) => input.output.amount === basicOutput.output.amount + ) + const burnedNativeToken = burnedNftInputIndex < 0 ? processedTransaction.getBurnedNativeTokens() : undefined + + // NFT Activity + if (isSelfTransaction && burnedNftInputIndex >= 0) { + const wrappedInput = burnedNftInputs[burnedNftInputIndex] + const nftInput = wrappedInput.output as NftOutput + + activity = await ActivityNft.fromProcessedTransaction( + wallet, + { + action: ActivityAction.Burn, + processedTransaction, + wrappedOutput: basicOutput, + }, + getNftId(nftInput.nftId, wrappedInput.outputId) + ) + const nft = buildNftFromNftOutput(wrappedInput, wallet.depositAddress, false) + addOrUpdateNftInAllWalletNfts(wallet.id, nft) + + burnedNftInputs.splice(burnedNftInputIndex, 1) + } + // Burn Activity + // else if (isSelfTransaction && burnedNativeToken) { + // activity = await generateSingleBasicActivity( + // wallet, + // { + // action: ActivityAction.Burn, + // processedTransaction, + // wrappedOutput: basicOutput, + // }, + // burnedNativeToken.assetId, + // burnedNativeToken.amount + // ) + // } + // Consolidation Activity + // else if (isSelfTransaction && isConsolidation(basicOutput, processedTransaction)) { + // activity = await generateSingleConsolidationActivity(wallet, { + // action: ActivityAction.Send, + // processedTransaction, + // wrappedOutput: basicOutput, + // }) + // } + // Send Activity + else { + activity = await AcitivitySend.fromProcessedTransaction(wallet, { + action: ActivityAction.Send, + processedTransaction, + wrappedOutput: basicOutput, + }) + } + activities.push(activity) + } + } else { + + } + + } + return activities + } +} \ No newline at end of file diff --git a/packages/shared/lib/core/wallet/types/activities/nft-activity.type.ts b/packages/shared/lib/core/wallet/types/activities/nft-activity.type.ts index d336ccd157c..2994a2f9932 100644 --- a/packages/shared/lib/core/wallet/types/activities/nft-activity.type.ts +++ b/packages/shared/lib/core/wallet/types/activities/nft-activity.type.ts @@ -1,7 +1,66 @@ -import { ActivityType } from '@core/wallet' -import { BaseActivity } from './base-activity.type' +import { ActivityAction, ActivityGenerationParameters, ActivityType, IWalletState, InclusionState, getAsyncDataFromOutput, getClient, getLayer2ActivityInformation, getMetadataFromOutput, getNftId, getStorageDepositFromOutput, getTagFromOutput } from '@core/wallet' +import { ActivityBase, BaseActivity, SpecialStatus } from './base-activity.type' +import { NftOutput } from '@iota/sdk/out/types' +import { handleError } from 'shared/lib/core/error/handlers' export type NftActivity = BaseActivity & { type: ActivityType.Nft nftId: string } + +export class ActivityNft extends ActivityBase { + constructor(private nftId: string, id: string, + inclusionState: InclusionState, + specialStatus: SpecialStatus, + time: number, + from: string[], + to: string[],) { + super(id, inclusionState, specialStatus, time, from, to) + } + + static async fromProcessedTransaction(wallet: IWalletState, + { action, processedTransaction, wrappedOutput }: ActivityGenerationParameters, + nftIdFromInput?: string): Promise { + const { claimingData, time, inclusionState, transactionId, direction } = processedTransaction + const outputId = wrappedOutput.outputId + const output = wrappedOutput.output as NftOutput + const id = outputId || transactionId + + const isHidden = false + const isAssetHidden = false + const containsValue = true + const specialStatus = SpecialStatus.Unclaimed // TODO: Fix this + + const nftId = nftIdFromInput ? nftIdFromInput : getNftId(output.nftId, outputId) + const metadata = getMetadataFromOutput(output) + const tag = getTagFromOutput(output) + + const sendingInfo = processedTransaction.getSendingInformation(wallet, output) + const { subject, isInternal } = sendingInfo + + const { parsedLayer2Metadata, destinationNetwork } = getLayer2ActivityInformation(metadata, sendingInfo) + const gasBudget = Number(parsedLayer2Metadata?.gasBudget ?? '0') + + const storageDepositData = await getStorageDepositFromOutput(output) + const { storageDeposit } = storageDepositData + let { giftedStorageDeposit } = storageDepositData + giftedStorageDeposit = action === ActivityAction.Burn ? 0 : giftedStorageDeposit + giftedStorageDeposit = gasBudget === 0 ? giftedStorageDeposit : 0 + + let surplus: number | undefined = undefined + try { + const client = await getClient() + const minimumRequiredStorageDeposit = await client.computeMinimumOutputAmount(output) + surplus = Number(output.amount) - Number(minimumRequiredStorageDeposit) + if (surplus && !storageDeposit) { + giftedStorageDeposit = Number(minimumRequiredStorageDeposit) + } + } catch (err) { + handleError(err) + } + + const asyncData = await getAsyncDataFromOutput(output, outputId, claimingData, wallet) + + return new ActivityNft(nftId, id, inclusionState, specialStatus, time, from, to) + } +} \ No newline at end of file diff --git a/packages/shared/lib/core/wallet/types/activities/send-activity.class.ts b/packages/shared/lib/core/wallet/types/activities/send-activity.class.ts new file mode 100644 index 00000000000..96792ac734b --- /dev/null +++ b/packages/shared/lib/core/wallet/types/activities/send-activity.class.ts @@ -0,0 +1,105 @@ +import { BasicOutput } from "@iota/sdk/out/types"; +import { ActivityAction, InclusionState } from "../../enums"; +import { IActivityGenerationParameters, IWalletState, ProcessedTransaction } from "../../interfaces"; +import { activityOutputContainsValue, getAmountFromOutput, getAsyncDataFromOutput, getLayer2ActivityInformation, getMetadataFromOutput, getNativeTokenFromOutput, getSendingInformation, getStorageDepositFromOutput, getTagFromOutput } from "../../utils"; +import { ActivityBase, SpecialStatus } from "./base-activity.type"; +import { isShimmerClaimingTransaction } from "shared/lib/contexts/onboarding"; +import { activeProfileId, getCoinType } from "shared/lib/core/profile"; +import { get } from "svelte/store"; + +export class AcitivitySend extends ActivityBase { + constructor(id: string, + inclusionState: InclusionState, + specialStatus: SpecialStatus, + time: number, + from: string[], + to: string[],) { + super(id, inclusionState, specialStatus, time, from, to) + } + + static async fromProcessedTransaction(wallet: IWalletState, + { action, processedTransaction, wrappedOutput }: IActivityGenerationParameters, + fallbackAssetId?: string, + fallbackAmount?: number): Promise { + const { transactionId, direction, claimingData, time, inclusionState } = processedTransaction + + const isHidden = false + const isAssetHidden = false + const containsValue = await activityOutputContainsValue(wallet, wrappedOutput) + + const outputId = wrappedOutput.outputId + const id = outputId || transactionId + + const output = wrappedOutput.output as BasicOutput + const amount = getAmountFromOutput(output) + + const isShimmerClaiming = isShimmerClaimingTransaction(transactionId, get(activeProfileId)) + + const tag = getTagFromOutput(output) + const metadata = getMetadataFromOutput(output) + const publicNote = '' + + const sendingInfo = getSendingInformation(processedTransaction, output, wallet) + const asyncData = await getAsyncDataFromOutput(output, outputId, claimingData, wallet) + + const { parsedLayer2Metadata, destinationNetwork } = getLayer2ActivityInformation(metadata, sendingInfo) + const layer2Allowance = Number(parsedLayer2Metadata?.baseTokens ?? '0') + const gasBudget = Number(parsedLayer2Metadata?.gasBudget ?? '0') + const gasFee = layer2Allowance > 0 ? amount - layer2Allowance : 0 + + let { storageDeposit, giftedStorageDeposit } = await getStorageDepositFromOutput(output) + giftedStorageDeposit = action === ActivityAction.Burn ? 0 : giftedStorageDeposit + giftedStorageDeposit = gasBudget === 0 ? giftedStorageDeposit : 0 + + const baseTokenAmount = amount - storageDeposit - gasFee + + const nativeToken = getNativeTokenFromOutput(output) + const assetId = fallbackAssetId ?? nativeToken?.id ?? getCoinType() + + let surplus: number | undefined = undefined + if (nativeToken) { + const storageDepositToDeduct = (storageDeposit > 0 ? storageDeposit : giftedStorageDeposit) ?? 0 + surplus = Number(output.amount) - storageDepositToDeduct + } + + let rawAmount: number + if (fallbackAmount === undefined) { + rawAmount = nativeToken ? Number(nativeToken?.amount) : baseTokenAmount + } else { + rawAmount = fallbackAmount + } + + // Note: we update the displayed storage deposit so it matches what was displayed in the send confirmation flow + // set the storage deposit to zero if the amount is greater than the storage deposit + // to improve the UX so the user doesnt think they need to pay the storage deposit + if (!nativeToken && !storageDeposit && rawAmount >= giftedStorageDeposit) { + storageDeposit = giftedStorageDeposit = 0 + } + + return { + //type: ActivityType.Basic, + isHidden, + id, + transactionId, + time, + direction, + action, + isAssetHidden, + inclusionState, + containsValue, + outputId, + storageDeposit, + giftedStorageDeposit, + surplus, + rawAmount, + isShimmerClaiming, + publicNote, + metadata, + tag, + assetId, + asyncData, + destinationNetwork, + parsedLayer2Metadata, + + } + } \ No newline at end of file diff --git a/packages/shared/lib/core/wallet/utils/generateActivity/generateActivitiesFromBasicOutputs.ts b/packages/shared/lib/core/wallet/utils/generateActivity/generateActivitiesFromBasicOutputs.ts index d08cd5a7067..a32e4f0e2c5 100644 --- a/packages/shared/lib/core/wallet/utils/generateActivity/generateActivitiesFromBasicOutputs.ts +++ b/packages/shared/lib/core/wallet/utils/generateActivity/generateActivitiesFromBasicOutputs.ts @@ -157,7 +157,7 @@ function getAllNativeTokensFromOutputs(outputs: IWrappedOutput[]): { [key: strin return nativeTokens } -function isConsolidation(output: IWrappedOutput, processedTransaction: IProcessedTransaction): boolean { +export function isConsolidation(output: IWrappedOutput, processedTransaction: IProcessedTransaction): boolean { const allBasicInputs = processedTransaction.wrappedInputs.every((input) => input.output.type === OutputType.Basic) const isSelfTransaction = processedTransaction.direction === ActivityDirection.SelfTransaction const isSameAmount = From 0ae1520d2504f073aa7b35a5ce72f080aa18637d Mon Sep 17 00:00:00 2001 From: marc2332 Date: Wed, 6 Mar 2024 10:23:19 +0100 Subject: [PATCH 2/4] more wip --- .../core/wallet/enums/inclusion-state.enum.ts | 1 + .../processed-transaction.interface.ts | 4 +- .../types/activities/base-activity.type.ts | 48 ++++++------ ...ivity.class.ts => basic-activity.class.ts} | 73 ++++++++++--------- .../lib/core/wallet/types/activities/index.ts | 2 + .../types/activities/nft-activity.type.ts | 25 ++++--- 6 files changed, 82 insertions(+), 71 deletions(-) rename packages/shared/lib/core/wallet/types/activities/{send-activity.class.ts => basic-activity.class.ts} (77%) diff --git a/packages/shared/lib/core/wallet/enums/inclusion-state.enum.ts b/packages/shared/lib/core/wallet/enums/inclusion-state.enum.ts index 37c7f5797f7..84cc703e6d6 100644 --- a/packages/shared/lib/core/wallet/enums/inclusion-state.enum.ts +++ b/packages/shared/lib/core/wallet/enums/inclusion-state.enum.ts @@ -1,3 +1,4 @@ +// TODO: Remove this and use the SDK version. export enum InclusionState { Pending = 'Pending', Accepted = 'Accepted', diff --git a/packages/shared/lib/core/wallet/interfaces/processed-transaction.interface.ts b/packages/shared/lib/core/wallet/interfaces/processed-transaction.interface.ts index 7fa9d5f3df9..8187ef665ee 100644 --- a/packages/shared/lib/core/wallet/interfaces/processed-transaction.interface.ts +++ b/packages/shared/lib/core/wallet/interfaces/processed-transaction.interface.ts @@ -1,6 +1,6 @@ -import { ActivityDirection, InclusionState } from '@core/wallet/enums' +import { ActivityDirection } from '@core/wallet/enums' import { IWrappedOutput } from './wrapped-output.interface' -import { CommonOutput, NftOutput, OutputType, UTXOInput } from '@iota/sdk/out/types' +import { CommonOutput, NftOutput, OutputType, UTXOInput, InclusionState } from '@iota/sdk/out/types' import { getNativeTokenFromOutput, getNftId, getRecipientFromOutput, getSenderAddressFromInputs, getSenderFromTransaction, getSubjectFromAddress, isSubjectInternal } from '../utils' import { IWalletState } from './wallet-state.interface' import { SenderInfo } from '../types' diff --git a/packages/shared/lib/core/wallet/types/activities/base-activity.type.ts b/packages/shared/lib/core/wallet/types/activities/base-activity.type.ts index 58b9f2f60c1..b031ecce76c 100644 --- a/packages/shared/lib/core/wallet/types/activities/base-activity.type.ts +++ b/packages/shared/lib/core/wallet/types/activities/base-activity.type.ts @@ -1,5 +1,5 @@ -import { NftOutput, OutputType } from '@iota/sdk/out/types' -import { ActivityAsyncStatus, ActivityDirection, InclusionState, ActivityAction } from '../../enums' +import { NftOutput, OutputType, InclusionState } from '@iota/sdk/out/types' +import { ActivityAsyncStatus, ActivityDirection, ActivityAction } from '../../enums' import { IProcessedTransaction, IWalletState, ProcessedTransaction } from '../../interfaces' import { Subject } from '../subject.type' import { Layer2Metadata } from '@core/layer-2' @@ -9,7 +9,7 @@ import { ActivityNft } from './nft-activity.type' import { addOrUpdateNftInAllWalletNfts, buildNftFromNftOutput } from 'shared/lib/core/nfts' // TODO: Fix imports import { generateSingleBasicActivity } from '../../utils/generateActivity/generateSingleBasicActivity' import { isConsolidation } from '../../utils/generateActivity/generateActivitiesFromBasicOutputs' -import { AcitivitySend } from './send-activity.class' +import { ActivityBasic } from './basic-activity.class' export type BaseActivity = { id: string @@ -51,22 +51,23 @@ export enum SpecialStatus { TimeLocked = "TimeLocked" } -export class ActivityBase { - constructor( - id: string, - inclusionState: InclusionState, - specialStatus: SpecialStatus, - time: number, - from: string[], - to: string[], +export interface ActivityBaseOptions { + id: string, + inclusionState: InclusionState, + specialStatus: SpecialStatus, + time: Date, // Should this be number, slot index? + from: string[], + to: string[], +} - ) { } +export class ActivityBase { + constructor(options: ActivityBaseOptions) { } /** * Generate a group of activies given a processed transaction * @returns */ - static async generateActiviesFromProcessedTransaction(wallet: IWalletState, processedTransaction: ProcessedTransaction): Promise> { + static async generateActivitiesFromProcessedTransaction(wallet: IWalletState, processedTransaction: ProcessedTransaction): Promise> { let activities: Array = [] const { wrappedInputs, outputs, direction } = processedTransaction; @@ -147,18 +148,13 @@ export class ActivityBase { burnedNftInputs.splice(burnedNftInputIndex, 1) } // Burn Activity - // else if (isSelfTransaction && burnedNativeToken) { - // activity = await generateSingleBasicActivity( - // wallet, - // { - // action: ActivityAction.Burn, - // processedTransaction, - // wrappedOutput: basicOutput, - // }, - // burnedNativeToken.assetId, - // burnedNativeToken.amount - // ) - // } + else if (isSelfTransaction && burnedNativeToken) { + activity = await ActivityBasic.fromProcessedTransaction(wallet, { + action: ActivityAction.Burn, + processedTransaction, + wrappedOutput: basicOutput, + }) + } // Consolidation Activity // else if (isSelfTransaction && isConsolidation(basicOutput, processedTransaction)) { // activity = await generateSingleConsolidationActivity(wallet, { @@ -169,7 +165,7 @@ export class ActivityBase { // } // Send Activity else { - activity = await AcitivitySend.fromProcessedTransaction(wallet, { + activity = await ActivityBasic.fromProcessedTransaction(wallet, { action: ActivityAction.Send, processedTransaction, wrappedOutput: basicOutput, diff --git a/packages/shared/lib/core/wallet/types/activities/send-activity.class.ts b/packages/shared/lib/core/wallet/types/activities/basic-activity.class.ts similarity index 77% rename from packages/shared/lib/core/wallet/types/activities/send-activity.class.ts rename to packages/shared/lib/core/wallet/types/activities/basic-activity.class.ts index 96792ac734b..21541d08197 100644 --- a/packages/shared/lib/core/wallet/types/activities/send-activity.class.ts +++ b/packages/shared/lib/core/wallet/types/activities/basic-activity.class.ts @@ -2,25 +2,24 @@ import { BasicOutput } from "@iota/sdk/out/types"; import { ActivityAction, InclusionState } from "../../enums"; import { IActivityGenerationParameters, IWalletState, ProcessedTransaction } from "../../interfaces"; import { activityOutputContainsValue, getAmountFromOutput, getAsyncDataFromOutput, getLayer2ActivityInformation, getMetadataFromOutput, getNativeTokenFromOutput, getSendingInformation, getStorageDepositFromOutput, getTagFromOutput } from "../../utils"; -import { ActivityBase, SpecialStatus } from "./base-activity.type"; +import { ActivityBase, ActivityBaseOptions, SpecialStatus } from "./base-activity.type"; import { isShimmerClaimingTransaction } from "shared/lib/contexts/onboarding"; import { activeProfileId, getCoinType } from "shared/lib/core/profile"; import { get } from "svelte/store"; -export class AcitivitySend extends ActivityBase { - constructor(id: string, - inclusionState: InclusionState, - specialStatus: SpecialStatus, - time: number, - from: string[], - to: string[],) { - super(id, inclusionState, specialStatus, time, from, to) +interface ActivityBasicOptions extends ActivityBaseOptions { + +} + +export class ActivityBasic extends ActivityBase { + constructor(options: ActivityBasicOptions) { + super(options) } static async fromProcessedTransaction(wallet: IWalletState, { action, processedTransaction, wrappedOutput }: IActivityGenerationParameters, fallbackAssetId?: string, - fallbackAmount?: number): Promise { + fallbackAmount?: number): Promise { const { transactionId, direction, claimingData, time, inclusionState } = processedTransaction const isHidden = false @@ -76,30 +75,36 @@ export class AcitivitySend extends ActivityBase { storageDeposit = giftedStorageDeposit = 0 } - return { + return new ActivityBasic({ //type: ActivityType.Basic, - isHidden, + //isHidden, id, - transactionId, - time, - direction, - action, - isAssetHidden, inclusionState, - containsValue, - outputId, - storageDeposit, - giftedStorageDeposit, - surplus, - rawAmount, - isShimmerClaiming, - publicNote, - metadata, - tag, - assetId, - asyncData, - destinationNetwork, - parsedLayer2Metadata, - - } - } \ No newline at end of file + specialStatus, + time, + to, + from, + //transactionId, + // time, + // direction, + // action, + // isAssetHidden, + // inclusionState, + // containsValue, + // outputId, + // storageDeposit, + // giftedStorageDeposit, + // surplus, + // rawAmount, + // isShimmerClaiming, + // publicNote, + // metadata, + // tag, + // assetId, + // asyncData, + // destinationNetwork, + // parsedLayer2Metadata, + + }) + } +} \ No newline at end of file diff --git a/packages/shared/lib/core/wallet/types/activities/index.ts b/packages/shared/lib/core/wallet/types/activities/index.ts index befe1e74963..8334c661bab 100644 --- a/packages/shared/lib/core/wallet/types/activities/index.ts +++ b/packages/shared/lib/core/wallet/types/activities/index.ts @@ -7,3 +7,5 @@ export * from './governance-activity.type' export * from './nft-activity.type' export * from './transaction-activity.type' export * from './vesting-activity.type' + +export * from './base-activity.type' \ No newline at end of file diff --git a/packages/shared/lib/core/wallet/types/activities/nft-activity.type.ts b/packages/shared/lib/core/wallet/types/activities/nft-activity.type.ts index 2994a2f9932..321897e3938 100644 --- a/packages/shared/lib/core/wallet/types/activities/nft-activity.type.ts +++ b/packages/shared/lib/core/wallet/types/activities/nft-activity.type.ts @@ -1,5 +1,5 @@ import { ActivityAction, ActivityGenerationParameters, ActivityType, IWalletState, InclusionState, getAsyncDataFromOutput, getClient, getLayer2ActivityInformation, getMetadataFromOutput, getNftId, getStorageDepositFromOutput, getTagFromOutput } from '@core/wallet' -import { ActivityBase, BaseActivity, SpecialStatus } from './base-activity.type' +import { ActivityBase, ActivityBaseOptions, BaseActivity, SpecialStatus } from './base-activity.type' import { NftOutput } from '@iota/sdk/out/types' import { handleError } from 'shared/lib/core/error/handlers' @@ -8,14 +8,13 @@ export type NftActivity = BaseActivity & { nftId: string } +interface ActivityNftOptions extends ActivityBaseOptions { + nftId: string +} + export class ActivityNft extends ActivityBase { - constructor(private nftId: string, id: string, - inclusionState: InclusionState, - specialStatus: SpecialStatus, - time: number, - from: string[], - to: string[],) { - super(id, inclusionState, specialStatus, time, from, to) + constructor(options: ActivityNftOptions) { + super(options) } static async fromProcessedTransaction(wallet: IWalletState, @@ -61,6 +60,14 @@ export class ActivityNft extends ActivityBase { const asyncData = await getAsyncDataFromOutput(output, outputId, claimingData, wallet) - return new ActivityNft(nftId, id, inclusionState, specialStatus, time, from, to) + return new ActivityNft({ + nftId, + id, + inclusionState, + specialStatus, + time, + from, + to + }) } } \ No newline at end of file From 5b76a5b29b1d4ab9c351ff6bb885397198e6df6d Mon Sep 17 00:00:00 2001 From: marc2332 Date: Thu, 7 Mar 2024 09:02:29 +0100 Subject: [PATCH 3/4] even more wip --- .../types/activities/account-activity.type.ts | 51 +++- .../types/activities/base-activity.type.ts | 263 +++++++++++------- .../types/activities/basic-activity.class.ts | 9 +- .../activities/consolidation-activity.type.ts | 48 +++- .../helper/getActivityTypeFromOutput.ts | 4 +- .../activityOutputContainsValue.ts | 2 +- 6 files changed, 265 insertions(+), 112 deletions(-) diff --git a/packages/shared/lib/core/wallet/types/activities/account-activity.type.ts b/packages/shared/lib/core/wallet/types/activities/account-activity.type.ts index 410ea7842a7..41871230d1d 100644 --- a/packages/shared/lib/core/wallet/types/activities/account-activity.type.ts +++ b/packages/shared/lib/core/wallet/types/activities/account-activity.type.ts @@ -1,8 +1,57 @@ import { ActivityType } from '@core/wallet/enums' -import { BaseActivity } from './base-activity.type' +import { ActivityBase, ActivityBaseOptions, BaseActivity, SpecialStatus } from './base-activity.type' +import { IActivityGenerationParameters, IWalletState } from '../../interfaces' export type AccountActivity = BaseActivity & { type: ActivityType.Account accountAddress: string accountId: string } + + +interface ActivityAccountOptions extends ActivityBaseOptions { + accountAddress: string + accountId: string +} + +export class ActivityAccount extends ActivityBase { + constructor(options: ActivityAccountOptions) { + super(options) + } + + static async fromProcessedTransaction(wallet: IWalletState, + { action, processedTransaction, wrappedOutput }: IActivityGenerationParameters + ) { + const { transactionId, claimingData, direction, time, inclusionState } = processedTransaction + + const specialStatus = SpecialStatus.Unclaimed // TODO: Fix this + const output = wrappedOutput.output as AccountOutput + const outputId = wrappedOutput.outputId + const id = outputId || transactionId + + const { storageDeposit: _storageDeposit, giftedStorageDeposit } = await getStorageDepositFromOutput(output) + const storageDeposit = getAmountFromOutput(output) + _storageDeposit + const accountId = getAccountId(output, outputId) + const accountAddress = api.accountIdToBech32(accountId, getNetworkHrp()) + + const isHidden = false + const isAssetHidden = false + const containsValue = true + + const metadata = getMetadataFromOutput(output) + const tag = getTagFromOutput(output) + const asyncData = await getAsyncDataFromOutput(output, outputId, claimingData, wallet) + const sendingInfo = getSendingInformation(processedTransaction, output, wallet) + + return new ActivityAccount({ + id, + inclusionState, + specialStatus, + accountAddress, + accountId, + time, + from, + to + }) + } +} diff --git a/packages/shared/lib/core/wallet/types/activities/base-activity.type.ts b/packages/shared/lib/core/wallet/types/activities/base-activity.type.ts index b031ecce76c..c783ef690c7 100644 --- a/packages/shared/lib/core/wallet/types/activities/base-activity.type.ts +++ b/packages/shared/lib/core/wallet/types/activities/base-activity.type.ts @@ -1,15 +1,15 @@ import { NftOutput, OutputType, InclusionState } from '@iota/sdk/out/types' -import { ActivityAsyncStatus, ActivityDirection, ActivityAction } from '../../enums' -import { IProcessedTransaction, IWalletState, ProcessedTransaction } from '../../interfaces' +import { ActivityAsyncStatus, ActivityDirection, ActivityAction, ActivityType } from '../../enums' +import { IProcessedTransaction, IWalletState, IWrappedOutput, ProcessedTransaction } from '../../interfaces' import { Subject } from '../subject.type' import { Layer2Metadata } from '@core/layer-2' -import { isParticipationOutput } from 'shared/lib/contexts/governance' -import { getNftId, getNonRemainderBasicOutputsFromTransaction } from '../../utils' +import { isParticipationOutput } from '@contexts/governance' +import { getActivityTypeFromOutput, getNftId, getNonRemainderBasicOutputsFromTransaction } from '../../utils' import { ActivityNft } from './nft-activity.type' -import { addOrUpdateNftInAllWalletNfts, buildNftFromNftOutput } from 'shared/lib/core/nfts' // TODO: Fix imports -import { generateSingleBasicActivity } from '../../utils/generateActivity/generateSingleBasicActivity' -import { isConsolidation } from '../../utils/generateActivity/generateActivitiesFromBasicOutputs' +import { addOrUpdateNftInAllWalletNfts, buildNftFromNftOutput } from '@core/nfts' // TODO: Fix imports import { ActivityBasic } from './basic-activity.class' +import { ActivityConsolidation } from './consolidation-activity.type' +import { ActivityAccount } from './account-activity.type' export type BaseActivity = { id: string @@ -44,6 +44,7 @@ export type AsyncData = { claimedDate: Date } +// TODO: Move somewhere else. export enum SpecialStatus { Unclaimed = "Unclaimed", Claimed = "Claimed", @@ -63,121 +64,177 @@ export interface ActivityBaseOptions { export class ActivityBase { constructor(options: ActivityBaseOptions) { } + id(){ + return this.id + } + + inclusionState(){ + return this.inclusionState + } + /** * Generate a group of activies given a processed transaction - * @returns + * @returns ActivityBase[] */ - static async generateActivitiesFromProcessedTransaction(wallet: IWalletState, processedTransaction: ProcessedTransaction): Promise> { + static generateActivitiesFromProcessedTransaction(wallet: IWalletState, processedTransaction: ProcessedTransaction): Promise> { + if (processedTransaction.wrappedInputs?.length > 0) { + return this.generateActivitiesFromProcessedTransactionsWithInputs(wallet, processedTransaction) + } else { + return this.generateActivitiesFromProcessedTransactionsWithoutInputs(wallet, processedTransaction) + } + } + + static async generateActivitiesFromProcessedTransactionsWithInputs(wallet: IWalletState, processedTransaction: ProcessedTransaction): Promise> { let activities: Array = [] const { wrappedInputs, outputs, direction } = processedTransaction; - if (wrappedInputs?.length > 0) { - - const containsFoundryActivity = outputs.some((output) => output.output.type === OutputType.Foundry) - if (containsFoundryActivity) { - // const foundryActivities = await generateActivitiesFromFoundryOutputs(processedTransaction, wallet) - // activities.push(...foundryActivities) - } - - const containsNftActivity = outputs.some((output) => output.output.type === OutputType.Nft) - if (containsNftActivity) { - // const nftActivities = await generateActivitiesFromNftOutputs(processedTransaction, wallet) - // activities.push(...nftActivities) - } + const containsFoundryActivity = outputs.some((output) => output.output.type === OutputType.Foundry) + if (containsFoundryActivity) { + // const foundryActivities = await generateActivitiesFromFoundryOutputs(processedTransaction, wallet) + // activities.push(...foundryActivities) + } - const containsAccountActivity = - outputs.some((output) => output.output.type === OutputType.Account) && !containsFoundryActivity - if (containsAccountActivity) { - // const accountActivities = await generateActivitiesFromAccountOutputs(processedTransaction, wallet) - // activities.push(...accountActivities) - } + const containsNftActivity = outputs.some((output) => output.output.type === OutputType.Nft) + if (containsNftActivity) { + // const nftActivities = await generateActivitiesFromNftOutputs(processedTransaction, wallet) + // activities.push(...nftActivities) + } - const hasParticipationInputs = wrappedInputs?.some((input) => isParticipationOutput(input.output)) - const governanceOutput = hasParticipationInputs - ? outputs[0] - : outputs.find((output) => isParticipationOutput(output.output)) - if (governanceOutput) { - // const governanceActivity = await generateSingleGovernanceActivity(wallet, { - // processedTransaction, - // wrappedOutput: governanceOutput, - // action: null, - // }) - // activities.push(governanceActivity) - } + const containsAccountActivity = + outputs.some((output) => output.output.type === OutputType.Account) && !containsFoundryActivity + if (containsAccountActivity) { + // const accountActivities = await generateActivitiesFromAccountOutputs(processedTransaction, wallet) + // activities.push(...accountActivities) + } - const containsAnchorActivity = outputs.some((output) => output.output.type === OutputType.Anchor) - if (containsAnchorActivity) { - // const anchorActivities = await generateActivitiesFromAnchorOutputs(processedTransaction, wallet) - // activities.push(...anchorActivities) - } - if (!containsFoundryActivity && !containsNftActivity && !containsAccountActivity && !governanceOutput) { + const hasParticipationInputs = wrappedInputs?.some((input) => isParticipationOutput(input.output)) + const governanceOutput = hasParticipationInputs + ? outputs[0] + : outputs.find((output) => isParticipationOutput(output.output)) + if (governanceOutput) { + // const governanceActivity = await generateSingleGovernanceActivity(wallet, { + // processedTransaction, + // wrappedOutput: governanceOutput, + // action: null, + // }) + // activities.push(governanceActivity) + } - const basicOutputs = getNonRemainderBasicOutputsFromTransaction( - outputs, - wallet.depositAddress, - direction + const containsAnchorActivity = outputs.some((output) => output.output.type === OutputType.Anchor) + if (containsAnchorActivity) { + // const anchorActivities = await generateActivitiesFromAnchorOutputs(processedTransaction, wallet) + // activities.push(...anchorActivities) + } + if (!containsFoundryActivity && !containsNftActivity && !containsAccountActivity && !governanceOutput) { + + const basicOutputs = getNonRemainderBasicOutputsFromTransaction( + outputs, + wallet.depositAddress, + direction + ) + const burnedNftInputs = processedTransaction.getBurnedNftInputs(); + for (const basicOutput of basicOutputs) { + let activity: ActivityBase + + const isSelfTransaction = processedTransaction.direction === ActivityDirection.SelfTransaction + const burnedNftInputIndex = burnedNftInputs.findIndex( + (input) => input.output.amount === basicOutput.output.amount ) - const burnedNftInputs = processedTransaction.getBurnedNftInputs(); - for (const basicOutput of basicOutputs) { - let activity: ActivityBase - - const isSelfTransaction = processedTransaction.direction === ActivityDirection.SelfTransaction - const burnedNftInputIndex = burnedNftInputs.findIndex( - (input) => input.output.amount === basicOutput.output.amount - ) - const burnedNativeToken = burnedNftInputIndex < 0 ? processedTransaction.getBurnedNativeTokens() : undefined - - // NFT Activity - if (isSelfTransaction && burnedNftInputIndex >= 0) { - const wrappedInput = burnedNftInputs[burnedNftInputIndex] - const nftInput = wrappedInput.output as NftOutput - - activity = await ActivityNft.fromProcessedTransaction( - wallet, - { - action: ActivityAction.Burn, - processedTransaction, - wrappedOutput: basicOutput, - }, - getNftId(nftInput.nftId, wrappedInput.outputId) - ) - const nft = buildNftFromNftOutput(wrappedInput, wallet.depositAddress, false) - addOrUpdateNftInAllWalletNfts(wallet.id, nft) - - burnedNftInputs.splice(burnedNftInputIndex, 1) - } - // Burn Activity - else if (isSelfTransaction && burnedNativeToken) { - activity = await ActivityBasic.fromProcessedTransaction(wallet, { + const burnedNativeToken = burnedNftInputIndex < 0 ? processedTransaction.getBurnedNativeTokens() : undefined + + // NFT Activity + if (isSelfTransaction && burnedNftInputIndex >= 0) { + const wrappedInput = burnedNftInputs[burnedNftInputIndex] + const nftInput = wrappedInput.output as NftOutput + + activity = await ActivityNft.fromProcessedTransaction( + wallet, + { action: ActivityAction.Burn, processedTransaction, wrappedOutput: basicOutput, - }) - } - // Consolidation Activity - // else if (isSelfTransaction && isConsolidation(basicOutput, processedTransaction)) { - // activity = await generateSingleConsolidationActivity(wallet, { - // action: ActivityAction.Send, - // processedTransaction, - // wrappedOutput: basicOutput, - // }) - // } - // Send Activity - else { - activity = await ActivityBasic.fromProcessedTransaction(wallet, { - action: ActivityAction.Send, - processedTransaction, - wrappedOutput: basicOutput, - }) - } - activities.push(activity) - } - } else { + }, + getNftId(nftInput.nftId, wrappedInput.outputId) + ) + const nft = buildNftFromNftOutput(wrappedInput, wallet.depositAddress, false) + addOrUpdateNftInAllWalletNfts(wallet.id, nft) + burnedNftInputs.splice(burnedNftInputIndex, 1) + } + // Burn Activity + else if (isSelfTransaction && burnedNativeToken) { + activity = await ActivityBasic.fromProcessedTransaction(wallet, { + action: ActivityAction.Burn, + processedTransaction, + wrappedOutput: basicOutput, + }) + } + // Consolidation Activity + else if (isSelfTransaction && ActivityBase.isConsolidation(basicOutput, processedTransaction)) { + activity = await ActivityConsolidation.fromProcessedTransaction(wallet, { + action: ActivityAction.Send, + processedTransaction, + wrappedOutput: basicOutput, + }) + } + // Send Activity + else { + activity = await ActivityBasic.fromProcessedTransaction(wallet, { + action: ActivityAction.Send, + processedTransaction, + wrappedOutput: basicOutput, + }) + } + activities.push(activity) } + } else { } return activities } + + static async generateActivitiesFromProcessedTransactionsWithoutInputs(wallet: IWalletState, processedTransaction: ProcessedTransaction): Promise> { + const nonRemainderOutputs = processedTransaction.outputs.filter((wrappedOutput) => !wrappedOutput.remainder) + const activities = await Promise.all( + nonRemainderOutputs.map(async (wrappedOutput) => { + const params = { + type: getActivityTypeFromOutput(wrappedOutput), + action: ActivityAction.Unknown, + processedTransaction, + wrappedOutput, + } + switch (params.type) { + case ActivityType.Basic: + return ActivityBasic.fromProcessedTransaction(wallet, params) + case ActivityType.Governance: + // return generateSingleGovernanceActivity(wallet, params) + case ActivityType.Foundry: + // return generateSingleFoundryActivity(wallet, params) + case ActivityType.Account: + return ActivityAccount.fromProcessedTransaction(wallet, params) + case ActivityType.Nft: + return ActivityNft.fromProcessedTransaction(wallet, params) + case ActivityType.Vesting: + // return generateVestingActivity(wallet, params) + case ActivityType.Anchor: + // return generateSingleAnchorActivity(wallet, params) + default: + throw new Error(`Unknown activity type: ${params.type}`) + } + }) + ) + return activities + } + + static isConsolidation(output: IWrappedOutput, processedTransaction: IProcessedTransaction): boolean { + const allBasicInputs = processedTransaction.wrappedInputs.every((input) => input.output.type === OutputType.Basic) + const isSelfTransaction = processedTransaction.direction === ActivityDirection.SelfTransaction + const isSameAmount = + processedTransaction.wrappedInputs.reduce((sum, input) => sum + Number(input.output.amount), 0) === + Number(output.output.amount) + + return allBasicInputs && isSelfTransaction && isSameAmount + + } } \ No newline at end of file diff --git a/packages/shared/lib/core/wallet/types/activities/basic-activity.class.ts b/packages/shared/lib/core/wallet/types/activities/basic-activity.class.ts index 21541d08197..7e7aaba6523 100644 --- a/packages/shared/lib/core/wallet/types/activities/basic-activity.class.ts +++ b/packages/shared/lib/core/wallet/types/activities/basic-activity.class.ts @@ -1,10 +1,10 @@ import { BasicOutput } from "@iota/sdk/out/types"; -import { ActivityAction, InclusionState } from "../../enums"; -import { IActivityGenerationParameters, IWalletState, ProcessedTransaction } from "../../interfaces"; +import { ActivityAction } from "../../enums"; +import { IActivityGenerationParameters, IWalletState } from "../../interfaces"; import { activityOutputContainsValue, getAmountFromOutput, getAsyncDataFromOutput, getLayer2ActivityInformation, getMetadataFromOutput, getNativeTokenFromOutput, getSendingInformation, getStorageDepositFromOutput, getTagFromOutput } from "../../utils"; import { ActivityBase, ActivityBaseOptions, SpecialStatus } from "./base-activity.type"; -import { isShimmerClaimingTransaction } from "shared/lib/contexts/onboarding"; -import { activeProfileId, getCoinType } from "shared/lib/core/profile"; +import { isShimmerClaimingTransaction } from "@contexts/onboarding"; +import { activeProfileId, getCoinType } from "@core/profile"; import { get } from "svelte/store"; interface ActivityBasicOptions extends ActivityBaseOptions { @@ -22,6 +22,7 @@ export class ActivityBasic extends ActivityBase { fallbackAmount?: number): Promise { const { transactionId, direction, claimingData, time, inclusionState } = processedTransaction + const specialStatus = SpecialStatus.Unclaimed // TODO: Fix this const isHidden = false const isAssetHidden = false const containsValue = await activityOutputContainsValue(wallet, wrappedOutput) diff --git a/packages/shared/lib/core/wallet/types/activities/consolidation-activity.type.ts b/packages/shared/lib/core/wallet/types/activities/consolidation-activity.type.ts index ea36de6aa22..7acec6430ab 100644 --- a/packages/shared/lib/core/wallet/types/activities/consolidation-activity.type.ts +++ b/packages/shared/lib/core/wallet/types/activities/consolidation-activity.type.ts @@ -1,7 +1,53 @@ import { ActivityType } from '@core/wallet/enums' -import { BaseActivity } from './base-activity.type' +import { ActivityBase, ActivityBaseOptions, BaseActivity, SpecialStatus } from './base-activity.type' +import { IActivityGenerationParameters, IWalletState } from '../../interfaces' export type ConsolidationActivity = BaseActivity & { type: ActivityType.Consolidation amountConsolidatedInputs: number } + +interface ActivityConsolidationOptions extends ActivityBaseOptions { + amountConsolidatedInputs: number +} + +export class ActivityConsolidation extends ActivityBase { + constructor(options: ActivityConsolidationOptions) { + super(options) + } + + static async fromProcessedTransaction(wallet: IWalletState, + { action, processedTransaction, wrappedOutput }: IActivityGenerationParameters + ) { + const { transactionId, direction, claimingData, time, inclusionState, wrappedInputs } = processedTransaction + + const specialStatus = SpecialStatus.Unclaimed // TODO: Fix this + const isHidden = false + const isAssetHidden = false + const containsValue = await activityOutputContainsValue(wallet, wrappedOutput) + + const outputId = wrappedOutput.outputId + const id = outputId || transactionId + + const output = wrappedOutput.output as BasicOutput + + const amountConsolidatedInputs = getAmountOfConsolidationInputs(wrappedInputs) + + const tag = getTagFromOutput(output) + const metadata = getMetadataFromOutput(output) + + const sendingInfo = getSendingInformation(processedTransaction, output, wallet) + const asyncData = await getAsyncDataFromOutput(output, outputId, claimingData, wallet) + + const { storageDeposit, giftedStorageDeposit } = await getStorageDepositFromOutput(output) + return new ActivityConsolidation({ + id, + inclusionState, + specialStatus, + amountConsolidatedInputs, + time, + from, + to + }) + } +} diff --git a/packages/shared/lib/core/wallet/utils/generateActivity/helper/getActivityTypeFromOutput.ts b/packages/shared/lib/core/wallet/utils/generateActivity/helper/getActivityTypeFromOutput.ts index ecc9c9c937d..e012ec698e1 100644 --- a/packages/shared/lib/core/wallet/utils/generateActivity/helper/getActivityTypeFromOutput.ts +++ b/packages/shared/lib/core/wallet/utils/generateActivity/helper/getActivityTypeFromOutput.ts @@ -17,8 +17,8 @@ export function getActivityTypeFromOutput(output: IWrappedOutput): ActivityType return ActivityType.Governance } else if (isVestingOutputId(output.outputId)) { return ActivityType.Vesting - } else { - return ActivityType.Basic } } + + return ActivityType.Basic } diff --git a/packages/shared/lib/core/wallet/utils/transactions/activityOutputContainsValue.ts b/packages/shared/lib/core/wallet/utils/transactions/activityOutputContainsValue.ts index 4280572f83e..8e0e5a73a7a 100644 --- a/packages/shared/lib/core/wallet/utils/transactions/activityOutputContainsValue.ts +++ b/packages/shared/lib/core/wallet/utils/transactions/activityOutputContainsValue.ts @@ -6,7 +6,7 @@ import { getActivityTypeFromOutput, getAmountFromOutput, getStorageDepositFromOu import { BasicOutput } from '@iota/sdk/out/types' export async function activityOutputContainsValue( - wallet: IWalletState, + wallet: IWalletState, // TODO: Remove this parameter wrappedOutput: IWrappedOutput ): Promise { const type = getActivityTypeFromOutput(wrappedOutput) From 1e01877fd8fd7fe66884cca04856fca42ff782cb Mon Sep 17 00:00:00 2001 From: marc2332 Date: Fri, 15 Mar 2024 11:34:10 +0100 Subject: [PATCH 4/4] :godmode: --- .../activities/class-diagram.md | 1 - .../shimmer-claiming-transactions.store.ts | 2 +- .../hideActivitiesForHiddenAssets.ts | 2 +- ...inkTransactionsWithClaimingTransactions.ts | 2 + .../activities/loadAssetsForAllWallets.ts | 2 +- .../updateClaimingTransactionInclusion.ts | 6 +- .../events-handlers/handleNewOutputEvent.ts | 2 +- .../default-activity-filter.constant.ts | 4 +- .../core/wallet/enums/activity-type.enum.ts | 2 +- .../core/wallet/enums/inclusion-state.enum.ts | 4 +- .../processed-transaction.interface.ts | 55 +-- .../selected-wallet-activities.store.ts | 9 +- .../types/activities/account-activity.type.ts | 98 ++++- .../types/activities/anchor-activity.type.ts | 101 ++++- .../types/activities/base-activity.type.ts | 347 ++++++++++++------ .../types/activities/basic-activity.class.ts | 111 ------ .../activities/consolidation-activity.type.ts | 45 ++- .../types/activities/foundry-activity.type.ts | 108 +++++- .../activities/governance-activity.type.ts | 93 ++++- .../lib/core/wallet/types/activities/index.ts | 6 +- .../types/activities/nft-activity.type.ts | 85 ++++- .../activities/transaction-activity.type.ts | 215 ++++++++++- .../types/activities/vesting-activity.type.ts | 80 +++- .../generateActivity/generateActivities.ts | 2 +- .../generateSingleBasicActivity.ts | 9 +- .../helper/getActivityTypeFromOutput.ts | 2 +- .../helper/getAsyncDataFromOutput.ts | 3 +- .../helper/getGovernanceInfo.ts | 1 + .../helper/getSubjectFromActivity.ts | 3 +- .../updateActivityFromPartialActivity.ts | 2 +- .../core/wallet/utils/getActivityTileTitle.ts | 2 +- .../core/wallet/utils/isVisibleActivity.ts | 10 +- .../outputs/getFormattedAmountFromActivity.ts | 2 +- .../lib/core/wallet/utils/send/sendUtils.ts | 8 +- .../activityOutputContainsValue.ts | 2 +- 35 files changed, 1082 insertions(+), 344 deletions(-) delete mode 100644 packages/shared/lib/core/wallet/types/activities/basic-activity.class.ts diff --git a/docs/specifications/activities/class-diagram.md b/docs/specifications/activities/class-diagram.md index 94af714ae76..1b098ed1134 100644 --- a/docs/specifications/activities/class-diagram.md +++ b/docs/specifications/activities/class-diagram.md @@ -42,7 +42,6 @@ classDiagram - type: ActivityType.Basic - rawAmount: number - assetId: string - - publicNote: string - isShimmerClaiming: boolean } class Nft Activity { diff --git a/packages/shared/lib/contexts/onboarding/stores/shimmer-claiming-transactions.store.ts b/packages/shared/lib/contexts/onboarding/stores/shimmer-claiming-transactions.store.ts index ed0f4f8f70f..a98809758a0 100644 --- a/packages/shared/lib/contexts/onboarding/stores/shimmer-claiming-transactions.store.ts +++ b/packages/shared/lib/contexts/onboarding/stores/shimmer-claiming-transactions.store.ts @@ -12,7 +12,7 @@ export const shimmerClaimingTransactions: Writable { state[get(selectedWalletId)].forEach((_activity) => { - if (_activity.type === ActivityType.Basic || _activity.type === ActivityType.Foundry) { + if (_activity.type === ActivityType.Transaction || _activity.type === ActivityType.Foundry) { const isAssetHidden = !assets[_activity.assetId] || assets[_activity.assetId]?.hidden updateActivityFromPartialActivity(_activity, { isAssetHidden }) } diff --git a/packages/shared/lib/core/wallet/actions/activities/linkTransactionsWithClaimingTransactions.ts b/packages/shared/lib/core/wallet/actions/activities/linkTransactionsWithClaimingTransactions.ts index d55edcb912e..58c4c786362 100644 --- a/packages/shared/lib/core/wallet/actions/activities/linkTransactionsWithClaimingTransactions.ts +++ b/packages/shared/lib/core/wallet/actions/activities/linkTransactionsWithClaimingTransactions.ts @@ -5,6 +5,8 @@ import { isOutputAsync } from '@core/wallet/utils/outputs/isOutputAsync' import { get } from 'svelte/store' import { addClaimedActivity, claimedActivities } from '../../stores' +// TODO: Refactor and clean this + /** * It takes a list of transactions and links the transactions that are claiming async transactions * @param {IProcessedTransaction[]} transactions - IProcessedTransaction[] diff --git a/packages/shared/lib/core/wallet/actions/activities/loadAssetsForAllWallets.ts b/packages/shared/lib/core/wallet/actions/activities/loadAssetsForAllWallets.ts index 19afb3ad237..cbc4a263202 100644 --- a/packages/shared/lib/core/wallet/actions/activities/loadAssetsForAllWallets.ts +++ b/packages/shared/lib/core/wallet/actions/activities/loadAssetsForAllWallets.ts @@ -11,7 +11,7 @@ export async function loadAssetsForAllWallets(wallet: IWalletState): Promise { @@ -74,7 +79,6 @@ getSendingInformation( }) } - getBurnedNativeTokens(): { assetId: string; amount: number } | undefined { // If the transaction is unblanced and there is a surplus of native tokens on the // input side of the transaction: the transaction destroys tokens. @@ -90,7 +94,9 @@ getSendingInformation( return } - const outputNativeTokens: { [key: string]: number } = ProcessedTransaction.getAllNativeTokensFromOutputs(this.outputs) + const outputNativeTokens: { [key: string]: number } = ProcessedTransaction.getAllNativeTokensFromOutputs( + this.outputs + ) // Find missing native tokens in outputNativeTokens (ex. input native tokens count === 3, output native tokens count === 2) // TO DO: adjust UI to account for burining entire amounts of multiple native tokens in one transaction. // We assume here that transaction burns entire amount of only one token. @@ -101,14 +107,15 @@ getSendingInformation( return { assetId: burnedTokenKeys[0], amount: inputNativeTokens[burnedTokenKeys[0]] } } // Check if the amount of output native token was larger on the input side (partially burned native tokens) - burnedTokenKeys = Object.keys(outputNativeTokens).filter((key) => outputNativeTokens[key] < inputNativeTokens[key]) + burnedTokenKeys = Object.keys(outputNativeTokens).filter( + (key) => outputNativeTokens[key] < inputNativeTokens[key] + ) if (Object.keys(burnedTokenKeys).length > 0) { const burnedAmount = inputNativeTokens[burnedTokenKeys[0]] - Number(outputNativeTokens[burnedTokenKeys[0]]) return { assetId: burnedTokenKeys[0], amount: burnedAmount } } } - static getAllNativeTokensFromOutputs(outputs: IWrappedOutput[]): { [key: string]: number } { const nativeTokens: { [key: string]: number } = {} for (const output of outputs) { @@ -126,4 +133,4 @@ getSendingInformation( } return nativeTokens } -} \ No newline at end of file +} diff --git a/packages/shared/lib/core/wallet/stores/selected-wallet-activities.store.ts b/packages/shared/lib/core/wallet/stores/selected-wallet-activities.store.ts index 6ad7af83d0c..7e82abd4440 100644 --- a/packages/shared/lib/core/wallet/stores/selected-wallet-activities.store.ts +++ b/packages/shared/lib/core/wallet/stores/selected-wallet-activities.store.ts @@ -28,14 +28,15 @@ export const activitySearchTerm: Writable = writable('') export const queriedActivities: Readable = derived( [selectedWalletActivities, activitySearchTerm, activityFilter], ([$selectedWalletActivities, $activitySearchTerm]) => { + // TODO: Refactor this an clean up. let activityList = $selectedWalletActivities.filter((_activity) => { - const containsAssets = _activity.type === ActivityType.Basic || _activity.type === ActivityType.Foundry + const containsAssets = _activity.type === ActivityType.Transaction || _activity.type === ActivityType.Foundry if (!_activity.isHidden && !containsAssets) { return true } const asset = - _activity.type === ActivityType.Basic || _activity.type === ActivityType.Foundry + _activity.type === ActivityType.Transaction || _activity.type === ActivityType.Foundry ? getAssetFromPersistedAssets(_activity.assetId) : undefined const hasValidAsset = asset?.metadata && isValidIrc30Token(asset.metadata) @@ -64,7 +65,7 @@ function getFieldsToSearchFromActivity(activity: Activity): string[] { fieldsToSearch.push(activity.transactionId) } - if ((activity.type === ActivityType.Basic || activity.type === ActivityType.Foundry) && activity.assetId) { + if ((activity.type === ActivityType.Transaction || activity.type === ActivityType.Foundry) && activity.assetId) { fieldsToSearch.push(activity.assetId) const assetName = getAssetFromPersistedAssets(activity.assetId)?.metadata?.name @@ -73,7 +74,7 @@ function getFieldsToSearchFromActivity(activity: Activity): string[] { } } - if ((activity.type === ActivityType.Basic || activity.type === ActivityType.Foundry) && activity.rawAmount) { + if ((activity.type === ActivityType.Transaction || activity.type === ActivityType.Foundry) && activity.rawAmount) { fieldsToSearch.push(activity.rawAmount?.toString()) fieldsToSearch.push(getFormattedAmountFromActivity(activity, false)?.toLowerCase()) } diff --git a/packages/shared/lib/core/wallet/types/activities/account-activity.type.ts b/packages/shared/lib/core/wallet/types/activities/account-activity.type.ts index 41871230d1d..7e6f3d16fb0 100644 --- a/packages/shared/lib/core/wallet/types/activities/account-activity.type.ts +++ b/packages/shared/lib/core/wallet/types/activities/account-activity.type.ts @@ -1,6 +1,25 @@ -import { ActivityType } from '@core/wallet/enums' +import { ActivityAction, ActivityType } from '@core/wallet/enums' import { ActivityBase, ActivityBaseOptions, BaseActivity, SpecialStatus } from './base-activity.type' -import { IActivityGenerationParameters, IWalletState } from '../../interfaces' +import { + ActivityGenerationParameters, + IActivityGenerationParameters, + IProcessedTransaction, + IWalletState, + ProcessedTransaction, +} from '../../interfaces' +import { AccountOutput, InclusionState, OutputType } from '@iota/sdk/out/types' +import { api } from 'shared/lib/core/api' +import { getNetworkHrp } from 'shared/lib/core/profile' +import { + getStorageDepositFromOutput, + getAmountFromOutput, + getMetadataFromOutput, + getTagFromOutput, + getAsyncDataFromOutput, + getSendingInformation, +} from '../../utils' +import { Activity } from '../activity.type' +import { EMPTY_HEX_ID } from '../../constants' export type AccountActivity = BaseActivity & { type: ActivityType.Account @@ -8,19 +27,56 @@ export type AccountActivity = BaseActivity & { accountId: string } - interface ActivityAccountOptions extends ActivityBaseOptions { accountAddress: string accountId: string } export class ActivityAccount extends ActivityBase { - constructor(options: ActivityAccountOptions) { - super(options) + constructor(private accountOptions: ActivityAccountOptions) { + super(accountOptions) + } + + accountId(): string { + return this.accountOptions.accountId + } + + accountAddress(): string { + return this.accountOptions.accountAddress + } + + tileTitle(): string { + const isConfirmed = this.inclusionState() === InclusionState.Confirmed + //if (action === ActivityAction.Mint) { + // return isConfirmed ? 'general.accountCreated' : 'general.creatingAnAccount' + //} + return isConfirmed ? 'general.minted' : 'general.minting' } - static async fromProcessedTransaction(wallet: IWalletState, - { action, processedTransaction, wrappedOutput }: IActivityGenerationParameters + static async fromOutputs( + processedTransaction: ProcessedTransaction, + wallet: IWalletState + ): Promise { + const outputs = processedTransaction.outputs + const activities: ActivityAccount[] = [] + + const accountOutputs = outputs.filter((output) => output.output.type === OutputType.Account) + for (const accountOutput of accountOutputs) { + const output = accountOutput.output as AccountOutput + const activity = await ActivityAccount.fromProcessedTransaction(wallet, { + // TODO: Check if an account is created or minted and set the action accordingly + action: output.accountId === EMPTY_HEX_ID ? ActivityAction.Send : ActivityAction.Mint, + processedTransaction, + wrappedOutput: accountOutput, + }) + activities.push(activity) + } + return activities + } + + static async fromProcessedTransaction( + wallet: IWalletState, + { action, processedTransaction, wrappedOutput }: ActivityGenerationParameters ) { const { transactionId, claimingData, direction, time, inclusionState } = processedTransaction @@ -28,30 +84,46 @@ export class ActivityAccount extends ActivityBase { const output = wrappedOutput.output as AccountOutput const outputId = wrappedOutput.outputId const id = outputId || transactionId - + const { storageDeposit: _storageDeposit, giftedStorageDeposit } = await getStorageDepositFromOutput(output) const storageDeposit = getAmountFromOutput(output) + _storageDeposit const accountId = getAccountId(output, outputId) const accountAddress = api.accountIdToBech32(accountId, getNetworkHrp()) - + const isHidden = false const isAssetHidden = false const containsValue = true - + const metadata = getMetadataFromOutput(output) const tag = getTagFromOutput(output) const asyncData = await getAsyncDataFromOutput(output, outputId, claimingData, wallet) - const sendingInfo = getSendingInformation(processedTransaction, output, wallet) + const sendingInfo = processedTransaction.getSendingInformation(wallet, output) return new ActivityAccount({ + isHidden, id, inclusionState, specialStatus, accountAddress, accountId, time, - from, - to + action, + direction, + isAssetHidden, + containsValue, + metadata, + tag, + asyncData, + outputId, + transactionId, + storageDeposit, + giftedStorageDeposit, + ...sendingInfo }) } } + +function getAccountId(output: AccountOutput, outputId: string): string { + const isNewAccount = output.accountId === EMPTY_HEX_ID + return isNewAccount ? api.computeAccountId(outputId) : output.accountId +} diff --git a/packages/shared/lib/core/wallet/types/activities/anchor-activity.type.ts b/packages/shared/lib/core/wallet/types/activities/anchor-activity.type.ts index ab77b06f452..f5171775f9a 100644 --- a/packages/shared/lib/core/wallet/types/activities/anchor-activity.type.ts +++ b/packages/shared/lib/core/wallet/types/activities/anchor-activity.type.ts @@ -1,8 +1,105 @@ -import { ActivityType } from '@core/wallet/enums' -import { BaseActivity } from './base-activity.type' +import { ActivityAction, ActivityType } from '@core/wallet/enums' +import { ActivityBase, ActivityBaseOptions, BaseActivity, SpecialStatus } from './base-activity.type' +import { + ActivityGenerationParameters, + IWalletState, + ProcessedTransaction, +} from '../../interfaces' +import { AnchorOutput, OutputType } from '@iota/sdk/out/types' +import { EMPTY_HEX_ID } from '../../constants' +import { getAmountFromOutput, getAsyncDataFromOutput, getGovernorAddressFromAnchorOutput, getMetadataFromOutput, getStateControllerAddressFromAnchorOutput, getStorageDepositFromOutput, getTagFromOutput } from '../../utils' export type AnchorActivity = BaseActivity & { type: ActivityType.Anchor governorAddress: string stateControllerAddress: string } + +interface ActivityAnchorOptions extends ActivityBaseOptions { + governorAddress: string + stateControllerAddress: string +} + +export class ActivityAnchor extends ActivityBase { + constructor(private anchorOptions: ActivityAnchorOptions) { + super(anchorOptions) + } + + governorAddress(): string { + return this.anchorOptions.governorAddress + } + + stateControllerAddress(): string { + return this.anchorOptions.stateControllerAddress + } + + static async fromOutputs( + processedTransaction: ProcessedTransaction, + wallet: IWalletState + ): Promise { + const outputs = processedTransaction.outputs + const activities = [] + + const anchorOutputs = outputs.filter((output) => output.output.type === OutputType.Anchor) + for (const anchorOutput of anchorOutputs) { + const output = anchorOutput.output as AnchorOutput + const activity = await ActivityAnchor.fromProcessedTransaction(wallet, { + action: output.anchorId === EMPTY_HEX_ID ? ActivityAction.Mint : ActivityAction.Send, + processedTransaction, + wrappedOutput: anchorOutput, + }) + activities.push(activity) + } + return activities + } + + static async fromProcessedTransaction( + wallet: IWalletState, + { action, processedTransaction, wrappedOutput }: ActivityGenerationParameters + ): Promise { + const { transactionId, claimingData, direction, time, inclusionState } = processedTransaction + + const specialStatus = SpecialStatus.Unclaimed // TODO: Fix this + const output = wrappedOutput.output as AnchorOutput + const outputId = wrappedOutput.outputId + const id = outputId || transactionId + + const { storageDeposit: _storageDeposit, giftedStorageDeposit } = await getStorageDepositFromOutput( + output + ) + const storageDeposit = getAmountFromOutput(output) + _storageDeposit + const governorAddress = getGovernorAddressFromAnchorOutput(output) + const stateControllerAddress = getStateControllerAddressFromAnchorOutput(output) + + const isHidden = false + const isAssetHidden = false + const containsValue = true + + const metadata = getMetadataFromOutput(output) + const tag = getTagFromOutput(output) + const asyncData = await getAsyncDataFromOutput(output, outputId, claimingData, wallet) + const sendingInfo = processedTransaction.getSendingInformation(wallet, output) + + return new ActivityAnchor({ + isHidden, + id, + inclusionState, + specialStatus, + time, + transactionId, + direction, + action, + governorAddress, + stateControllerAddress, + isAssetHidden, + containsValue, + outputId, + storageDeposit, + giftedStorageDeposit, + metadata, + tag, + asyncData, + ...sendingInfo, + }) + } +} diff --git a/packages/shared/lib/core/wallet/types/activities/base-activity.type.ts b/packages/shared/lib/core/wallet/types/activities/base-activity.type.ts index c783ef690c7..996d653a38d 100644 --- a/packages/shared/lib/core/wallet/types/activities/base-activity.type.ts +++ b/packages/shared/lib/core/wallet/types/activities/base-activity.type.ts @@ -1,15 +1,14 @@ import { NftOutput, OutputType, InclusionState } from '@iota/sdk/out/types' -import { ActivityAsyncStatus, ActivityDirection, ActivityAction, ActivityType } from '../../enums' +import { ActivityAsyncStatus, ActivityDirection, ActivityAction, ActivityType, SubjectType } from '../../enums' import { IProcessedTransaction, IWalletState, IWrappedOutput, ProcessedTransaction } from '../../interfaces' import { Subject } from '../subject.type' -import { Layer2Metadata } from '@core/layer-2' +import { Layer2Metadata, getLayer2NetworkFromAddress } from '@core/layer-2' import { isParticipationOutput } from '@contexts/governance' import { getActivityTypeFromOutput, getNftId, getNonRemainderBasicOutputsFromTransaction } from '../../utils' -import { ActivityNft } from './nft-activity.type' import { addOrUpdateNftInAllWalletNfts, buildNftFromNftOutput } from '@core/nfts' // TODO: Fix imports -import { ActivityBasic } from './basic-activity.class' -import { ActivityConsolidation } from './consolidation-activity.type' -import { ActivityAccount } from './account-activity.type' +import * as Activities from './' +import { localize } from 'shared/lib/core/i18n' +import { truncateString } from 'shared/lib/core/utils' export type BaseActivity = { id: string @@ -19,7 +18,7 @@ export type BaseActivity = { inclusionState: InclusionState isHidden?: boolean containsValue: boolean - isAssetHidden: boolean + isAssetHidden: boolean // TODO: Is `isAssetHidden` even used? direction: ActivityDirection action: ActivityAction isInternal: boolean @@ -29,7 +28,7 @@ export type BaseActivity = { subject: Subject | undefined metadata?: string tag?: string - asyncData: AsyncData + asyncData?: AsyncData destinationNetwork?: string parsedLayer2Metadata?: Partial } @@ -46,37 +45,197 @@ export type AsyncData = { // TODO: Move somewhere else. export enum SpecialStatus { - Unclaimed = "Unclaimed", - Claimed = "Claimed", - Expired = "Expired", - TimeLocked = "TimeLocked" + Unclaimed = 'Unclaimed', + Claimed = 'Claimed', + Expired = 'Expired', + TimeLocked = 'TimeLocked', } export interface ActivityBaseOptions { - id: string, - inclusionState: InclusionState, - specialStatus: SpecialStatus, - time: Date, // Should this be number, slot index? - from: string[], - to: string[], + id: string + inclusionState: InclusionState + specialStatus: SpecialStatus + time: Date // Should this be number, slot index? + //from: string[] + //to: string[] + + isHidden: boolean + action: ActivityAction + direction: ActivityDirection + isInternal?: boolean + + outputId: string + transactionId: string + containsValue: boolean + isAssetHidden: boolean // TODO: Is `isAssetHidden` even used? + storageDeposit: number + giftedStorageDeposit: number + surplus?: number + subject: Subject | undefined + metadata?: string + tag?: string + asyncData?: AsyncData + destinationNetwork?: string + parsedLayer2Metadata?: Partial | null } -export class ActivityBase { - constructor(options: ActivityBaseOptions) { } +abstract class ActivityUtils { + abstract tileTitle(): string + + abstract subjectLocale(): string +} - id(){ +export class ActivityBase implements ActivityUtils { + constructor(private options: ActivityBaseOptions) {} + + isIncoming(): boolean { + return [ActivityDirection.Incoming, ActivityDirection.Incoming].includes(this.direction()) + } + + subject(): Subject | undefined { + const subject = this.options.subject; + if (this.parsedLayer2Metadata() && subject) { + return { + ...subject, + ...(subject?.type === SubjectType.Address && { + address: this.parsedLayer2Metadata()?.ethereumAddress, + }), + } + } else if (subject?.type === SubjectType.Address) { + const network = getLayer2NetworkFromAddress(subject.address) + return { ...subject, address: network ?? subject.address } + } else { + return this.subject() + } + } + + subjectLocale(): string { + const subject = this.subject(); + + if (subject?.type === SubjectType.Wallet) { + return truncateString(subject?.wallet?.name, 13, 0) + } else if (subject?.type === SubjectType.Address) { + const address = this.parsedLayer2Metadata()?.ethereumAddress ?? subject?.address + const network = getLayer2NetworkFromAddress(address) + + return network ?? truncateString(address, 6, 6) + } else { + return localize('general.unknownAddress') + } + } + + id() { return this.id } - - inclusionState(){ - return this.inclusionState + + inclusionState() { + return this.options.inclusionState + } + + specialStatus() { + return this.options.specialStatus + } + + time() { + return this.options.time + } + + isHidden() { + return this.options.isHidden + } + + action() { + return this.options.action + } + + direction() { + return this.options.direction + } + + isInternal(): boolean { + return this.options.isInternal ?? false + } + + outputId() { + return this.options.outputId + } + + transactionId() { + return this.options.transactionId + } + + containsValue() { + return this.options.containsValue + } + + isAssetHidden() { + return this.options.isAssetHidden + } + + storageDeposit() { + return this.options.storageDeposit + } + + giftedStorageDeposit() { + return this.options.giftedStorageDeposit + } + + surplus() { + return this.options.surplus + } + + metadata() { + return this.options.metadata + } + + tag() { + return this.options.tag + } + + asyncData(): AsyncData | undefined { + return this.options.asyncData + } + + destinationNetwork() { + return this.options.destinationNetwork + } + + parsedLayer2Metadata() { + return this.options.parsedLayer2Metadata + } + + tileTitle(): string { + const isConfirmed = this.inclusionState() === InclusionState.Confirmed + if (this.action() === ActivityAction.Burn) { + return isConfirmed ? 'general.burned' : 'general.burning' + } else if (this.action() === ActivityAction.Send) { + if (this.isInternal()) { + return isConfirmed ? 'general.transfer' : 'general.transferring' + } + if ( + this.direction() === ActivityDirection.Incoming || + this.direction() === ActivityDirection.SelfTransaction + ) { + return isConfirmed ? 'general.received' : 'general.receiving' + } + if (this.direction() === ActivityDirection.Outgoing) { + return isConfirmed ? 'general.sent' : 'general.sending' + } + } else { + return 'general.unknown' + } + + return '' } /** * Generate a group of activies given a processed transaction * @returns ActivityBase[] */ - static generateActivitiesFromProcessedTransaction(wallet: IWalletState, processedTransaction: ProcessedTransaction): Promise> { + static generateActivitiesFromProcessedTransaction( + wallet: IWalletState, + processedTransaction: ProcessedTransaction + ): Promise> { if (processedTransaction.wrappedInputs?.length > 0) { return this.generateActivitiesFromProcessedTransactionsWithInputs(wallet, processedTransaction) } else { @@ -84,28 +243,31 @@ export class ActivityBase { } } - static async generateActivitiesFromProcessedTransactionsWithInputs(wallet: IWalletState, processedTransaction: ProcessedTransaction): Promise> { + static async generateActivitiesFromProcessedTransactionsWithInputs( + wallet: IWalletState, + processedTransaction: ProcessedTransaction + ): Promise> { let activities: Array = [] - const { wrappedInputs, outputs, direction } = processedTransaction; + const { wrappedInputs, outputs } = processedTransaction const containsFoundryActivity = outputs.some((output) => output.output.type === OutputType.Foundry) if (containsFoundryActivity) { - // const foundryActivities = await generateActivitiesFromFoundryOutputs(processedTransaction, wallet) - // activities.push(...foundryActivities) + const foundryActivities = await Activities.ActivityFoundry.fromOutputs(processedTransaction, wallet) + activities.push(...foundryActivities) } const containsNftActivity = outputs.some((output) => output.output.type === OutputType.Nft) if (containsNftActivity) { - // const nftActivities = await generateActivitiesFromNftOutputs(processedTransaction, wallet) - // activities.push(...nftActivities) + const nftActivities = await Activities.ActivityNft.fromOutputs(processedTransaction, wallet) + activities.push(...nftActivities) } const containsAccountActivity = outputs.some((output) => output.output.type === OutputType.Account) && !containsFoundryActivity if (containsAccountActivity) { - // const accountActivities = await generateActivitiesFromAccountOutputs(processedTransaction, wallet) - // activities.push(...accountActivities) + const accountActivities = await Activities.ActivityAccount.fromOutputs(processedTransaction, wallet) + activities.push(...accountActivities) } const hasParticipationInputs = wrappedInputs?.some((input) => isParticipationOutput(input.output)) @@ -113,114 +275,58 @@ export class ActivityBase { ? outputs[0] : outputs.find((output) => isParticipationOutput(output.output)) if (governanceOutput) { - // const governanceActivity = await generateSingleGovernanceActivity(wallet, { - // processedTransaction, - // wrappedOutput: governanceOutput, - // action: null, - // }) - // activities.push(governanceActivity) + const governanceActivity = await Activities.ActivityGovernance.fromProcessedTransaction(wallet, { + processedTransaction, + wrappedOutput: governanceOutput, + action: ActivityAction.Unknown, // TODO: Maybe this should be optional? + }) + activities.push(governanceActivity) } const containsAnchorActivity = outputs.some((output) => output.output.type === OutputType.Anchor) if (containsAnchorActivity) { - // const anchorActivities = await generateActivitiesFromAnchorOutputs(processedTransaction, wallet) - // activities.push(...anchorActivities) + const anchorActivities = await Activities.ActivityAnchor.fromOutputs(processedTransaction, wallet) + activities.push(...anchorActivities) } - if (!containsFoundryActivity && !containsNftActivity && !containsAccountActivity && !governanceOutput) { - - const basicOutputs = getNonRemainderBasicOutputsFromTransaction( - outputs, - wallet.depositAddress, - direction - ) - const burnedNftInputs = processedTransaction.getBurnedNftInputs(); - for (const basicOutput of basicOutputs) { - let activity: ActivityBase - - const isSelfTransaction = processedTransaction.direction === ActivityDirection.SelfTransaction - const burnedNftInputIndex = burnedNftInputs.findIndex( - (input) => input.output.amount === basicOutput.output.amount - ) - const burnedNativeToken = burnedNftInputIndex < 0 ? processedTransaction.getBurnedNativeTokens() : undefined - - // NFT Activity - if (isSelfTransaction && burnedNftInputIndex >= 0) { - const wrappedInput = burnedNftInputs[burnedNftInputIndex] - const nftInput = wrappedInput.output as NftOutput - - activity = await ActivityNft.fromProcessedTransaction( - wallet, - { - action: ActivityAction.Burn, - processedTransaction, - wrappedOutput: basicOutput, - }, - getNftId(nftInput.nftId, wrappedInput.outputId) - ) - const nft = buildNftFromNftOutput(wrappedInput, wallet.depositAddress, false) - addOrUpdateNftInAllWalletNfts(wallet.id, nft) - - burnedNftInputs.splice(burnedNftInputIndex, 1) - } - // Burn Activity - else if (isSelfTransaction && burnedNativeToken) { - activity = await ActivityBasic.fromProcessedTransaction(wallet, { - action: ActivityAction.Burn, - processedTransaction, - wrappedOutput: basicOutput, - }) - } - // Consolidation Activity - else if (isSelfTransaction && ActivityBase.isConsolidation(basicOutput, processedTransaction)) { - activity = await ActivityConsolidation.fromProcessedTransaction(wallet, { - action: ActivityAction.Send, - processedTransaction, - wrappedOutput: basicOutput, - }) - } - // Send Activity - else { - activity = await ActivityBasic.fromProcessedTransaction(wallet, { - action: ActivityAction.Send, - processedTransaction, - wrappedOutput: basicOutput, - }) - } - activities.push(activity) - } - } else { + if (!containsFoundryActivity && !containsNftActivity && !containsAccountActivity && !governanceOutput) { + const basicActivities = await Activities.ActivityTransaction.fromOutputs(processedTransaction, wallet) + activities.push(...basicActivities) } + return activities } - static async generateActivitiesFromProcessedTransactionsWithoutInputs(wallet: IWalletState, processedTransaction: ProcessedTransaction): Promise> { + static async generateActivitiesFromProcessedTransactionsWithoutInputs( + wallet: IWalletState, + processedTransaction: ProcessedTransaction + ): Promise> { const nonRemainderOutputs = processedTransaction.outputs.filter((wrappedOutput) => !wrappedOutput.remainder) const activities = await Promise.all( nonRemainderOutputs.map(async (wrappedOutput) => { const params = { - type: getActivityTypeFromOutput(wrappedOutput), action: ActivityAction.Unknown, processedTransaction, wrappedOutput, } - switch (params.type) { - case ActivityType.Basic: - return ActivityBasic.fromProcessedTransaction(wallet, params) + const activityType = getActivityTypeFromOutput(wrappedOutput) + switch (activityType) { + case ActivityType.Transaction: + return Activities.ActivityTransaction.fromProcessedTransaction(wallet, params) case ActivityType.Governance: - // return generateSingleGovernanceActivity(wallet, params) + return Activities.ActivityGovernance.fromProcessedTransaction(wallet, params) case ActivityType.Foundry: - // return generateSingleFoundryActivity(wallet, params) + return Activities.ActivityFoundry.fromProcessedTransaction(wallet, params) case ActivityType.Account: - return ActivityAccount.fromProcessedTransaction(wallet, params) + return Activities.ActivityAccount.fromProcessedTransaction(wallet, params) case ActivityType.Nft: - return ActivityNft.fromProcessedTransaction(wallet, params) + return Activities.ActivityNft.fromProcessedTransaction(wallet, params) case ActivityType.Vesting: - // return generateVestingActivity(wallet, params) + return Activities.ActivityVesting.fromProcessedTransaction(wallet, params) case ActivityType.Anchor: - // return generateSingleAnchorActivity(wallet, params) + return Activities.ActivityAnchor.fromProcessedTransaction(wallet, params) default: - throw new Error(`Unknown activity type: ${params.type}`) + throw new Error(`Unknown activity type: ${activityType}`) } }) ) @@ -228,13 +334,14 @@ export class ActivityBase { } static isConsolidation(output: IWrappedOutput, processedTransaction: IProcessedTransaction): boolean { - const allBasicInputs = processedTransaction.wrappedInputs.every((input) => input.output.type === OutputType.Basic) + const allBasicInputs = processedTransaction.wrappedInputs.every( + (input) => input.output.type === OutputType.Basic + ) const isSelfTransaction = processedTransaction.direction === ActivityDirection.SelfTransaction const isSameAmount = processedTransaction.wrappedInputs.reduce((sum, input) => sum + Number(input.output.amount), 0) === Number(output.output.amount) - + return allBasicInputs && isSelfTransaction && isSameAmount - } -} \ No newline at end of file +} diff --git a/packages/shared/lib/core/wallet/types/activities/basic-activity.class.ts b/packages/shared/lib/core/wallet/types/activities/basic-activity.class.ts deleted file mode 100644 index 7e7aaba6523..00000000000 --- a/packages/shared/lib/core/wallet/types/activities/basic-activity.class.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { BasicOutput } from "@iota/sdk/out/types"; -import { ActivityAction } from "../../enums"; -import { IActivityGenerationParameters, IWalletState } from "../../interfaces"; -import { activityOutputContainsValue, getAmountFromOutput, getAsyncDataFromOutput, getLayer2ActivityInformation, getMetadataFromOutput, getNativeTokenFromOutput, getSendingInformation, getStorageDepositFromOutput, getTagFromOutput } from "../../utils"; -import { ActivityBase, ActivityBaseOptions, SpecialStatus } from "./base-activity.type"; -import { isShimmerClaimingTransaction } from "@contexts/onboarding"; -import { activeProfileId, getCoinType } from "@core/profile"; -import { get } from "svelte/store"; - -interface ActivityBasicOptions extends ActivityBaseOptions { - -} - -export class ActivityBasic extends ActivityBase { - constructor(options: ActivityBasicOptions) { - super(options) - } - - static async fromProcessedTransaction(wallet: IWalletState, - { action, processedTransaction, wrappedOutput }: IActivityGenerationParameters, - fallbackAssetId?: string, - fallbackAmount?: number): Promise { - const { transactionId, direction, claimingData, time, inclusionState } = processedTransaction - - const specialStatus = SpecialStatus.Unclaimed // TODO: Fix this - const isHidden = false - const isAssetHidden = false - const containsValue = await activityOutputContainsValue(wallet, wrappedOutput) - - const outputId = wrappedOutput.outputId - const id = outputId || transactionId - - const output = wrappedOutput.output as BasicOutput - const amount = getAmountFromOutput(output) - - const isShimmerClaiming = isShimmerClaimingTransaction(transactionId, get(activeProfileId)) - - const tag = getTagFromOutput(output) - const metadata = getMetadataFromOutput(output) - const publicNote = '' - - const sendingInfo = getSendingInformation(processedTransaction, output, wallet) - const asyncData = await getAsyncDataFromOutput(output, outputId, claimingData, wallet) - - const { parsedLayer2Metadata, destinationNetwork } = getLayer2ActivityInformation(metadata, sendingInfo) - const layer2Allowance = Number(parsedLayer2Metadata?.baseTokens ?? '0') - const gasBudget = Number(parsedLayer2Metadata?.gasBudget ?? '0') - const gasFee = layer2Allowance > 0 ? amount - layer2Allowance : 0 - - let { storageDeposit, giftedStorageDeposit } = await getStorageDepositFromOutput(output) - giftedStorageDeposit = action === ActivityAction.Burn ? 0 : giftedStorageDeposit - giftedStorageDeposit = gasBudget === 0 ? giftedStorageDeposit : 0 - - const baseTokenAmount = amount - storageDeposit - gasFee - - const nativeToken = getNativeTokenFromOutput(output) - const assetId = fallbackAssetId ?? nativeToken?.id ?? getCoinType() - - let surplus: number | undefined = undefined - if (nativeToken) { - const storageDepositToDeduct = (storageDeposit > 0 ? storageDeposit : giftedStorageDeposit) ?? 0 - surplus = Number(output.amount) - storageDepositToDeduct - } - - let rawAmount: number - if (fallbackAmount === undefined) { - rawAmount = nativeToken ? Number(nativeToken?.amount) : baseTokenAmount - } else { - rawAmount = fallbackAmount - } - - // Note: we update the displayed storage deposit so it matches what was displayed in the send confirmation flow - // set the storage deposit to zero if the amount is greater than the storage deposit - // to improve the UX so the user doesnt think they need to pay the storage deposit - if (!nativeToken && !storageDeposit && rawAmount >= giftedStorageDeposit) { - storageDeposit = giftedStorageDeposit = 0 - } - - return new ActivityBasic({ - //type: ActivityType.Basic, - //isHidden, - id, - inclusionState, - specialStatus, - time, - to, - from, - //transactionId, - // time, - // direction, - // action, - // isAssetHidden, - // inclusionState, - // containsValue, - // outputId, - // storageDeposit, - // giftedStorageDeposit, - // surplus, - // rawAmount, - // isShimmerClaiming, - // publicNote, - // metadata, - // tag, - // assetId, - // asyncData, - // destinationNetwork, - // parsedLayer2Metadata, - - }) - } -} \ No newline at end of file diff --git a/packages/shared/lib/core/wallet/types/activities/consolidation-activity.type.ts b/packages/shared/lib/core/wallet/types/activities/consolidation-activity.type.ts index 7acec6430ab..9ff77f6b199 100644 --- a/packages/shared/lib/core/wallet/types/activities/consolidation-activity.type.ts +++ b/packages/shared/lib/core/wallet/types/activities/consolidation-activity.type.ts @@ -1,6 +1,8 @@ import { ActivityType } from '@core/wallet/enums' import { ActivityBase, ActivityBaseOptions, BaseActivity, SpecialStatus } from './base-activity.type' -import { IActivityGenerationParameters, IWalletState } from '../../interfaces' +import { ActivityGenerationParameters, IActivityGenerationParameters, IWalletState, IWrappedOutput } from '../../interfaces' +import { BasicOutput, InclusionState, OutputType } from '@iota/sdk/out/types' +import { activityOutputContainsValue, getAsyncDataFromOutput, getMetadataFromOutput, getSendingInformation, getStorageDepositFromOutput, getTagFromOutput } from '../../utils' export type ConsolidationActivity = BaseActivity & { type: ActivityType.Consolidation @@ -12,12 +14,22 @@ interface ActivityConsolidationOptions extends ActivityBaseOptions { } export class ActivityConsolidation extends ActivityBase { - constructor(options: ActivityConsolidationOptions) { - super(options) + constructor(private consolidationOptions: ActivityConsolidationOptions) { + super(consolidationOptions) } - static async fromProcessedTransaction(wallet: IWalletState, - { action, processedTransaction, wrappedOutput }: IActivityGenerationParameters + amountConsolidatedInputs(): number { + return this.consolidationOptions.amountConsolidatedInputs + } + + tileTitle(): string { + const isConfirmed = this.inclusionState() === InclusionState.Confirmed + return isConfirmed ? 'general.consolidated' : 'general.consolidating' + } + + static async fromProcessedTransaction( + wallet: IWalletState, + { action, processedTransaction, wrappedOutput }: ActivityGenerationParameters ) { const { transactionId, direction, claimingData, time, inclusionState, wrappedInputs } = processedTransaction @@ -41,13 +53,28 @@ export class ActivityConsolidation extends ActivityBase { const { storageDeposit, giftedStorageDeposit } = await getStorageDepositFromOutput(output) return new ActivityConsolidation({ + specialStatus, + isHidden, id, + transactionId, + time, + direction, + action, + isAssetHidden, inclusionState, - specialStatus, + containsValue, + outputId, + storageDeposit, + giftedStorageDeposit, + metadata, + tag, + asyncData, amountConsolidatedInputs, - time, - from, - to + ...sendingInfo, }) } } + +function getAmountOfConsolidationInputs(inputs: IWrappedOutput[]): number { + return inputs.filter((input) => input.output.type === OutputType.Basic).length +} \ No newline at end of file diff --git a/packages/shared/lib/core/wallet/types/activities/foundry-activity.type.ts b/packages/shared/lib/core/wallet/types/activities/foundry-activity.type.ts index 7c1dc02aeab..eed60f3b675 100644 --- a/packages/shared/lib/core/wallet/types/activities/foundry-activity.type.ts +++ b/packages/shared/lib/core/wallet/types/activities/foundry-activity.type.ts @@ -1,5 +1,10 @@ -import { ActivityType } from '@core/wallet/enums' -import { BaseActivity } from './base-activity.type' +import { ActivityAction, ActivityType } from '@core/wallet/enums' +import { ActivityBase, ActivityBaseOptions, BaseActivity, SpecialStatus } from './base-activity.type' +import { ActivityGenerationParameters, IWalletState, ProcessedTransaction } from '../../interfaces' +import { AccountAddress, FoundryOutput, ImmutableAccountAddressUnlockCondition, OutputType, SimpleTokenScheme, UnlockConditionType } from '@iota/sdk/out/types' +import { getAmountFromOutput, getAsyncDataFromOutput, getMetadataFromOutput, getNativeTokenFromOutput, getTagFromOutput } from '../../utils' +import { api } from 'shared/lib/core/api' +import { getCoinType, getNetworkHrp } from 'shared/lib/core/profile' export type FoundryActivity = BaseActivity & { type: ActivityType.Foundry @@ -10,3 +15,102 @@ export type FoundryActivity = BaseActivity & { meltedTokens: string maximumSupply: string } + +interface ActivityFoundryOptions extends ActivityBaseOptions { + rawAmount: number + assetId: string + accountAddress: string + mintedTokens: string + meltedTokens: string + maximumSupply: string +} + +export class ActivityFoundry extends ActivityBase { + constructor(private foundryOptions: ActivityFoundryOptions) { + super(foundryOptions) + } + + static async fromOutputs( + processedTransaction: ProcessedTransaction, + wallet: IWalletState + ): Promise { + const outputs = processedTransaction.outputs + const activities = [] + + const foundryOutputs = outputs.filter((output) => output.output.type === OutputType.Foundry) + for (const foundryOutput of foundryOutputs) { + activities.push( + await ActivityFoundry.fromProcessedTransaction(wallet, { + action: ActivityAction.Mint, + processedTransaction, + wrappedOutput: foundryOutput, + }) + ) + } + return activities + } + + static async fromProcessedTransaction( + wallet: IWalletState, + { action, processedTransaction, wrappedOutput }: ActivityGenerationParameters + ) { + const { transactionId, claimingData, time, direction, inclusionState } = processedTransaction + + const specialStatus = SpecialStatus.Unclaimed // TODO: Fix this + const output = wrappedOutput.output as FoundryOutput + const outputId = wrappedOutput.outputId + const tokenScheme = output.tokenScheme as SimpleTokenScheme + const mintedTokens = tokenScheme.mintedTokens.toString() + const meltedTokens = tokenScheme.meltedTokens.toString() + const maximumSupply = tokenScheme.maximumSupply.toString() + + const addressUnlockCondition = output.unlockConditions.find( + (unlockCondition) => unlockCondition.type === UnlockConditionType.ImmutableAccountAddress + ) as ImmutableAccountAddressUnlockCondition + const accountId = (addressUnlockCondition?.address as AccountAddress)?.accountId + // TODO: Research whether this address should be optional or not. + const accountAddress = accountId ? api.accountIdToBech32(accountId, getNetworkHrp()) : undefined + + const isHidden = false + const isAssetHidden = false + const containsValue = true + + const id = outputId || transactionId + const nativeToken = getNativeTokenFromOutput(output) + const assetId = nativeToken?.id ?? getCoinType() + + const storageDeposit = getAmountFromOutput(output) + const giftedStorageDeposit = 0 + const rawAmount = Number(nativeToken?.amount ?? 0) + const metadata = getMetadataFromOutput(output) + const tag = getTagFromOutput(output) + + const sendingInfo = processedTransaction.getSendingInformation(wallet, output) + const asyncData = await getAsyncDataFromOutput(output, outputId, claimingData, wallet) + return new ActivityFoundry({ + specialStatus, + isHidden, + id, + outputId, + transactionId, + direction, + action, + assetId, + accountAddress, + mintedTokens, + meltedTokens, + maximumSupply, + storageDeposit, + giftedStorageDeposit, + rawAmount, + time, + inclusionState, + containsValue, + isAssetHidden, + metadata, + tag, + asyncData, + ...sendingInfo, + }) + } +} diff --git a/packages/shared/lib/core/wallet/types/activities/governance-activity.type.ts b/packages/shared/lib/core/wallet/types/activities/governance-activity.type.ts index f2c4f81dee0..ecac4c3c559 100644 --- a/packages/shared/lib/core/wallet/types/activities/governance-activity.type.ts +++ b/packages/shared/lib/core/wallet/types/activities/governance-activity.type.ts @@ -1,6 +1,12 @@ import { ActivityType, GovernanceAction } from '@core/wallet/enums' -import { IParticipation } from '@core/wallet/interfaces' -import { BaseActivity } from './base-activity.type' +import { + ActivityGenerationParameters, + IParticipation, + IWalletState, +} from '@core/wallet/interfaces' +import { ActivityBase, ActivityBaseOptions, BaseActivity, SpecialStatus } from './base-activity.type' +import { BasicOutput, InclusionState } from '@iota/sdk/out/types' +import { activityOutputContainsValue, getGovernanceInfo, getMetadataFromOutput, getStorageDepositFromOutput, getTagFromOutput } from '../../utils' export type GovernanceActivity = BaseActivity & { type: ActivityType.Governance @@ -9,3 +15,86 @@ export type GovernanceActivity = BaseActivity & { participation?: IParticipation votingPowerDifference?: number } + +interface ActivityGovernanceOptions extends ActivityBaseOptions { + governanceAction: GovernanceAction + votingPower: number + participation?: IParticipation + votingPowerDifference?: number +} + +export class ActivityGovernance extends ActivityBase { + constructor(private governanceOptions: ActivityGovernanceOptions) { + super(governanceOptions) + } + + governanceAction(){ + return this.governanceOptions.governanceAction + } + + tileTitle(): string { + const isConfirmed = this.inclusionState() === InclusionState.Confirmed; + switch(this.governanceAction()) { + case GovernanceAction.IncreaseVotingPower: + return isConfirmed ? 'general.increased' : 'general.increasing' + case GovernanceAction.DecreaseVotingPower: + return isConfirmed ? 'general.decreased' : 'general.decreasing' + case GovernanceAction.StartVoting: + return isConfirmed ? 'general.voted' : 'general.voting' + case GovernanceAction.StopVoting: + return isConfirmed ? 'general.unvoted' : 'general.unvoting' + case GovernanceAction.ChangedVote: + return isConfirmed ? 'general.changedVote' : 'general.changingVote' + case GovernanceAction.Revote: + return isConfirmed ? 'general.revoted' : 'general.revoting' + default: + return super.tileTitle() + } + } + + static async fromProcessedTransaction( + wallet: IWalletState, + { action, processedTransaction, wrappedOutput }: ActivityGenerationParameters + ): Promise { + const { transactionId, direction, time, inclusionState, wrappedInputs } = processedTransaction + + const specialStatus = SpecialStatus.Unclaimed // TODO: Fix this + const giftedStorageDeposit = 0 + const isHidden = false + const isAssetHidden = false + const containsValue = await activityOutputContainsValue(wallet, wrappedOutput) + + const outputId = wrappedOutput.outputId + const id = outputId || transactionId + + const output = wrappedOutput.output as BasicOutput + + const tag = getTagFromOutput(output) + const metadata = getMetadataFromOutput(output) + + const sendingInfo = processedTransaction.getSendingInformation(wallet, output) + + const { storageDeposit } = await getStorageDepositFromOutput(output) + const governanceInfo = getGovernanceInfo(output, wrappedInputs, metadata) + + return new ActivityGovernance({ + isHidden, + id, + inclusionState, + specialStatus, + time, + transactionId, + direction, + action, + isAssetHidden, + containsValue, + outputId, + storageDeposit, + giftedStorageDeposit, + metadata, + tag, + ...governanceInfo, + ...sendingInfo, + }) + } +} diff --git a/packages/shared/lib/core/wallet/types/activities/index.ts b/packages/shared/lib/core/wallet/types/activities/index.ts index 8334c661bab..11716f037a8 100644 --- a/packages/shared/lib/core/wallet/types/activities/index.ts +++ b/packages/shared/lib/core/wallet/types/activities/index.ts @@ -1,11 +1,9 @@ export * from './account-activity.type' export * from './anchor-activity.type' -export * from './base-activity.type' +export * from './transaction-activity.type' export * from './consolidation-activity.type' export * from './foundry-activity.type' export * from './governance-activity.type' export * from './nft-activity.type' -export * from './transaction-activity.type' export * from './vesting-activity.type' - -export * from './base-activity.type' \ No newline at end of file +export * from './base-activity.type' diff --git a/packages/shared/lib/core/wallet/types/activities/nft-activity.type.ts b/packages/shared/lib/core/wallet/types/activities/nft-activity.type.ts index 321897e3938..c913df75306 100644 --- a/packages/shared/lib/core/wallet/types/activities/nft-activity.type.ts +++ b/packages/shared/lib/core/wallet/types/activities/nft-activity.type.ts @@ -1,7 +1,21 @@ -import { ActivityAction, ActivityGenerationParameters, ActivityType, IWalletState, InclusionState, getAsyncDataFromOutput, getClient, getLayer2ActivityInformation, getMetadataFromOutput, getNftId, getStorageDepositFromOutput, getTagFromOutput } from '@core/wallet' +import { + ActivityAction, + ActivityGenerationParameters, + ActivityType, + EMPTY_HEX_ID, + IWalletState, + ProcessedTransaction, + getAsyncDataFromOutput, + getClient, + getLayer2ActivityInformation, + getMetadataFromOutput, + getNftId, + getStorageDepositFromOutput, + getTagFromOutput, +} from '@core/wallet' import { ActivityBase, ActivityBaseOptions, BaseActivity, SpecialStatus } from './base-activity.type' -import { NftOutput } from '@iota/sdk/out/types' -import { handleError } from 'shared/lib/core/error/handlers' +import { NftOutput, OutputType } from '@iota/sdk/out/types' +import { handleError } from '@core/error/handlers' export type NftActivity = BaseActivity & { type: ActivityType.Nft @@ -13,13 +27,39 @@ interface ActivityNftOptions extends ActivityBaseOptions { } export class ActivityNft extends ActivityBase { - constructor(options: ActivityNftOptions) { - super(options) + constructor(private nftOptions: ActivityNftOptions) { + super(nftOptions) } - static async fromProcessedTransaction(wallet: IWalletState, + nftId(): string { + return this.nftOptions.nftId + } + + static async fromOutputs( + processedTransaction: ProcessedTransaction, + wallet: IWalletState + ): Promise { + const outputs = processedTransaction.outputs + const activities = [] + + const nftOutputs = outputs.filter((output) => output.output.type === OutputType.Nft) + for (const nftOutput of nftOutputs) { + const output = nftOutput.output as NftOutput + const activity = await ActivityNft.fromProcessedTransaction(wallet, { + action: output.nftId === EMPTY_HEX_ID ? ActivityAction.Mint : ActivityAction.Send, + processedTransaction, + wrappedOutput: nftOutput, + }) + activities.push(activity) + } + return activities + } + + static async fromProcessedTransaction( + wallet: IWalletState, { action, processedTransaction, wrappedOutput }: ActivityGenerationParameters, - nftIdFromInput?: string): Promise { + nftIdFromInput?: string + ): Promise { const { claimingData, time, inclusionState, transactionId, direction } = processedTransaction const outputId = wrappedOutput.outputId const output = wrappedOutput.output as NftOutput @@ -61,13 +101,28 @@ export class ActivityNft extends ActivityBase { const asyncData = await getAsyncDataFromOutput(output, outputId, claimingData, wallet) return new ActivityNft({ - nftId, - id, - inclusionState, - specialStatus, - time, - from, - to + specialStatus, + id, + transactionId, + outputId, + nftId, + time, + isHidden, + action, + giftedStorageDeposit, + surplus, + isAssetHidden, + containsValue, + inclusionState, + storageDeposit, + metadata, + tag, + asyncData, + subject, + isInternal, + direction, + destinationNetwork, + parsedLayer2Metadata, }) } -} \ No newline at end of file +} diff --git a/packages/shared/lib/core/wallet/types/activities/transaction-activity.type.ts b/packages/shared/lib/core/wallet/types/activities/transaction-activity.type.ts index 792acfa5922..b66e236b75e 100644 --- a/packages/shared/lib/core/wallet/types/activities/transaction-activity.type.ts +++ b/packages/shared/lib/core/wallet/types/activities/transaction-activity.type.ts @@ -1,10 +1,221 @@ +import { BasicOutput, InclusionState, NftOutput } from '@iota/sdk/out/types' +import { ActivityAction, ActivityDirection } from '../../enums' +import { + ActivityGenerationParameters, + IWalletState, + ProcessedTransaction, +} from '../../interfaces' +import { + activityOutputContainsValue, + getAmountFromOutput, + getAsyncDataFromOutput, + getLayer2ActivityInformation, + getMetadataFromOutput, + getNativeTokenFromOutput, + getNftId, + getNonRemainderBasicOutputsFromTransaction, + getStorageDepositFromOutput, + getTagFromOutput, +} from '../../utils' +import { ActivityBase, ActivityBaseOptions, SpecialStatus } from './base-activity.type' +import { isShimmerClaimingTransaction } from '@contexts/onboarding' +import { activeProfileId, getCoinType } from '@core/profile' +import { get } from 'svelte/store' +import { ActivityNft } from './nft-activity.type' +import { addOrUpdateNftInAllWalletNfts, buildNftFromNftOutput } from '@core/nfts' +import { ActivityConsolidation } from './consolidation-activity.type' +import { localize } from '@core/i18n' import { ActivityType } from '@core/wallet/enums' import { BaseActivity } from './base-activity.type' export type TransactionActivity = BaseActivity & { - type: ActivityType.Basic + type: ActivityType.Transaction rawAmount: number assetId: string - publicNote: string isShimmerClaiming: boolean } + + +interface ActivityTransactionOptions extends ActivityBaseOptions { + rawAmount: number + assetId: string + isShimmerClaiming: boolean +} + +export class ActivityTransaction extends ActivityBase { + constructor(private basicOptions: ActivityTransactionOptions) { + super(basicOptions) + } + + subjectLocale(): string { + if (this.isShimmerClaiming()) { + return localize('general.shimmerGenesis') + } else { + return super.subjectLocale() + } + } + + isShimmerClaiming(){ + return this.basicOptions.isShimmerClaiming + } + + tileTitle(): string { + const isConfirmed = this.inclusionState() === InclusionState.Confirmed + if (this.isShimmerClaiming()) { + return isConfirmed ? 'general.shimmerClaimed' : 'general.shimmerClaiming' + } else { + return super.tileTitle() + } + } + + static async fromOutputs( + processedTransaction: ProcessedTransaction, + wallet: IWalletState + ): Promise { + const activities = [] + + const basicOutputs = getNonRemainderBasicOutputsFromTransaction( + processedTransaction.outputs, + wallet.depositAddress, + processedTransaction.direction + ) + const burnedNftInputs = processedTransaction.getBurnedNftInputs() + for (const basicOutput of basicOutputs) { + let activity: ActivityBase + + const isSelfTransaction = processedTransaction.direction === ActivityDirection.SelfTransaction + const burnedNftInputIndex = burnedNftInputs.findIndex( + (input) => input.output.amount === basicOutput.output.amount + ) + const burnedNativeToken = burnedNftInputIndex < 0 ? processedTransaction.getBurnedNativeTokens() : undefined + + // NFT Activity + if (isSelfTransaction && burnedNftInputIndex >= 0) { + const wrappedInput = burnedNftInputs[burnedNftInputIndex] + const nftInput = wrappedInput.output as NftOutput + + activity = await ActivityNft.fromProcessedTransaction( + wallet, + { + action: ActivityAction.Burn, + processedTransaction, + wrappedOutput: basicOutput, + }, + getNftId(nftInput.nftId, wrappedInput.outputId) + ) + const nft = buildNftFromNftOutput(wrappedInput, wallet.depositAddress, false) + addOrUpdateNftInAllWalletNfts(wallet.id, nft) + + burnedNftInputs.splice(burnedNftInputIndex, 1) + } + // Burn Activity + else if (isSelfTransaction && burnedNativeToken) { + activity = await ActivityTransaction.fromProcessedTransaction(wallet, { + action: ActivityAction.Burn, + processedTransaction, + wrappedOutput: basicOutput, + }) + } + // Consolidation Activity + else if (isSelfTransaction && ActivityBase.isConsolidation(basicOutput, processedTransaction)) { + activity = await ActivityConsolidation.fromProcessedTransaction(wallet, { + action: ActivityAction.Send, + processedTransaction, + wrappedOutput: basicOutput, + }) + } + // Send Activity + else { + activity = await ActivityTransaction.fromProcessedTransaction(wallet, { + action: ActivityAction.Send, + processedTransaction, + wrappedOutput: basicOutput, + }) + } + activities.push(activity) + } + return activities + } + + static async fromProcessedTransaction( + wallet: IWalletState, + { action, processedTransaction, wrappedOutput }: ActivityGenerationParameters, + fallbackAssetId?: string, + fallbackAmount?: number + ): Promise { + const { transactionId, direction, claimingData, time, inclusionState } = processedTransaction + + const specialStatus = SpecialStatus.Unclaimed // TODO: Fix this + const isHidden = false + const isAssetHidden = false + const outputId = wrappedOutput.outputId + const id = outputId || transactionId + const output = wrappedOutput.output as BasicOutput + const containsValue = await activityOutputContainsValue(wallet, wrappedOutput) + const amount = getAmountFromOutput(output) + const isShimmerClaiming = isShimmerClaimingTransaction(transactionId, get(activeProfileId)) + const tag = getTagFromOutput(output) + const metadata = getMetadataFromOutput(output) + const sendingInfo = processedTransaction.getSendingInformation(wallet, output) + const asyncData = await getAsyncDataFromOutput(output, outputId, claimingData, wallet) + const { parsedLayer2Metadata, destinationNetwork } = getLayer2ActivityInformation(metadata, sendingInfo) + const layer2Allowance = Number(parsedLayer2Metadata?.baseTokens ?? '0') + const gasBudget = Number(parsedLayer2Metadata?.gasBudget ?? '0') + const gasFee = layer2Allowance > 0 ? amount - layer2Allowance : 0 + + let { storageDeposit, giftedStorageDeposit } = await getStorageDepositFromOutput(output) + giftedStorageDeposit = action === ActivityAction.Burn ? 0 : giftedStorageDeposit + giftedStorageDeposit = gasBudget === 0 ? giftedStorageDeposit : 0 + + const baseTokenAmount = amount - storageDeposit - gasFee + const nativeToken = getNativeTokenFromOutput(output) + const assetId = fallbackAssetId ?? nativeToken?.id ?? getCoinType() + + let surplus: number | undefined = undefined + if (nativeToken) { + const storageDepositToDeduct = (storageDeposit > 0 ? storageDeposit : giftedStorageDeposit) ?? 0 + surplus = Number(output.amount) - storageDepositToDeduct + } + + let rawAmount: number + if (fallbackAmount === undefined) { + rawAmount = nativeToken ? Number(nativeToken?.amount) : baseTokenAmount + } else { + rawAmount = fallbackAmount + } + + // Note: we update the displayed storage deposit so it matches what was displayed in the send confirmation flow + // set the storage deposit to zero if the amount is greater than the storage deposit + // to improve the UX so the user doesnt think they need to pay the storage deposit + if (!nativeToken && !storageDeposit && rawAmount >= giftedStorageDeposit) { + storageDeposit = giftedStorageDeposit = 0 + } + + return new ActivityTransaction({ + isHidden, + id, + inclusionState, + specialStatus, + time, + transactionId, + direction, + action, + isAssetHidden, + containsValue, + outputId, + storageDeposit, + giftedStorageDeposit, + surplus, + isShimmerClaiming, + metadata, + tag, + asyncData, + destinationNetwork, + parsedLayer2Metadata, + isInternal: sendingInfo.isInternal, + subject: sendingInfo.subject, + rawAmount, + assetId + }) + } +} diff --git a/packages/shared/lib/core/wallet/types/activities/vesting-activity.type.ts b/packages/shared/lib/core/wallet/types/activities/vesting-activity.type.ts index 5b6aaafa803..4511b6cfc62 100644 --- a/packages/shared/lib/core/wallet/types/activities/vesting-activity.type.ts +++ b/packages/shared/lib/core/wallet/types/activities/vesting-activity.type.ts @@ -1,8 +1,86 @@ import { ActivityType } from '@core/wallet/enums' -import { BaseActivity } from './base-activity.type' +import { ActivityBase, ActivityBaseOptions, BaseActivity, SpecialStatus } from './base-activity.type' +import { activityOutputContainsValue, getAmountFromOutput, getAsyncDataFromOutput, getLayer2ActivityInformation, getMetadataFromOutput, getStorageDepositFromOutput, getTagFromOutput } from '../../utils' +import { ActivityGenerationParameters, IActivityGenerationParameters, IWalletState } from '../../interfaces' +import { BasicOutput } from '@iota/sdk/out/types' +import { localize } from 'shared/lib/core/i18n' +import { getCoinType } from 'shared/lib/core/profile' export type VestingActivity = BaseActivity & { type: ActivityType.Vesting rawAmount: number assetId: string } + +interface ActivityVestingOptions extends ActivityBaseOptions { + rawAmount: number + assetId: string +} + +export class ActivityVesting extends ActivityBase { + constructor(options: ActivityVestingOptions) { + super(options) + } + + subjectLocale(): string { + return localize('general.stardustGenesis') + } + + tileTitle(): string { + return 'general.vestingReward' + } + + static async fromProcessedTransaction( + wallet: IWalletState, + { action, processedTransaction, wrappedOutput }: ActivityGenerationParameters + ): Promise { + const { transactionId, direction, claimingData, time, inclusionState } = processedTransaction + + const specialStatus = SpecialStatus.Unclaimed // TODO: Fix this + const isHidden = false + const isAssetHidden = false + const containsValue = await activityOutputContainsValue(wallet, wrappedOutput) + + const outputId = wrappedOutput.outputId + const id = outputId || transactionId + + const output = wrappedOutput.output as BasicOutput + + const tag = getTagFromOutput(output) + const metadata = getMetadataFromOutput(output) + + const sendingInfo = processedTransaction.getSendingInformation(wallet, output) + const asyncData = await getAsyncDataFromOutput(output, outputId, claimingData, wallet) + + const { parsedLayer2Metadata, destinationNetwork } = getLayer2ActivityInformation(metadata, sendingInfo) + + const { storageDeposit, giftedStorageDeposit } = await getStorageDepositFromOutput(output) + const rawAmount = getAmountFromOutput(output) - storageDeposit + + const assetId = getCoinType() + + return new ActivityVesting({ + isHidden, + id, + inclusionState, + specialStatus, + time, + transactionId, + direction, + action, + isAssetHidden, + containsValue, + outputId, + assetId, + storageDeposit, + giftedStorageDeposit, + rawAmount, + metadata, + tag, + asyncData, + destinationNetwork, + ...sendingInfo, + ...parsedLayer2Metadata, + }) + } +} diff --git a/packages/shared/lib/core/wallet/utils/generateActivity/generateActivities.ts b/packages/shared/lib/core/wallet/utils/generateActivity/generateActivities.ts index c6904ab32c2..70cfaddd2f2 100644 --- a/packages/shared/lib/core/wallet/utils/generateActivity/generateActivities.ts +++ b/packages/shared/lib/core/wallet/utils/generateActivity/generateActivities.ts @@ -98,7 +98,7 @@ async function generateActivitiesFromProcessedTransactionsWithoutInputs( wrappedOutput, } switch (params.type) { - case ActivityType.Basic: + case ActivityType.Transaction: return generateSingleBasicActivity(wallet, params) case ActivityType.Governance: return generateSingleGovernanceActivity(wallet, params) diff --git a/packages/shared/lib/core/wallet/utils/generateActivity/generateSingleBasicActivity.ts b/packages/shared/lib/core/wallet/utils/generateActivity/generateSingleBasicActivity.ts index 40eeee55d6b..2a8e267f40b 100644 --- a/packages/shared/lib/core/wallet/utils/generateActivity/generateSingleBasicActivity.ts +++ b/packages/shared/lib/core/wallet/utils/generateActivity/generateSingleBasicActivity.ts @@ -2,7 +2,7 @@ import { isShimmerClaimingTransaction } from '@contexts/onboarding/stores' import { IWalletState } from '@core/wallet/interfaces' import { activeProfileId, getCoinType } from '@core/profile' import { IActivityGenerationParameters } from '@core/wallet/interfaces' -import { TransactionActivity } from '@core/wallet/types' +import { BaseActivity } from '@core/wallet/types' import { get } from 'svelte/store' import { activityOutputContainsValue, getNativeTokenFromOutput } from '..' import { ActivityAction, ActivityType } from '../../enums' @@ -22,7 +22,7 @@ export async function generateSingleBasicActivity( { action, processedTransaction, wrappedOutput }: IActivityGenerationParameters, fallbackAssetId?: string, fallbackAmount?: number -): Promise { +): Promise { const { transactionId, direction, claimingData, time, inclusionState } = processedTransaction const isHidden = false @@ -39,8 +39,6 @@ export async function generateSingleBasicActivity( const tag = getTagFromOutput(output) const metadata = getMetadataFromOutput(output) - const publicNote = '' - const sendingInfo = getSendingInformation(processedTransaction, output, wallet) const asyncData = await getAsyncDataFromOutput(output, outputId, claimingData, wallet) @@ -79,7 +77,7 @@ export async function generateSingleBasicActivity( } return { - type: ActivityType.Basic, + type: ActivityType.Transaction, isHidden, id, transactionId, @@ -95,7 +93,6 @@ export async function generateSingleBasicActivity( surplus, rawAmount, isShimmerClaiming, - publicNote, metadata, tag, assetId, diff --git a/packages/shared/lib/core/wallet/utils/generateActivity/helper/getActivityTypeFromOutput.ts b/packages/shared/lib/core/wallet/utils/generateActivity/helper/getActivityTypeFromOutput.ts index e012ec698e1..3d74aabdee7 100644 --- a/packages/shared/lib/core/wallet/utils/generateActivity/helper/getActivityTypeFromOutput.ts +++ b/packages/shared/lib/core/wallet/utils/generateActivity/helper/getActivityTypeFromOutput.ts @@ -20,5 +20,5 @@ export function getActivityTypeFromOutput(output: IWrappedOutput): ActivityType } } - return ActivityType.Basic + return ActivityType.Transaction } diff --git a/packages/shared/lib/core/wallet/utils/generateActivity/helper/getAsyncDataFromOutput.ts b/packages/shared/lib/core/wallet/utils/generateActivity/helper/getAsyncDataFromOutput.ts index 2b835d5a121..71eaeee304e 100644 --- a/packages/shared/lib/core/wallet/utils/generateActivity/helper/getAsyncDataFromOutput.ts +++ b/packages/shared/lib/core/wallet/utils/generateActivity/helper/getAsyncDataFromOutput.ts @@ -6,10 +6,11 @@ import { getAsyncStatus } from './getAsyncStatus' import { getStorageDepositFromOutput } from './getStorageDepositFromOutput' import { CommonOutput, Output } from '@iota/sdk/out/types' +// TODO: REFACTOR THIS export async function getAsyncDataFromOutput( output: Output, outputId: string, - claimingData: IClaimData, + claimingData: IClaimData | undefined, wallet: IWalletState ): Promise { const isAsync = isOutputAsync(output) diff --git a/packages/shared/lib/core/wallet/utils/generateActivity/helper/getGovernanceInfo.ts b/packages/shared/lib/core/wallet/utils/generateActivity/helper/getGovernanceInfo.ts index 26b28e054e2..27f33c2a032 100644 --- a/packages/shared/lib/core/wallet/utils/generateActivity/helper/getGovernanceInfo.ts +++ b/packages/shared/lib/core/wallet/utils/generateActivity/helper/getGovernanceInfo.ts @@ -13,6 +13,7 @@ interface IGovernanceInfo { participation?: IParticipation } +// TODO: Think about moving this to GovernanceActivity export function getGovernanceInfo(output: Output, inputs: IWrappedOutput[], metadata: string): IGovernanceInfo { /** * NOTE: If the output is NOT a participation output, then it doesn't have any voting power. diff --git a/packages/shared/lib/core/wallet/utils/generateActivity/helper/getSubjectFromActivity.ts b/packages/shared/lib/core/wallet/utils/generateActivity/helper/getSubjectFromActivity.ts index 0fab008e8dd..f94fa846e81 100644 --- a/packages/shared/lib/core/wallet/utils/generateActivity/helper/getSubjectFromActivity.ts +++ b/packages/shared/lib/core/wallet/utils/generateActivity/helper/getSubjectFromActivity.ts @@ -4,6 +4,7 @@ import { truncateString } from '@core/utils' import { ActivityType, SubjectType } from '@core/wallet/enums' import type { Activity, Subject } from '@core/wallet/types' +// TODO: Remove this export function getSubjectFromActivity(activity: Activity): Subject { if (activity.parsedLayer2Metadata) { return { @@ -23,7 +24,7 @@ export function getSubjectFromActivity(activity: Activity): Subject { export function getSubjectLocaleFromActivity(activity: Activity): string { const { subject } = activity - if (activity.type === ActivityType.Basic && activity?.isShimmerClaiming) { + if (activity.type === ActivityType.Transaction && activity?.isShimmerClaiming) { return localize('general.shimmerGenesis') } else if (activity.type === ActivityType.Vesting) { return localize('general.stardustGenesis') diff --git a/packages/shared/lib/core/wallet/utils/generateActivity/helper/updateActivityFromPartialActivity.ts b/packages/shared/lib/core/wallet/utils/generateActivity/helper/updateActivityFromPartialActivity.ts index 501c57dd498..e4bf2c3fcf6 100644 --- a/packages/shared/lib/core/wallet/utils/generateActivity/helper/updateActivityFromPartialActivity.ts +++ b/packages/shared/lib/core/wallet/utils/generateActivity/helper/updateActivityFromPartialActivity.ts @@ -2,7 +2,7 @@ import { ActivityType } from '@core/wallet/enums' import { Activity } from '@core/wallet/types' export function updateActivityFromPartialActivity(activity: Activity, partialData: Partial): void { - if (partialData.type === ActivityType.Basic && activity.type === ActivityType.Basic) { + if (partialData.type === ActivityType.Transaction && activity.type === ActivityType.Transaction) { Object.assign(activity, partialData) } else if (partialData.type === ActivityType.Foundry && activity.type === ActivityType.Foundry) { Object.assign(activity, partialData) diff --git a/packages/shared/lib/core/wallet/utils/getActivityTileTitle.ts b/packages/shared/lib/core/wallet/utils/getActivityTileTitle.ts index e1ce4e31372..9f99ea28308 100644 --- a/packages/shared/lib/core/wallet/utils/getActivityTileTitle.ts +++ b/packages/shared/lib/core/wallet/utils/getActivityTileTitle.ts @@ -5,7 +5,7 @@ export function getActivityTileTitle(activity: Activity): string { const { type, isInternal, direction, inclusionState, action } = activity const isConfirmed = inclusionState === InclusionState.Confirmed - if (activity.type === ActivityType.Basic && activity.isShimmerClaiming) { + if (activity.type === ActivityType.Transaction && activity.isShimmerClaiming) { return isConfirmed ? 'general.shimmerClaimed' : 'general.shimmerClaiming' } if (activity.type === ActivityType.Vesting) { diff --git a/packages/shared/lib/core/wallet/utils/isVisibleActivity.ts b/packages/shared/lib/core/wallet/utils/isVisibleActivity.ts index 5081f99e778..71b316b7065 100644 --- a/packages/shared/lib/core/wallet/utils/isVisibleActivity.ts +++ b/packages/shared/lib/core/wallet/utils/isVisibleActivity.ts @@ -16,6 +16,8 @@ import { StatusFilterOption, } from '@core/utils/enums/filters' +// TODO: Refactor this an clean up. + // Filters activities based on activity properties. If none of the conditionals are valid, then activity is shown. export function isVisibleActivity(activity: Activity): boolean { const filter = get(activityFilter) @@ -86,7 +88,7 @@ function isVisibleWithActiveRejectedFilter(activity: Activity, filter: ActivityF function isVisibleWithActiveAssetFilter(activity: Activity, filter: ActivityFilter): boolean { if (filter.asset.active && filter.asset.selected) { - if (activity.type !== ActivityType.Basic && activity.type !== ActivityType.Foundry) { + if (activity.type !== ActivityType.Transaction && activity.type !== ActivityType.Foundry) { return false } if (filter.asset.selected && activity.assetId !== filter.asset.selected) { @@ -98,7 +100,7 @@ function isVisibleWithActiveAssetFilter(activity: Activity, filter: ActivityFilt function isVisibleWithActiveAmountFilter(activity: Activity, filter: ActivityFilter): boolean { if (filter.amount.active) { - if (activity.type !== ActivityType.Basic && activity.type !== ActivityType.Foundry) return false + if (activity.type !== ActivityType.Transaction && activity.type !== ActivityType.Foundry) return false const asset = getAssetFromPersistedAssets(activity.assetId) if (!asset?.metadata) { return false @@ -250,14 +252,14 @@ function isVisibleWithActiveStatusFilter(activity: Activity, filter: ActivityFil } if ( filter.status.selected === StatusFilterOption.Claimed && - (activity.type === ActivityType.Basic || activity.type === ActivityType.Nft) && + (activity.type === ActivityType.Transaction || activity.type === ActivityType.Nft) && activity.asyncData?.asyncStatus === ActivityAsyncStatus.Claimed ) { return true } if ( filter.status.selected === StatusFilterOption.Unclaimed && - (activity.type === ActivityType.Basic || activity.type === ActivityType.Nft) && + (activity.type === ActivityType.Transaction || activity.type === ActivityType.Nft) && activity.asyncData?.asyncStatus === ActivityAsyncStatus.Unclaimed ) { return true diff --git a/packages/shared/lib/core/wallet/utils/outputs/getFormattedAmountFromActivity.ts b/packages/shared/lib/core/wallet/utils/outputs/getFormattedAmountFromActivity.ts index 6c947115c19..04c1d931280 100644 --- a/packages/shared/lib/core/wallet/utils/outputs/getFormattedAmountFromActivity.ts +++ b/packages/shared/lib/core/wallet/utils/outputs/getFormattedAmountFromActivity.ts @@ -10,7 +10,7 @@ export function getFormattedAmountFromActivity( if (!activity) return '' const metadata = getAssetFromPersistedAssets(activity.assetId)?.metadata const amount = formatTokenAmountBestMatch(activity.rawAmount, metadata) - if (activity.type === ActivityType.Basic) { + if (activity.type === ActivityType.Transaction) { return `${ (activity.direction === ActivityDirection.Outgoing || activity.action === ActivityAction.Burn) && signed ? '- ' diff --git a/packages/shared/lib/core/wallet/utils/send/sendUtils.ts b/packages/shared/lib/core/wallet/utils/send/sendUtils.ts index 8f0070bcbbc..569cf432a73 100644 --- a/packages/shared/lib/core/wallet/utils/send/sendUtils.ts +++ b/packages/shared/lib/core/wallet/utils/send/sendUtils.ts @@ -1,4 +1,4 @@ -import { NewTransactionDetails, NftActivity, Subject, TransactionActivity, VestingActivity } from '@core/wallet/types' +import { NewTransactionDetails, NftActivity, Subject, BaseActivity, VestingActivity } from '@core/wallet/types' import { NewTransactionType } from '@core/wallet/stores' import { ActivityAction, ActivityDirection, ActivityType, InclusionState } from '@core/wallet/enums' import { TimePeriod } from '@core/utils' @@ -44,9 +44,9 @@ export function rebuildActivity( visibleSurplus: number, isInternal: boolean, layer2Parameters: ILayer2Parameters -): Partial { +): Partial { return { - ...(transactionDetails as unknown as TransactionActivity | VestingActivity | NftActivity), + ...(transactionDetails as unknown as BaseActivity | VestingActivity | NftActivity), id: undefined, outputId: undefined, transactionId: undefined, @@ -61,7 +61,7 @@ export function rebuildActivity( isAssetHidden: false, giftedStorageDeposit: 0, surplus: visibleSurplus, - type: ActivityType.Basic, + type: ActivityType.Transaction, direction: ActivityDirection.Outgoing, inclusionState: InclusionState.Pending, action: ActivityAction.Send, diff --git a/packages/shared/lib/core/wallet/utils/transactions/activityOutputContainsValue.ts b/packages/shared/lib/core/wallet/utils/transactions/activityOutputContainsValue.ts index 8e0e5a73a7a..82aeb9b804e 100644 --- a/packages/shared/lib/core/wallet/utils/transactions/activityOutputContainsValue.ts +++ b/packages/shared/lib/core/wallet/utils/transactions/activityOutputContainsValue.ts @@ -10,7 +10,7 @@ export async function activityOutputContainsValue( wrappedOutput: IWrappedOutput ): Promise { const type = getActivityTypeFromOutput(wrappedOutput) - const typesToCheck = [ActivityType.Basic] + const typesToCheck = [ActivityType.Transaction] if (typesToCheck.includes(type)) { const output = wrappedOutput.output as BasicOutput