@stellar-expert/ui-framework 1.16.7 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CLAUDE.md +35 -0
  2. package/README.md +1125 -3
  3. package/account/account-address.js +127 -127
  4. package/account/available-balance.js +22 -22
  5. package/account/identicon.js +90 -90
  6. package/account/signer-key.js +65 -64
  7. package/api/explorer-api-hooks.js +209 -202
  8. package/api/explorer-api-paginated-list-hooks.js +441 -440
  9. package/api/explorer-batch-info-loader.js +111 -85
  10. package/api/explorer-tx-api.js +28 -28
  11. package/api/ledger-stream.js +15 -0
  12. package/asset/amount.js +56 -56
  13. package/asset/asset-icon.js +41 -41
  14. package/asset/asset-issuer.js +21 -21
  15. package/asset/asset-link.js +107 -107
  16. package/asset/asset-list-hooks.js +59 -59
  17. package/asset/asset-meta-hooks.js +88 -88
  18. package/asset/asset-selector.js +72 -71
  19. package/basic-styles/base.scss +50 -50
  20. package/basic-styles/grid.scss +1 -1
  21. package/basic-styles/table.scss +1 -1
  22. package/claimable-balance/claimable-balance-claimants.js +5 -0
  23. package/contract/contract-api.js +15 -0
  24. package/contract/invocation-info-view.js +10 -2
  25. package/contract/sc-val.js +24 -0
  26. package/controls/button-group.js +25 -19
  27. package/controls/button-group.scss +46 -46
  28. package/controls/button.js +93 -78
  29. package/controls/button.scss +173 -173
  30. package/controls/code-block.js +42 -34
  31. package/controls/code-block.scss +71 -71
  32. package/controls/dropdown.js +318 -287
  33. package/controls/dropdown.scss +159 -159
  34. package/controls/external-link.js +10 -4
  35. package/controls/info-tooltip.js +23 -16
  36. package/controls/slider.js +29 -19
  37. package/controls/tabs.js +94 -94
  38. package/controls/tabs.scss +70 -70
  39. package/controls/tooltip.js +244 -240
  40. package/controls/tooltip.scss +116 -116
  41. package/controls/update-highlighter.js +32 -27
  42. package/controls/update-highlighter.scss +7 -7
  43. package/date/date-selector.js +56 -54
  44. package/date/elapsed-time.js +28 -21
  45. package/dex/price-dynamic.js +53 -44
  46. package/dex/price-dynamic.scss +33 -33
  47. package/directory/directory-hooks.js +109 -97
  48. package/effect/effect-description.js +5 -3
  49. package/errors/error-boundary.js +110 -97
  50. package/horizon/horizon-account-helpers.js +104 -104
  51. package/horizon/horizon-ledger-helpers.js +35 -35
  52. package/horizon/horizon-trades-helper.js +88 -88
  53. package/horizon/horizon-transaction-helpers.js +36 -36
  54. package/index.d.ts +1241 -0
  55. package/interaction/accordion.js +43 -35
  56. package/interaction/autofocus.js +13 -9
  57. package/interaction/block-select.js +64 -53
  58. package/interaction/block-select.scss +21 -21
  59. package/interaction/copy-to-clipboard.js +25 -18
  60. package/interaction/inline-progress.js +20 -15
  61. package/interaction/qr-code.js +34 -34
  62. package/interaction/responsive.js +20 -20
  63. package/interaction/spoiler.js +52 -39
  64. package/interaction/spoiler.scss +6 -6
  65. package/interaction/theme-selector.js +13 -10
  66. package/ledger/ledger-entry-href-formatter.js +4 -3
  67. package/ledger/ledger-entry-link.js +93 -58
  68. package/ledger/ledger-info-parser.js +28 -0
  69. package/meta/page-meta-tags.js +243 -238
  70. package/module/dynamic-module.js +47 -47
  71. package/package.json +3 -2
  72. package/state/on-screen-hooks.js +22 -22
  73. package/state/page-visibility-helpers.js +29 -16
  74. package/state/page-visibility-hooks.js +13 -11
  75. package/state/screen-orientation-hooks.js +19 -15
  76. package/state/state-hooks.js +76 -76
  77. package/state/stellar-network-hooks.js +56 -44
  78. package/state/theme.js +29 -28
  79. package/stellar/key-type.js +92 -91
  80. package/stellar/ledger-generic-id.js +39 -39
  81. package/stellar/signature-hint-utils.js +65 -65
  82. package/toast/toast-notifications-block.js +59 -59
  83. package/toast/toast-notifications.scss +1 -1
  84. package/tx/op-description-view.js +84 -81
  85. package/tx/op-icon.js +98 -98
  86. package/tx/parser/op-balance-changes.js +66 -66
  87. package/tx/parser/op-descriptor.js +51 -48
  88. package/tx/parser/tx-details-parser.js +81 -81
  89. package/tx/parser/tx-matcher.js +371 -371
  90. package/tx/parser/type-filter-matcher.js +126 -126
  91. package/tx/tx-list-hooks.js +32 -18
  92. package/tx/tx-operations-list.js +117 -116
