Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: tx history: include account output in average txs #8253

Merged
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { IWalletState } from '@core/wallet/interfaces'
import { preprocessOutgoingTransaction } from '../../utils'
import { preprocessIncomingTransaction, preprocessOutgoingTransaction } from '../../utils'
import { IProcessedTransaction } from '../../interfaces/processed-transaction.interface'

export async function preprocessTransactionsForWallet(wallet: IWalletState): Promise<IProcessedTransaction[]> {
const transactions = await wallet.transactions()

const processedTransactions: IProcessedTransaction[] = []
const transactions = await wallet.transactions()

for (const transaction of transactions) {
try {
Expand All @@ -15,5 +14,14 @@ export async function preprocessTransactionsForWallet(wallet: IWalletState): Pro
console.error(err)
}
}
const incomingTransactions = await wallet.incomingTransactions()
for (const incomingTransaction of incomingTransactions) {
try {
const processedTransaction = preprocessIncomingTransaction(incomingTransaction)
processedTransactions.push(processedTransaction)
} catch (err) {
console.error(err)
}
}
return processedTransactions
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,11 @@ async function generateActivitiesFromProcessedTransactionsWithInputs(
activities.push(...nftActivities)
}

const containsAccountInInputs = wrappedInputs.some((input) => input.output.type === OutputType.Account)
const containsAccountActivity =
outputs.some((output) => output.output.type === OutputType.Account) && !containsFoundryActivity
!containsAccountInInputs &&
outputs.some((output) => output.output.type === OutputType.Account) &&
!containsFoundryActivity
if (containsAccountActivity) {
const accountActivities = await generateActivitiesFromAccountOutputs(processedTransaction, wallet)
activities.push(...accountActivities)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,54 +29,56 @@ export async function generateActivitiesFromBasicOutputs(
const burnedNftInputs = getBurnedNftInputs(processedTransaction)
for (const basicOutput of basicOutputs) {
let activity: Activity
const isRemainder = basicOutput.remainder

const isSelfTransaction = processedTransaction.direction === ActivityDirection.SelfTransaction
const burnedNftInputIndex = burnedNftInputs.findIndex(
(input) => input.output.amount === basicOutput.output.amount
)
const burnedNativeToken = burnedNftInputIndex < 0 ? getBurnedNativeTokens(processedTransaction) : undefined

if (isSelfTransaction && burnedNftInputIndex >= 0) {
const wrappedInput = burnedNftInputs[burnedNftInputIndex]
const nftInput = wrappedInput.output as NftOutput
activity = await generateSingleNftActivity(
wallet,
{
action: ActivityAction.Burn,
if (!isRemainder) {
if (burnedNftInputIndex >= 0) {
const wrappedInput = burnedNftInputs[burnedNftInputIndex]
const nftInput = wrappedInput.output as NftOutput
activity = await generateSingleNftActivity(
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)
} else if (burnedNativeToken) {
activity = await generateSingleBasicActivity(
wallet,
{
action: ActivityAction.Burn,
processedTransaction,
wrappedOutput: basicOutput,
},
burnedNativeToken.assetId,
burnedNativeToken.amount
)
} else if (isConsolidation(basicOutput, processedTransaction)) {
activity = await generateSingleConsolidationActivity(wallet, {
action: ActivityAction.Send,
processedTransaction,
wrappedOutput: basicOutput,
},
getNftId(nftInput.nftId, wrappedInput.outputId)
)
const nft = buildNftFromNftOutput(wrappedInput, wallet.depositAddress, false)
addOrUpdateNftInAllWalletNfts(wallet.id, nft)

burnedNftInputs.splice(burnedNftInputIndex, 1)
} else if (isSelfTransaction && burnedNativeToken) {
activity = await generateSingleBasicActivity(
wallet,
{
action: ActivityAction.Burn,
})
} else {
activity = await generateSingleBasicActivity(wallet, {
action: ActivityAction.Send,
processedTransaction,
wrappedOutput: basicOutput,
},
burnedNativeToken.assetId,
burnedNativeToken.amount
)
} else if (isSelfTransaction && isConsolidation(basicOutput, processedTransaction)) {
activity = await generateSingleConsolidationActivity(wallet, {
action: ActivityAction.Send,
processedTransaction,
wrappedOutput: basicOutput,
})
} else {
activity = await generateSingleBasicActivity(wallet, {
action: ActivityAction.Send,
processedTransaction,
wrappedOutput: basicOutput,
})
})
}
activities.push(activity)
}
activities.push(activity)
}
return activities
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TransactionId, OutputId } from '@iota/sdk/out/types'
import { api } from '@core/api'

export function computeOutputId(id: TransactionId, index: number): Promise<OutputId> {
export function computeOutputId(id: TransactionId, index: number): OutputId {
return api.computeOutputId(id, index)
}
1 change: 1 addition & 0 deletions packages/shared/lib/core/wallet/utils/outputs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ export * from './preprocessOutgoingTransaction'
export * from './getOutputIdFromTransactionIdAndIndex'
export * from './getRequiredStorageDepositForMinimalBasicOutput'
export * from './getSerialNumberFromAccountAddress'
export * from './preprocessIncomingTransaction'
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { IProcessedTransaction, IWrappedOutput } from '../../interfaces'
import { Output, OutputType, OutputWithMetadata, TransactionWithMetadata, UTXOInput } from '@iota/sdk/out/types'
// import { computeOutputId } from './computeOutputId'
import { getOutputIdFromTransactionIdAndIndex } from './getOutputIdFromTransactionIdAndIndex'
import { ActivityDirection } from '../../enums'

export function preprocessIncomingTransaction(transaction: TransactionWithMetadata): IProcessedTransaction {
const regularTransactionEssence = transaction.payload.transaction
const transactionId = transaction?.transactionId?.toString()

const outputs = convertTransactionsOutputTypesToWrappedOutputs(transactionId, regularTransactionEssence.outputs)

const utxoInputs = regularTransactionEssence.inputs.map((i) => i as UTXOInput)
// const inputIds = utxoInputs.map((input) => {
// const transactionId = input.transactionId
// const transactionOutputIndex = input.transactionOutputIndex
// return computeOutputId(transactionId, transactionOutputIndex)
// })
// const inputs = await Promise.all(inputIds.map((inputId) => wallet.getOutput(inputId)))

return {
outputs: outputs,
transactionId,
direction: ActivityDirection.Incoming,
time: new Date(Number(transaction.timestamp)),
inclusionState: transaction.inclusionState,
wrappedInputs: [],
// wrappedInputs: <IWrappedOutput[]>inputs,
utxoInputs,
}
}

function convertTransactionsOutputTypesToWrappedOutputs(
transactionId: string,
outputTypes: Output[]
): IWrappedOutput[] {
return outputTypes.map((outputType, index) =>
convertTransactionOutputTypeToWrappedOutput(transactionId, index, outputType)
)
}

function convertTransactionOutputTypeToWrappedOutput(
transactionId: string,
index: number,
outputType: Output
): IWrappedOutput {
const outputId = getOutputIdFromTransactionIdAndIndex(transactionId, index)
OutputWithMetadata
return {
outputId,
output: outputType,
remainder:
index === 0 || (outputType.type !== OutputType.Basic && outputType.type !== OutputType.Account)
? false
: true, // when sending prepared output in the resulting transactions outputs array it will always be first output(index = 0)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IProcessedTransaction, IWrappedOutput } from '../../interfaces'
import { Output, OutputType, TransactionWithMetadata, UTXOInput } from '@iota/sdk/out/types'
import { Output, OutputType, OutputWithMetadata, TransactionWithMetadata, UTXOInput } from '@iota/sdk/out/types'
import { computeOutputId } from './computeOutputId'
import { getOutputIdFromTransactionIdAndIndex } from './getOutputIdFromTransactionIdAndIndex'
import { getDirectionFromOutgoingTransaction } from '../transactions'
Expand All @@ -14,15 +14,13 @@ export async function preprocessOutgoingTransaction(

const outputs = convertTransactionsOutputTypesToWrappedOutputs(transactionId, regularTransactionEssence.outputs)

const direction = getDirectionFromOutgoingTransaction(outputs, wallet.depositAddress)
const inputIds = await Promise.all(
regularTransactionEssence.inputs.map((input) => {
const _input = input as UTXOInput
const transactionId = _input.transactionId
const transactionOutputIndex = _input.transactionOutputIndex
return computeOutputId(transactionId, transactionOutputIndex)
})
)
const direction = getDirectionFromOutgoingTransaction(regularTransactionEssence.outputs, await wallet.address())
const utxoInputs = regularTransactionEssence.inputs.map((i) => i as UTXOInput)
const inputIds = utxoInputs.map((input) => {
const transactionId = input.transactionId
const transactionOutputIndex = input.transactionOutputIndex
return computeOutputId(transactionId, transactionOutputIndex)
})

const inputs = await Promise.all(inputIds.map((inputId) => wallet.getOutput(inputId)))

Expand Down Expand Up @@ -51,9 +49,13 @@ function convertTransactionOutputTypeToWrappedOutput(
outputType: Output
): IWrappedOutput {
const outputId = getOutputIdFromTransactionIdAndIndex(transactionId, index)
OutputWithMetadata
return {
outputId,
output: outputType,
remainder: index === 0 || outputType.type !== OutputType.Basic ? false : true, // when sending prepared output in the resulting transactions outputs array it will always be first output(index = 0)
remainder:
index === 0 || (outputType.type !== OutputType.Basic && outputType.type !== OutputType.Account)
? false
: true, // when sending prepared output in the resulting transactions outputs array it will always be first output(index = 0)
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { IWrappedOutput } from '../../interfaces'
import { getRecipientAddressFromOutput } from '../outputs/getRecipientAddressFromOutput'
import { ActivityDirection } from '@core/wallet/enums'
import { CommonOutput } from '@iota/sdk/out/types'
import { CommonOutput, Output } from '@iota/sdk/out/types'

export function getDirectionFromOutgoingTransaction(
wrappedOutputs: IWrappedOutput[],
outputs: Output[],
walletDepositAddress: string
): ActivityDirection {
// Check if any output is not destined for the wallet
const containsNonWalletRecipient = wrappedOutputs.some((outputData) => {
const outputRecipient = getRecipientAddressFromOutput(outputData.output as CommonOutput)
const containsNonWalletRecipient = outputs.some((output) => {
const outputRecipient = getRecipientAddressFromOutput(output as CommonOutput)
return walletDepositAddress !== outputRecipient
})

Expand Down
Loading