@@ -3,6 +3,7 @@ import { Logger } from 'ts-log';
3
3
import { NEVER , Observable , combineLatest , concat , distinctUntilChanged , map , of , switchMap } from 'rxjs' ;
4
4
import { PersistentCollectionTrackerSubject , txInEquals , utxoEquals } from './util' ;
5
5
import { RetryBackoffConfig } from 'backoff-rxjs' ;
6
+ import { SignedTx } from '@cardano-sdk/tx-construction' ;
6
7
import { TxInFlight , UtxoTracker } from './types' ;
7
8
import { WalletStores } from '../persistence' ;
8
9
import { coldObservableProvider } from '@cardano-sdk/util-rxjs' ;
@@ -17,6 +18,7 @@ export interface UtxoTrackerProps {
17
18
addresses$ : Observable < Cardano . PaymentAddress [ ] > ;
18
19
stores : Pick < WalletStores , 'utxo' | 'unspendableUtxo' > ;
19
20
transactionsInFlight$ : Observable < TxInFlight [ ] > ;
21
+ signedTransactions$ : Observable < SignedTx [ ] > ;
20
22
tipBlockHeight$ : Observable < Cardano . BlockNo > ;
21
23
retryBackoffConfig : RetryBackoffConfig ;
22
24
logger : Logger ;
@@ -65,6 +67,7 @@ export const createUtxoTracker = (
65
67
transactionsInFlight$,
66
68
retryBackoffConfig,
67
69
tipBlockHeight$,
70
+ signedTransactions$,
68
71
logger,
69
72
onFatalError
70
73
} : UtxoTrackerProps ,
@@ -79,15 +82,24 @@ export const createUtxoTracker = (
79
82
)
80
83
} : UtxoTrackerInternals = { }
81
84
) : UtxoTracker => {
82
- const total$ = combineLatest ( [ utxoSource$ , transactionsInFlight$ , addresses$ ] ) . pipe (
83
- map ( ( [ onChainUtxo , transactionsInFlight , ownAddresses ] ) => [
85
+ const total$ = combineLatest ( [ utxoSource$ , transactionsInFlight$ , signedTransactions$ , addresses$ ] ) . pipe (
86
+ map ( ( [ onChainUtxo , transactionsInFlight , signedTransactions , ownAddresses ] ) => [
84
87
...onChainUtxo . filter ( ( [ utxoTxIn ] ) => {
85
88
const utxoIsUsedInFlight = transactionsInFlight . some ( ( { body : { inputs } } ) =>
86
- inputs . some ( ( input ) => input . txId === utxoTxIn . txId && input . index === utxoTxIn . index )
89
+ inputs . some ( ( input ) => txInEquals ( input , utxoTxIn ) )
87
90
) ;
88
- utxoIsUsedInFlight &&
89
- logger . debug ( 'OnChain UTXO is already used in in-flight transaction. Excluding from total$.' , utxoTxIn ) ;
90
- return ! utxoIsUsedInFlight ;
91
+ const utxoIsUsedInSigned = signedTransactions . some ( ( { tx } ) =>
92
+ tx . body . inputs . some ( ( input ) => txInEquals ( input , utxoTxIn ) )
93
+ ) ;
94
+ ( utxoIsUsedInSigned || utxoIsUsedInFlight ) &&
95
+ logger . debug (
96
+ `OnChain UTXO is already used in ${
97
+ utxoIsUsedInFlight ? 'in-flight' : 'signed'
98
+ } transaction. Excluding from total$.`,
99
+ utxoTxIn
100
+ ) ;
101
+
102
+ return ! utxoIsUsedInFlight && ! utxoIsUsedInSigned ;
91
103
} ) ,
92
104
...transactionsInFlight . flatMap ( ( { body : { outputs } , id } , txInFlightIndex ) =>
93
105
outputs
@@ -109,6 +121,31 @@ export const createUtxoTracker = (
109
121
logger . debug ( 'New UTXO available from in-flight transactions. Including in total$.' , txIn ) ;
110
122
return [ txIn , txOut ] ;
111
123
} )
124
+ ) ,
125
+ ...signedTransactions . flatMap ( ( { tx : signedTx } , signedTxIndex ) =>
126
+ signedTx . body . outputs
127
+ . filter ( ( { address } , outputIndex ) => {
128
+ if ( ! ownAddresses . includes ( address ) ) {
129
+ return false ;
130
+ }
131
+
132
+ const alreadyConsumed = signedTransactions . some (
133
+ ( { tx : { body } } , i ) =>
134
+ signedTxIndex !== i &&
135
+ body . inputs . some ( ( input ) => txInEquals ( input , { index : outputIndex , txId : signedTx . id } ) )
136
+ ) ;
137
+
138
+ return ! alreadyConsumed ;
139
+ } )
140
+ . map ( ( txOut ) : Cardano . Utxo => {
141
+ const txIn : Cardano . HydratedTxIn = {
142
+ address : txOut . address , // not necessarily correct in multi-address wallet
143
+ index : signedTx . body . outputs . indexOf ( txOut ) ,
144
+ txId : signedTx . id
145
+ } ;
146
+ logger . debug ( 'New UTXO available from signed transactions. Including in total$.' , txIn ) ;
147
+ return [ txIn , txOut ] ;
148
+ } )
112
149
)
113
150
] ) ,
114
151
map ( ( utxo ) => {
0 commit comments