From 46fb9a1292543cbd8a7fc8753f81de00cf08753b Mon Sep 17 00:00:00 2001 From: Bran <52735957+brancoder@users.noreply.github.com> Date: Wed, 18 Oct 2023 10:15:58 +0200 Subject: [PATCH] Fix: Enhanced logic for identifying burned native tokens. (#7584) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: getBurnedNativeTokens logic * fix: preprocessing outgoing transactions and non-remainder Basic Output logic --------- Co-authored-by: Begoña Álvarez de la Cruz --- .../generateActivitiesFromBasicOutputs.ts | 38 ++++++++++++------- .../outputs/preprocessOutgoingTransaction.ts | 4 +- ...NonRemainderBasicOutputsFromTransaction.ts | 5 ++- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/packages/shared/lib/core/wallet/utils/generateActivity/generateActivitiesFromBasicOutputs.ts b/packages/shared/lib/core/wallet/utils/generateActivity/generateActivitiesFromBasicOutputs.ts index 3daca215788..57c3038d050 100644 --- a/packages/shared/lib/core/wallet/utils/generateActivity/generateActivitiesFromBasicOutputs.ts +++ b/packages/shared/lib/core/wallet/utils/generateActivity/generateActivitiesFromBasicOutputs.ts @@ -33,8 +33,7 @@ export async function generateActivitiesFromBasicOutputs( const burnedNftInputIndex = burnedNftInputs.findIndex( (input) => input.output.amount === basicOutput.output.amount ) - const burnedNativeToken = - burnedNftInputIndex < 0 ? getBurnedNativeTokens(basicOutput, processedTransaction) : undefined + const burnedNativeToken = burnedNftInputIndex < 0 ? getBurnedNativeTokens(processedTransaction) : undefined if (isSelfTransaction && burnedNftInputIndex >= 0) { const wrappedInput = burnedNftInputs[burnedNftInputIndex] @@ -105,26 +104,37 @@ function getBurnedNftInputs(processedTransaction: IProcessedTransaction): IWrapp } function getBurnedNativeTokens( - output: IWrappedOutput, processedTransaction: IProcessedTransaction -): { assetId: string; amount: number } { +): { 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 (processedTransaction.direction !== ActivityDirection.SelfTransaction) { - return null + return } const inputNativeTokens: { [key: string]: number } = getAllNativeTokensFromOutputs( processedTransaction.wrappedInputs ) - const outputNativeTokens: { [key: string]: number } = getAllNativeTokensFromOutputs([output]) - for (const inputNativeTokenId of Object.keys(inputNativeTokens)) { - if (!outputNativeTokens[inputNativeTokenId]) { - return { assetId: inputNativeTokenId, amount: inputNativeTokens[inputNativeTokenId] } - } + // No burned native tokens if input doesn't contain any native tokens + if (Object.keys(inputNativeTokens).length === 0) { + return + } - if (inputNativeTokens[inputNativeTokenId] > Number(outputNativeTokens[inputNativeTokenId])) { - const burnedAmount = inputNativeTokens[inputNativeTokenId] - Number(outputNativeTokens[inputNativeTokenId]) - return { assetId: inputNativeTokenId, amount: burnedAmount } - } + const outputNativeTokens: { [key: string]: number } = getAllNativeTokensFromOutputs(processedTransaction.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 } } } diff --git a/packages/shared/lib/core/wallet/utils/outputs/preprocessOutgoingTransaction.ts b/packages/shared/lib/core/wallet/utils/outputs/preprocessOutgoingTransaction.ts index 284bafa5518..095db8f47bc 100644 --- a/packages/shared/lib/core/wallet/utils/outputs/preprocessOutgoingTransaction.ts +++ b/packages/shared/lib/core/wallet/utils/outputs/preprocessOutgoingTransaction.ts @@ -1,5 +1,5 @@ import { IProcessedTransaction, IWrappedOutput } from '../../interfaces' -import { Output, RegularTransactionEssence, Transaction, UTXOInput } from '@iota/sdk/out/types' +import { Output, OutputType, RegularTransactionEssence, Transaction, UTXOInput } from '@iota/sdk/out/types' import { computeOutputId } from './computeOutputId' import { getOutputIdFromTransactionIdAndIndex } from './getOutputIdFromTransactionIdAndIndex' import { getDirectionFromOutgoingTransaction } from '../transactions' @@ -57,6 +57,6 @@ function convertTransactionOutputTypeToWrappedOutput( return { outputId, output: outputType, - remainder: false, + 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) } } diff --git a/packages/shared/lib/core/wallet/utils/transactions/getNonRemainderBasicOutputsFromTransaction.ts b/packages/shared/lib/core/wallet/utils/transactions/getNonRemainderBasicOutputsFromTransaction.ts index 72c335fdff6..70f70e32446 100644 --- a/packages/shared/lib/core/wallet/utils/transactions/getNonRemainderBasicOutputsFromTransaction.ts +++ b/packages/shared/lib/core/wallet/utils/transactions/getNonRemainderBasicOutputsFromTransaction.ts @@ -9,12 +9,13 @@ export function getNonRemainderBasicOutputsFromTransaction( accountAddressesWithOutputs: AddressWithOutputs[], direction: ActivityDirection ): IWrappedOutput[] { + const accountAddresses = accountAddressesWithOutputs.map((addressWithOutputs) => addressWithOutputs.address) + return wrappedOutputs.filter((outputData) => { const recipientAddress = getRecipientAddressFromOutput(outputData.output as CommonOutput) - const accountAddresses = accountAddressesWithOutputs.map((addressWithOutputs) => addressWithOutputs.address) if (direction === ActivityDirection.Incoming || direction === ActivityDirection.SelfTransaction) { - return accountAddresses.includes(recipientAddress) + return !outputData.remainder && accountAddresses.includes(recipientAddress) } else { return !accountAddresses.includes(recipientAddress) }