Skip to content

Commit

Permalink
fix: tx history: include account output in average txs (#8253)
Browse files Browse the repository at this point in the history
* feat: Sync SDK types

* feat: stop generating txs out of outputs

* fix: tx history: include account output in average txs

---------

Co-authored-by: marc2332 <[email protected]>
Co-authored-by: Begoña Álvarez de la Cruz <[email protected]>
  • Loading branch information
3 people authored Mar 25, 2024
1 parent b3633ee commit d5ed5c7
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 58 deletions.
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

0 comments on commit d5ed5c7

Please sign in to comment.