@@ -1,372 +1,372 @@
1
- import {LiquidityPoolAsset} from '@stellar/stellar-base'
2
- import {AssetDescriptor} from '@stellar-expert/asset-descriptor'
3
- import {TypeFilterMatcher} from './type-filter-matcher'
4
-
5
- export default class TxMatcher {
6
- constructor(filters, skipUnrelated = false) {
7
- const matcherFilters = {}
8
- if (filters) {
9
- for (const key of ['account', 'source', 'destination', 'asset', 'src_asset', 'dest_asset', 'offer', 'pool']) {
10
- const values = filters[key]
11
- if (!values?.length)
12
- continue
13
- matcherFilters[key] = values
14
- this.noFilters = false
15
- }
16
- this.typeMatcher = new TypeFilterMatcher(filters.type)
17
- }
18
- this.filters = matcherFilters
19
- this.skipUnrelated = skipUnrelated
20
- }
21
-
22
- /**
23
- * @type {{[type], [account], [source], [destination], [asset], [src_asset], [dest_asset], [offer], [pool]}}
24
- * @private
25
- */
26
- filters
27
- /**
28
- * @type {Boolean}
29
- * @private
30
- */
31
- noFilters = true
32
- /**
33
- * @type {Boolean}
34
- * @private
35
- */
36
- skipUnrelated
37
- /**
38
- * @type {TypeFilterMatcher}
39
- * @private
40
- */
41
- typeMatcher
42
-
43
- /**
44
- * @param {OperationDescriptor} od
45
- * @return {Boolean}
46
- */
47
- matchOperation(od) {
48
- if (this.noFilters)
49
- return true
50
- if (!this.typeMatcher?.match(od.operation.type))
51
- return false
52
- if (!this.matchOperationProps(od))
53
- return false
54
- return true
55
- }
56
-
57
- /**
58
- * @param {TransactionBase} tx
59
- * @return {Boolean}
60
- */
61
- matchTxFee(tx) {
62
- if (!this.filter.asset?.includes('XLM'))
63
- return false
64
- const feeSource = tx.feeSource ? tx.feeSource : tx.source
65
- if (this.filter.account?.includes(feeSource))
66
- return true
67
- return !this.skipUnrelated
68
- }
69
-
70
- /**
71
- * @param {OperationDescriptor} od
72
- * @return {Boolean}
73
- * @private
74
- */
75
- matchOperationProps(od) {
76
- const matchingProps = this.prepareOperationMatchingProps(od)
77
- for (const [key, values] of Object.entries(this.filters)) {
78
- const options = matchingProps[key]
79
- if (!options)
80
- continue
81
- for (const value of values)
82
- if (options.has(value))
83
- return true
84
- }
85
- return false
86
- }
87
-
88
- /**
89
- * @param {OperationDescriptor} od
90
- * @returns {{[account], [source], [destination], [asset], [src_asset], [dest_asset], [offer], [pool]}}
91
- * @private
92
- */
93
- prepareOperationMatchingProps(od) {
94
- const {filters, skipUnrelated} = this
95
- const {operation: op} = od
96
- const matchingProps = {}
97
- //process accounts
98
- if (filters.account || filters.destination) {
99
- switch (op.type) {
100
- case 'createAccount':
101
- case 'accountMerge':
102
- case 'payment':
103
- case 'pathPaymentStrictSend':
104
- case 'pathPaymentStrictReceive':
105
- matchingProps.account = new Set([op.destination])
106
- matchingProps.destination = new Set([op.destination])
107
- break
108
- case 'inflation':
109
- if (od.successful) {
110
- const payouts = op.effects.filter(e => e.type === 'accountCredited').map(e => e.source)
111
- if (payouts.length) {
112
- matchingProps.account = new Set(payouts)
113
- matchingProps.destination = new Set(payouts)
114
- }
115
- }
116
- break
117
- case 'setOptions':
118
- matchingProps.account = new Set()
119
- if (!skipUnrelated) {
120
- const optionsRelated = [op.inflationDest, op.signer?.key].filter(acc => !!acc)
121
- if (optionsRelated.length) {
122
- matchingProps.account.add(optionsRelated)
123
- }
124
- }
125
- for (const e of (op.effects || []))
126
- if (e.prevSponsor) {
127
- matchingProps.account.add(e.prevSponsor)
128
- }
129
- break
130
- case 'allowTrust':
131
- case 'setTrustLineFlags':
132
- matchingProps.account = new Set([op.trustor])
133
- matchingProps.destination = new Set([op.trustor])
134
- break
135
- case 'createClaimableBalance':
136
- if (!skipUnrelated) {
137
- const indirect = op.claimants.map(c => c.destination)
138
- if (indirect.length) {
139
- matchingProps.account = new Set(...indirect)
140
- matchingProps.destination = new Set(...indirect)
141
- }
142
- }
143
- break
144
- case 'beginSponsoringFutureReserves':
145
- matchingProps.account = new Set([op.sponsoredId])
146
- matchingProps.destination = new Set([op.sponsoredId])
147
- break
148
- case 'revokeAccountSponsorship':
149
- case 'revokeTrustlineSponsorship':
150
- case 'revokeOfferSponsorship':
151
- case 'revokeDataSponsorship':
152
- case 'revokeClaimableBalanceSponsorship':
153
- case 'revokeLiquidityPoolSponsorship':
154
- case 'revokeSignerSponsorship':
155
- case 'revokeSponsorship':
156
- const revokedAccounts = [op.account, op.signer?.key, op.seller].filter(a => !!a)
157
- if (revokedAccounts.length) {
158
- matchingProps.account = new Set(revokedAccounts)
159
- matchingProps.destination = new Set(revokedAccounts)
160
- }
161
- break
162
- case 'clawback':
163
- matchingProps.account = new Set([op.from])
164
- matchingProps.destination = new Set([op.from])
165
- break
166
- case 'invokeHostFunction':
167
- matchingProps.account = new Set()
168
- for (const effect of op.effects) {
169
- matchingProps.account.add(effect.source)
170
- if (effect.issuer !== undefined) {
171
- matchingProps.account.add(effect.issuer)
172
- }
173
- if (effect.contract !== undefined) {
174
- matchingProps.account.add(effect.contract)
175
- }
176
- }
177
- break
178
- case 'liquidityPoolDeposit':
179
- case 'liquidityPoolWithdraw':
180
- case 'claimClaimableBalance':
181
- case 'changeTrust':
182
- case 'manageBuyOffer':
183
- case 'manageSellOffer':
184
- case 'createPassiveSellOffer':
185
- case 'manageData':
186
- case 'bumpSequence':
187
- case 'endSponsoringFutureReserves':
188
- case 'clawbackClaimableBalance':
189
- case 'extendFootprintTtl':
190
- case 'restoreFootprint':
191
- matchingProps.account = new Set()
192
- break
193
- default:
194
- throw new Error('Unsupported op type: ' + op.type)
195
- }
196
- if (!matchingProps.account) {
197
- matchingProps.account = new Set()
198
- }
199
- if (!matchingProps.account.has(op.source)) {
200
- matchingProps.account.add(op.source)
201
- }
202
- }
203
- //add op source account
204
- matchingProps.source = new Set([op.source])
205
- //process assets
206
- if (filters.asset || filters.src_asset || filters.dest_asset)
207
- switch (op.type) {
208
- case 'createAccount':
209
- case 'accountMerge':
210
- case 'inflation':
211
- matchingProps.asset = new Set(['XLM'])
212
- matchingProps.src_asset = new Set(['XLM'])
213
- matchingProps.dest_asset = new Set(['XLM'])
214
- break
215
- case 'payment':
216
- case 'clawback':
217
- case 'setTrustLineFlags':
218
- case 'createClaimableBalance':
219
- case 'revokeAccountSponsorship':
220
- case 'revokeTrustlineSponsorship':
221
- case 'revokeOfferSponsorship':
222
- case 'revokeDataSponsorship':
223
- case 'revokeClaimableBalanceSponsorship':
224
- case 'revokeLiquidityPoolSponsorship':
225
- case 'revokeSignerSponsorship':
226
- case 'revokeSponsorship':
227
- if (op.asset) {
228
- const a = [AssetDescriptor.parse(op.asset).toFQAN()]
229
- matchingProps.asset = new Set(a)
230
- matchingProps.src_asset = new Set(a)
231
- matchingProps.dest_asset = new Set(a)
232
- }
233
- break
234
- case 'changeTrust': {
235
- const a = [AssetDescriptor.parse(op.line).toFQAN()]
236
- matchingProps.dest_asset = new Set(a)
237
- if (op.line instanceof LiquidityPoolAsset) {
238
- a.push(AssetDescriptor.parse(op.line.assetA).toFQAN())
239
- a.push(AssetDescriptor.parse(op.line.assetB).toFQAN())
240
- }
241
- matchingProps.asset = new Set(a)
242
- matchingProps.src_asset = new Set(a)
243
- }
244
- break
245
- case 'pathPaymentStrictSend':
246
- case 'pathPaymentStrictReceive': {
247
- const src = AssetDescriptor.parse(op.sendAsset).toFQAN()
248
- const dest = AssetDescriptor.parse(op.destAsset).toFQAN()
249
- matchingProps.asset = new Set([src, dest, ...op.path.map(a => AssetDescriptor.parse(a).toFQAN())])
250
- matchingProps.src_asset = new Set([src])
251
- matchingProps.dest_asset = new Set([dest])
252
- }
253
- break
254
- case 'manageBuyOffer':
255
- case 'manageSellOffer':
256
- case 'createPassiveSellOffer': {
257
- const selling = AssetDescriptor.parse(op.selling).toFQAN()
258
- const buying = AssetDescriptor.parse(op.buying).toFQAN()
259
- matchingProps.asset = new Set([selling, buying])
260
- matchingProps.src_asset = new Set([selling])
261
- matchingProps.dest_asset = new Set([buying])
262
- }
263
- break
264
- case 'allowTrust': {
265
- const asset = AssetDescriptor.parse({code: op.assetCode, issuer: op.source}).toFQAN()
266
- matchingProps.asset = new Set([asset])
267
- matchingProps.src_asset = new Set([asset])
268
- matchingProps.dest_asset = new Set([asset])
269
- }
270
- break
271
- case 'liquidityPoolDeposit':
272
- case 'liquidityPoolWithdraw':
273
- if (od.successful) { // TODO: find a workaround for this (operations won't match for failed transactions)
274
- const poolUpdatedEffect = op.effects.find(e => e.type === 'liquidityPoolUpdated')
275
- const assets = poolUpdatedEffect.reserves.map(r => AssetDescriptor.parse(r.asset).toFQAN())
276
- matchingProps.asset = new Set(assets)
277
- matchingProps.src_asset = new Set(assets)
278
- matchingProps.dest_asset = new Set(assets)
279
- }
280
- break
281
- case 'invokeHostFunction':
282
- matchingProps.asset = new Set()
283
- matchingProps.src_asset = new Set()
284
- matchingProps.dest_asset = new Set()
285
- const assets = new Set()
286
- for (const effect of op.effects) {
287
- if (effect.asset !== undefined) {
288
- matchingProps.asset.add(effect.asset)
289
- matchingProps.src_asset.add(effect.asset)
290
- matchingProps.dest_asset.add(effect.asset)
291
- }
292
- }
293
- break
294
- }
295
-
296
- //process liquidity pools and offers
297
- if (filters.pool || filters.offer) {
298
- switch (op.type) {
299
- case 'liquidityPoolDeposit':
300
- case 'liquidityPoolWithdraw':
301
- case 'revokeLiquidityPoolSponsorship':
302
- matchingProps.pool = new Set([op.liquidityPoolId])
303
- break
304
- case 'changeTrust':
305
- case 'revokeTrustlineSponsorship':
306
- const asset = AssetDescriptor.parse(op.line || op.asset).toFQAN()
307
- if (asset.length === 64 && !asset.includes('-')) {
308
- matchingProps.pool = new Set([asset])
309
- }
310
- break
311
- case 'manageBuyOffer':
312
- case 'manageSellOffer':
313
- case 'createPassiveSellOffer':
314
- case 'pathPaymentStrictSend':
315
- case 'pathPaymentStrictReceive':
316
- if (od.successful) {
317
- const offers = []
318
- const pools = []
319
- for (const e of op.effects)
320
- if (e.type === 'trade') {
321
- if (e.pool) {
322
- pools.push(e.pool)
323
- }
324
- if (e.offer) {
325
- offers.push(e.offer)
326
- if (!skipUnrelated) {
327
- if (!matchingProps.account) {
328
- matchingProps.account = new Set()
329
- }
330
- if (!matchingProps.account.has(e.seller)) {
331
- matchingProps.account.add(e.seller)
332
- }
333
- }
334
- }
335
- }
336
- if (offers.length) {
337
- matchingProps.offer = new Set(offers)
338
- }
339
- if (pools.length) {
340
- matchingProps.pool = new Set(pools)
341
- }
342
- }
343
- if (op.offerId) {
344
- if (!matchingProps.offer) {
345
- matchingProps.offer = new Set()
346
- }
347
- if (!matchingProps.offer.has(op.offerId)) {
348
- matchingProps.offer.add(op.offerId)
349
- }
350
- }
351
- break
352
- case 'revokeOfferSponsorship':
353
- matchingProps.offer = new Set([op.offerId])
354
- break
355
- case 'clawback':
356
- matchingProps.account = new Set([op.from])
357
- matchingProps.destination = new Set([op.from])
358
- break
359
- }
360
- }
361
- //TODO: handle trustline authorization effects (deauthorization may cancel offers and withdraw liquidity pool stakes)
362
- return matchingProps
363
- }
364
- }
365
-
366
- /**
367
- * Callback for matching operation by type
368
- * @callback TypeMatcherCallback
369
- * @param {OperationDescriptor} op - Operation descriptor to match
370
- * @return {Boolean}
371
- * @internal
1
+ import {LiquidityPoolAsset} from '@stellar/stellar-base'
2
+ import {AssetDescriptor} from '@stellar-expert/asset-descriptor'
3
+ import {TypeFilterMatcher} from './type-filter-matcher'
4
+
5
+ export default class TxMatcher {
6
+ constructor(filters, skipUnrelated = false) {
7
+ const matcherFilters = {}
8
+ if (filters) {
9
+ for (const key of ['account', 'source', 'destination', 'asset', 'src_asset', 'dest_asset', 'offer', 'pool']) {
10
+ const values = filters[key]
11
+ if (!values?.length)
12
+ continue
13
+ matcherFilters[key] = values
14
+ this.noFilters = false
15
+ }
16
+ this.typeMatcher = new TypeFilterMatcher(filters.type)
17
+ }
18
+ this.filters = matcherFilters
19
+ this.skipUnrelated = skipUnrelated
20
+ }
21
+
22
+ /**
23
+ * @type {{[type], [account], [source], [destination], [asset], [src_asset], [dest_asset], [offer], [pool]}}
24
+ * @private
25
+ */
26
+ filters
27
+ /**
28
+ * @type {boolean}
29
+ * @private
30
+ */
31
+ noFilters = true
32
+ /**
33
+ * @type {boolean}
34
+ * @private
35
+ */
36
+ skipUnrelated
37
+ /**
38
+ * @type {TypeFilterMatcher}
39
+ * @private
40
+ */
41
+ typeMatcher
42
+
43
+ /**
44
+ * @param {OperationDescriptor} od
45
+ * @return {boolean}
46
+ */
47
+ matchOperation(od) {
48
+ if (this.noFilters)
49
+ return true
50
+ if (!this.typeMatcher?.match(od.operation.type))
51
+ return false
52
+ if (!this.matchOperationProps(od))
53
+ return false
54
+ return true
55
+ }
56
+
57
+ /**
58
+ * @param {TransactionBase} tx
59
+ * @return {boolean}
60
+ */
61
+ matchTxFee(tx) {
62
+ if (!this.filter.asset?.includes('XLM'))
63
+ return false
64
+ const feeSource = tx.feeSource ? tx.feeSource : tx.source
65
+ if (this.filter.account?.includes(feeSource))
66
+ return true
67
+ return !this.skipUnrelated
68
+ }
69
+
70
+ /**
71
+ * @param {OperationDescriptor} od
72
+ * @return {boolean}
73
+ * @private
74
+ */
75
+ matchOperationProps(od) {
76
+ const matchingProps = this.prepareOperationMatchingProps(od)
77
+ for (const [key, values] of Object.entries(this.filters)) {
78
+ const options = matchingProps[key]
79
+ if (!options)
80
+ continue
81
+ for (const value of values)
82
+ if (options.has(value))
83
+ return true
84
+ }
85
+ return false
86
+ }
87
+
88
+ /**
89
+ * @param {OperationDescriptor} od
90
+ * @returns {{[account], [source], [destination], [asset], [src_asset], [dest_asset], [offer], [pool]}}
91
+ * @private
92
+ */
93
+ prepareOperationMatchingProps(od) {
94
+ const {filters, skipUnrelated} = this
95
+ const {operation: op} = od
96
+ const matchingProps = {}
97
+ //process accounts
98
+ if (filters.account || filters.destination) {
99
+ switch (op.type) {
100
+ case 'createAccount':
101
+ case 'accountMerge':
102
+ case 'payment':
103
+ case 'pathPaymentStrictSend':
104
+ case 'pathPaymentStrictReceive':
105
+ matchingProps.account = new Set([op.destination])
106
+ matchingProps.destination = new Set([op.destination])
107
+ break
108
+ case 'inflation':
109
+ if (od.successful) {
110
+ const payouts = op.effects.filter(e => e.type === 'accountCredited').map(e => e.source)
111
+ if (payouts.length) {
112
+ matchingProps.account = new Set(payouts)
113
+ matchingProps.destination = new Set(payouts)
114
+ }
115
+ }
116
+ break
117
+ case 'setOptions':
118
+ matchingProps.account = new Set()
119
+ if (!skipUnrelated) {
120
+ const optionsRelated = [op.inflationDest, op.signer?.key].filter(acc => !!acc)
121
+ if (optionsRelated.length) {
122
+ matchingProps.account.add(optionsRelated)
123
+ }
124
+ }
125
+ for (const e of (op.effects || []))
126
+ if (e.prevSponsor) {
127
+ matchingProps.account.add(e.prevSponsor)
128
+ }
129
+ break
130
+ case 'allowTrust':
131
+ case 'setTrustLineFlags':
132
+ matchingProps.account = new Set([op.trustor])
133
+ matchingProps.destination = new Set([op.trustor])
134
+ break
135
+ case 'createClaimableBalance':
136
+ if (!skipUnrelated) {
137
+ const indirect = op.claimants.map(c => c.destination)
138
+ if (indirect.length) {
139
+ matchingProps.account = new Set(...indirect)
140
+ matchingProps.destination = new Set(...indirect)
141
+ }
142
+ }
143
+ break
144
+ case 'beginSponsoringFutureReserves':
145
+ matchingProps.account = new Set([op.sponsoredId])
146
+ matchingProps.destination = new Set([op.sponsoredId])
147
+ break
148
+ case 'revokeAccountSponsorship':
149
+ case 'revokeTrustlineSponsorship':
150
+ case 'revokeOfferSponsorship':
151
+ case 'revokeDataSponsorship':
152
+ case 'revokeClaimableBalanceSponsorship':
153
+ case 'revokeLiquidityPoolSponsorship':
154
+ case 'revokeSignerSponsorship':
155
+ case 'revokeSponsorship':
156
+ const revokedAccounts = [op.account, op.signer?.key, op.seller].filter(a => !!a)
157
+ if (revokedAccounts.length) {
158
+ matchingProps.account = new Set(revokedAccounts)
159
+ matchingProps.destination = new Set(revokedAccounts)
160
+ }
161
+ break
162
+ case 'clawback':
163
+ matchingProps.account = new Set([op.from])
164
+ matchingProps.destination = new Set([op.from])
165
+ break
166
+ case 'invokeHostFunction':
167
+ matchingProps.account = new Set()
168
+ for (const effect of op.effects) {
169
+ matchingProps.account.add(effect.source)
170
+ if (effect.issuer !== undefined) {
171
+ matchingProps.account.add(effect.issuer)
172
+ }
173
+ if (effect.contract !== undefined) {
174
+ matchingProps.account.add(effect.contract)
175
+ }
176
+ }
177
+ break
178
+ case 'liquidityPoolDeposit':
179
+ case 'liquidityPoolWithdraw':
180
+ case 'claimClaimableBalance':
181
+ case 'changeTrust':
182
+ case 'manageBuyOffer':
183
+ case 'manageSellOffer':
184
+ case 'createPassiveSellOffer':
185
+ case 'manageData':
186
+ case 'bumpSequence':
187
+ case 'endSponsoringFutureReserves':
188
+ case 'clawbackClaimableBalance':
189
+ case 'extendFootprintTtl':
190
+ case 'restoreFootprint':
191
+ matchingProps.account = new Set()
192
+ break
193
+ default:
194
+ throw new Error('Unsupported op type: ' + op.type)
195
+ }
196
+ if (!matchingProps.account) {
197
+ matchingProps.account = new Set()
198
+ }
199
+ if (!matchingProps.account.has(op.source)) {
200
+ matchingProps.account.add(op.source)
201
+ }
202
+ }
203
+ //add op source account
204
+ matchingProps.source = new Set([op.source])
205
+ //process assets
206
+ if (filters.asset || filters.src_asset || filters.dest_asset)
207
+ switch (op.type) {
208
+ case 'createAccount':
209
+ case 'accountMerge':
210
+ case 'inflation':
211
+ matchingProps.asset = new Set(['XLM'])
212
+ matchingProps.src_asset = new Set(['XLM'])
213
+ matchingProps.dest_asset = new Set(['XLM'])
214
+ break
215
+ case 'payment':
216
+ case 'clawback':
217
+ case 'setTrustLineFlags':
218
+ case 'createClaimableBalance':
219
+ case 'revokeAccountSponsorship':
220
+ case 'revokeTrustlineSponsorship':
221
+ case 'revokeOfferSponsorship':
222
+ case 'revokeDataSponsorship':
223
+ case 'revokeClaimableBalanceSponsorship':
224
+ case 'revokeLiquidityPoolSponsorship':
225
+ case 'revokeSignerSponsorship':
226
+ case 'revokeSponsorship':
227
+ if (op.asset) {
228
+ const a = [AssetDescriptor.parse(op.asset).toFQAN()]
229
+ matchingProps.asset = new Set(a)
230
+ matchingProps.src_asset = new Set(a)
231
+ matchingProps.dest_asset = new Set(a)
232
+ }
233
+ break
234
+ case 'changeTrust': {
235
+ const a = [AssetDescriptor.parse(op.line).toFQAN()]
236
+ matchingProps.dest_asset = new Set(a)
237
+ if (op.line instanceof LiquidityPoolAsset) {
238
+ a.push(AssetDescriptor.parse(op.line.assetA).toFQAN())
239
+ a.push(AssetDescriptor.parse(op.line.assetB).toFQAN())
240
+ }
241
+ matchingProps.asset = new Set(a)
242
+ matchingProps.src_asset = new Set(a)
243
+ }
244
+ break
245
+ case 'pathPaymentStrictSend':
246
+ case 'pathPaymentStrictReceive': {
247
+ const src = AssetDescriptor.parse(op.sendAsset).toFQAN()
248
+ const dest = AssetDescriptor.parse(op.destAsset).toFQAN()
249
+ matchingProps.asset = new Set([src, dest, ...op.path.map(a => AssetDescriptor.parse(a).toFQAN())])
250
+ matchingProps.src_asset = new Set([src])
251
+ matchingProps.dest_asset = new Set([dest])
252
+ }
253
+ break
254
+ case 'manageBuyOffer':
255
+ case 'manageSellOffer':
256
+ case 'createPassiveSellOffer': {
257
+ const selling = AssetDescriptor.parse(op.selling).toFQAN()
258
+ const buying = AssetDescriptor.parse(op.buying).toFQAN()
259
+ matchingProps.asset = new Set([selling, buying])
260
+ matchingProps.src_asset = new Set([selling])
261
+ matchingProps.dest_asset = new Set([buying])
262
+ }
263
+ break
264
+ case 'allowTrust': {
265
+ const asset = AssetDescriptor.parse({code: op.assetCode, issuer: op.source}).toFQAN()
266
+ matchingProps.asset = new Set([asset])
267
+ matchingProps.src_asset = new Set([asset])
268
+ matchingProps.dest_asset = new Set([asset])
269
+ }
270
+ break
271
+ case 'liquidityPoolDeposit':
272
+ case 'liquidityPoolWithdraw':
273
+ if (od.successful) { // TODO: find a workaround for this (operations won't match for failed transactions)
274
+ const poolUpdatedEffect = op.effects.find(e => e.type === 'liquidityPoolUpdated')
275
+ const assets = poolUpdatedEffect.reserves.map(r => AssetDescriptor.parse(r.asset).toFQAN())
276
+ matchingProps.asset = new Set(assets)
277
+ matchingProps.src_asset = new Set(assets)
278
+ matchingProps.dest_asset = new Set(assets)
279
+ }
280
+ break
281
+ case 'invokeHostFunction':
282
+ matchingProps.asset = new Set()
283
+ matchingProps.src_asset = new Set()
284
+ matchingProps.dest_asset = new Set()
285
+ const assets = new Set()
286
+ for (const effect of op.effects) {
287
+ if (effect.asset !== undefined) {
288
+ matchingProps.asset.add(effect.asset)
289
+ matchingProps.src_asset.add(effect.asset)
290
+ matchingProps.dest_asset.add(effect.asset)
291
+ }
292
+ }
293
+ break
294
+ }
295
+
296
+ //process liquidity pools and offers
297
+ if (filters.pool || filters.offer) {
298
+ switch (op.type) {
299
+ case 'liquidityPoolDeposit':
300
+ case 'liquidityPoolWithdraw':
301
+ case 'revokeLiquidityPoolSponsorship':
302
+ matchingProps.pool = new Set([op.liquidityPoolId])
303
+ break
304
+ case 'changeTrust':
305
+ case 'revokeTrustlineSponsorship':
306
+ const asset = AssetDescriptor.parse(op.line || op.asset).toFQAN()
307
+ if (asset.length === 64 && !asset.includes('-')) {
308
+ matchingProps.pool = new Set([asset])
309
+ }
310
+ break
311
+ case 'manageBuyOffer':
312
+ case 'manageSellOffer':
313
+ case 'createPassiveSellOffer':
314
+ case 'pathPaymentStrictSend':
315
+ case 'pathPaymentStrictReceive':
316
+ if (od.successful) {
317
+ const offers = []
318
+ const pools = []
319
+ for (const e of op.effects)
320
+ if (e.type === 'trade') {
321
+ if (e.pool) {
322
+ pools.push(e.pool)
323
+ }
324
+ if (e.offer) {
325
+ offers.push(e.offer)
326
+ if (!skipUnrelated) {
327
+ if (!matchingProps.account) {
328
+ matchingProps.account = new Set()
329
+ }
330
+ if (!matchingProps.account.has(e.seller)) {
331
+ matchingProps.account.add(e.seller)
332
+ }
333
+ }
334
+ }
335
+ }
336
+ if (offers.length) {
337
+ matchingProps.offer = new Set(offers)
338
+ }
339
+ if (pools.length) {
340
+ matchingProps.pool = new Set(pools)
341
+ }
342
+ }
343
+ if (op.offerId) {
344
+ if (!matchingProps.offer) {
345
+ matchingProps.offer = new Set()
346
+ }
347
+ if (!matchingProps.offer.has(op.offerId)) {
348
+ matchingProps.offer.add(op.offerId)
349
+ }
350
+ }
351
+ break
352
+ case 'revokeOfferSponsorship':
353
+ matchingProps.offer = new Set([op.offerId])
354
+ break
355
+ case 'clawback':
356
+ matchingProps.account = new Set([op.from])
357
+ matchingProps.destination = new Set([op.from])
358
+ break
359
+ }
360
+ }
361
+ //TODO: handle trustline authorization effects (deauthorization may cancel offers and withdraw liquidity pool stakes)
362
+ return matchingProps
363
+ }
364
+ }
365
+
366
+ /**
367
+ * Callback for matching operation by type
368
+ * @callback TypeMatcherCallback
369
+ * @param {OperationDescriptor} op - Operation descriptor to match
370
+ * @return {boolean}
371
+ * @internal
372
372
  */