@@ -9,6 +9,7 @@ import { BigNumber } from 'bignumber.js';
99import { KnownCaip19Id , type Network } from '../../../constants/solana' ;
1010import type { SolanaTransaction } from '../../../types/solana' ;
1111import { parseTransactionNativeTransfers } from './parseTransactionNativeTransfers' ;
12+ import { parseTransactionNativeTransfersV2 } from './parseTransactionNativeTransfersV2' ;
1213import { parseTransactionSplTransfers } from './parseTransactionSplTransfers' ;
1314
1415/**
@@ -40,14 +41,37 @@ export function mapRpcTransaction({
4041
4142 const id = firstSignature as string ;
4243 const timestamp = Number ( transactionData . blockTime ) ;
44+ const status = evaluateTransactionStatus ( transactionData ) ;
4345
44- const nativeTransfers = parseTransactionNativeTransfers ( {
45- scope,
46- transactionData,
47- } ) ;
46+ let fees : Transaction [ 'fees' ] = [ ] ;
47+ let nativeFrom : Transaction [ 'from' ] = [ ] ;
48+ let nativeTo : Transaction [ 'to' ] = [ ] ;
4849
49- let { fees } = nativeTransfers ;
50- const { from : nativeFrom , to : nativeTo } = nativeTransfers ;
50+ if ( status === TransactionStatus . Failed ) {
51+ /**
52+ * If a transaction fails we don't really have access to meaningful `preBalances`
53+ * and `postBalances` values, since nothing really happened. To extract information
54+ * from it we have created this second version of the native transfers parser which
55+ * reads instructions directly, instead of relying on balance differences.
56+ */
57+ const nativeTransfers = parseTransactionNativeTransfersV2 ( {
58+ scope,
59+ transactionData,
60+ } ) ;
61+
62+ fees = nativeTransfers . fees ;
63+ nativeFrom = nativeTransfers . from ;
64+ nativeTo = nativeTransfers . to ;
65+ } else {
66+ const nativeTransfers = parseTransactionNativeTransfers ( {
67+ scope,
68+ transactionData,
69+ } ) ;
70+
71+ fees = nativeTransfers . fees ;
72+ nativeFrom = nativeTransfers . from ;
73+ nativeTo = nativeTransfers . to ;
74+ }
5175
5276 const { from : splFrom , to : splTo } = parseTransactionSplTransfers ( {
5377 scope,
@@ -59,6 +83,7 @@ export function mapRpcTransaction({
5983
6084 const type = evaluateTransactionType ( {
6185 address,
86+ status,
6287 from,
6388 to,
6489 } ) ;
@@ -84,12 +109,6 @@ export function mapRpcTransaction({
84109 fees = [ ] ;
85110 }
86111
87- const status =
88- transactionData . meta ?. err ||
89- ( transactionData . meta ?. status && 'Err' in transactionData . meta . status )
90- ? TransactionStatus . Failed
91- : TransactionStatus . Confirmed ;
92-
93112 const isLegitimate = evaluateTransactionLegitimacy ( {
94113 address,
95114 from,
@@ -102,6 +121,17 @@ export function mapRpcTransaction({
102121 return null ;
103122 }
104123
124+ /**
125+ * We cannot do this filtering earlier because we need to use the incomplete
126+ * mapped `from` and `to` arrays to determine the transaction's legitimacy.
127+ * For failed transactions, we need to first check if they are not spam before
128+ * finally clearing out what we had mapped to `from` and `to`.
129+ */
130+ if ( status === TransactionStatus . Failed ) {
131+ from = [ ] ;
132+ to = [ ] ;
133+ }
134+
105135 return {
106136 id,
107137 account : address ,
@@ -121,25 +151,50 @@ export function mapRpcTransaction({
121151 } ;
122152}
123153
154+ /**
155+ * Evaluates the status of a transaction based on the transaction data.
156+ * @param transactionData - The transaction data.
157+ * @returns The status of the transaction.
158+ */
159+ function evaluateTransactionStatus (
160+ transactionData : SolanaTransaction ,
161+ ) : TransactionStatus {
162+ const isError =
163+ transactionData . meta ?. err ||
164+ ( transactionData . meta ?. status && 'Err' in transactionData . meta . status ) ;
165+
166+ const status = isError
167+ ? TransactionStatus . Failed
168+ : TransactionStatus . Confirmed ;
169+
170+ return status ;
171+ }
172+
124173/**
125174 * Evaluates the type of transaction based on the address and the from and to items.
126175 * @param params - The options object.
127176 * @param params.address - The address of the user.
128177 * @param params.from - The from items.
129178 * @param params.to - The to items.
179+ * @param params.status - The status of the transaction.
130180 * @returns The type of transaction.
131181 */
132182function evaluateTransactionType ( {
133183 address,
184+ status,
134185 from,
135186 to,
136187} : {
137188 address : Address ;
189+ status : TransactionStatus ;
138190 from : Transaction [ 'from' ] ;
139191 to : Transaction [ 'to' ] ;
140192} ) : TransactionType {
141- if ( from . length === 0 || to . length === 0 ) {
142- // if we are unable to determine the type of transaction, we should set it to unknown
193+ if (
194+ from . length === 0 ||
195+ to . length === 0 ||
196+ status === TransactionStatus . Failed
197+ ) {
143198 return TransactionType . Unknown ;
144199 }
145200
@@ -203,13 +258,13 @@ function passesSOLAmountThresholdCheck({
203258 to,
204259} : {
205260 address : Address ;
206- to : Transaction [ 'to ' ] ;
261+ to : Transaction [ 'from ' ] ;
207262} ) : boolean {
208263 const { hasReceivedSOL, receivedSOLAmount } = to . reduce (
209264 ( acc , toItem ) => {
210265 if (
211266 toItem . address === address &&
212- toItem . asset ?. fungible && // Use optional chaining here
267+ toItem . asset ?. fungible &&
213268 ( toItem . asset . type === KnownCaip19Id . SolMainnet ||
214269 toItem . asset . type === KnownCaip19Id . SolDevnet )
215270 ) {
0 commit comments