@stellar-expert/ui-framework 1.9.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 (124) hide show
  1. package/.npmrc +1 -0
  2. package/LICENSE +21 -0
  3. package/README.md +3 -0
  4. package/account/account-address.js +127 -0
  5. package/account/account-address.scss +20 -0
  6. package/account/available-balance.js +21 -0
  7. package/account/identicon.js +91 -0
  8. package/account/identicon.scss +6 -0
  9. package/account/signer-key.js +65 -0
  10. package/api/api-cache.js +5 -0
  11. package/api/explorer-api-call.js +37 -0
  12. package/api/explorer-api-hooks.js +202 -0
  13. package/api/explorer-api-paginated-list-hooks.js +439 -0
  14. package/api/explorer-batch-info-loader.js +85 -0
  15. package/api/explorer-tx-api.js +29 -0
  16. package/asset/amount.js +53 -0
  17. package/asset/asset-icon.js +34 -0
  18. package/asset/asset-issuer.js +22 -0
  19. package/asset/asset-link.js +93 -0
  20. package/asset/asset-link.scss +26 -0
  21. package/asset/asset-list-hooks.js +60 -0
  22. package/asset/asset-meta-hooks.js +78 -0
  23. package/asset/asset-selector.js +72 -0
  24. package/asset/asset-selector.scss +41 -0
  25. package/basic-styles/base.scss +53 -0
  26. package/basic-styles/form.scss +90 -0
  27. package/basic-styles/grid.scss +388 -0
  28. package/basic-styles/link.scss +17 -0
  29. package/basic-styles/lists.scss +68 -0
  30. package/basic-styles/loader.scss +22 -0
  31. package/basic-styles/responsive.scss +29 -0
  32. package/basic-styles/scrollbars.scss +19 -0
  33. package/basic-styles/spacing.scss +50 -0
  34. package/basic-styles/table.scss +104 -0
  35. package/basic-styles/themes.scss +59 -0
  36. package/basic-styles/typography.scss +191 -0
  37. package/basic-styles/variables.scss +26 -0
  38. package/claimable-balance/claimable-balance-claimants.js +12 -0
  39. package/contract/sc-val.js +70 -0
  40. package/contract/wasm-section-parser.js +159 -0
  41. package/contract/wasm-section-reader.js +79 -0
  42. package/contract/xdr-reader.js +148 -0
  43. package/controls/active-icon.scss +23 -0
  44. package/controls/button-group.js +20 -0
  45. package/controls/button-group.scss +48 -0
  46. package/controls/button.js +74 -0
  47. package/controls/button.scss +148 -0
  48. package/controls/code-block.js +35 -0
  49. package/controls/code-block.scss +74 -0
  50. package/controls/dropdown.js +281 -0
  51. package/controls/dropdown.scss +161 -0
  52. package/controls/external-link.js +5 -0
  53. package/controls/info-tooltip.js +17 -0
  54. package/controls/info-tooltip.scss +4 -0
  55. package/controls/slider.js +20 -0
  56. package/controls/slider.scss +22 -0
  57. package/controls/tabs.js +94 -0
  58. package/controls/tabs.scss +73 -0
  59. package/controls/tooltip.js +214 -0
  60. package/controls/tooltip.scss +97 -0
  61. package/controls/update-highlighter.js +28 -0
  62. package/controls/update-highlighter.scss +10 -0
  63. package/date/date-selector.js +44 -0
  64. package/date/elapsed-time.js +22 -0
  65. package/date/utc-timestamp.js +23 -0
  66. package/dex/price-dynamic.js +45 -0
  67. package/dex/price-dynamic.scss +36 -0
  68. package/directory/directory-hooks.js +89 -0
  69. package/effect/effect-description.js +289 -0
  70. package/effect/op-effects-view.js +11 -0
  71. package/effect/op-effects.scss +8 -0
  72. package/errors/error-boundary.js +98 -0
  73. package/fonts/font.scss +23 -0
  74. package/fonts/icons/config.json +1858 -0
  75. package/fonts/icons/icons-embedded.scss +644 -0
  76. package/horizon/horizon-account-helpers.js +104 -0
  77. package/horizon/horizon-client-helpers.js +52 -0
  78. package/horizon/horizon-ledger-helpers.js +36 -0
  79. package/horizon/horizon-orderbook-helpers.js +14 -0
  80. package/horizon/horizon-trades-helper.js +89 -0
  81. package/horizon/horizon-transaction-helpers.js +36 -0
  82. package/index.js +85 -0
  83. package/index.scss +16 -0
  84. package/interaction/autofocus.js +9 -0
  85. package/interaction/block-select.js +54 -0
  86. package/interaction/block-select.scss +24 -0
  87. package/interaction/copy-to-clipboard.js +19 -0
  88. package/interaction/inline-progress.js +16 -0
  89. package/interaction/qr-code.js +45 -0
  90. package/interaction/responsive.js +21 -0
  91. package/interaction/spoiler.js +40 -0
  92. package/interaction/spoiler.scss +9 -0
  93. package/interaction/theme-selector.js +11 -0
  94. package/ledger/ledger-entry-href-formatter.js +22 -0
  95. package/ledger/ledger-entry-link.js +59 -0
  96. package/meta/page-meta-tags.js +229 -0
  97. package/module/dynamic-module.js +48 -0
  98. package/package.json +41 -0
  99. package/state/on-screen-hooks.js +23 -0
  100. package/state/page-visibility-helpers.js +17 -0
  101. package/state/page-visibility-hooks.js +12 -0
  102. package/state/screen-orientation-hooks.js +16 -0
  103. package/state/state-hooks.js +77 -0
  104. package/state/stellar-network-hooks.js +45 -0
  105. package/state/theme.js +29 -0
  106. package/stellar/key-type.js +92 -0
  107. package/stellar/ledger-generic-id.js +40 -0
  108. package/stellar/signature-hint-utils.js +65 -0
  109. package/toast/toast-notification-instance.js +43 -0
  110. package/toast/toast-notification.js +46 -0
  111. package/toast/toast-notifications-block.js +55 -0
  112. package/toast/toast-notifications.scss +166 -0
  113. package/tx/op-accounting-changes.js +27 -0
  114. package/tx/op-description-view.js +925 -0
  115. package/tx/op-description.scss +98 -0
  116. package/tx/op-icon.js +93 -0
  117. package/tx/parser/op-balance-changes.js +62 -0
  118. package/tx/parser/op-descriptor.js +49 -0
  119. package/tx/parser/tx-details-parser.js +75 -0
  120. package/tx/parser/tx-matcher.js +372 -0
  121. package/tx/parser/type-filter-matcher.js +127 -0
  122. package/tx/tx-fee-effect.js +14 -0
  123. package/tx/tx-list-hooks.js +19 -0
  124. package/tx/tx-operations-list.js +105 -0
@@ -0,0 +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
+ }
17
+ this.filters = matcherFilters
18
+ this.typeMatcher = new TypeFilterMatcher(filters.type)
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 'bumpFootprintExpiration':
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
+ //porcess 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
+ */
@@ -0,0 +1,127 @@
1
+ const operatinTypeMap = {
2
+ createAccount: 0,
3
+ payment: 1,
4
+ pathPaymentStrictReceive: 2,
5
+ pathPaymentStrictSend: 13,
6
+ createPassiveSellOffer: 4,
7
+ manageSellOffer: 3,
8
+ manageBuyOffer: 12,
9
+ setOptions: 5,
10
+ changeTrust: 6,
11
+ allowTrust: 7,
12
+ accountMerge: 8,
13
+ inflation: 9,
14
+ manageData: 10,
15
+ bumpSequence: 11,
16
+ createClaimableBalance: 14,
17
+ claimClaimableBalance: 15,
18
+ beginSponsoringFutureReserves: 16,
19
+ endSponsoringFutureReserves: 17,
20
+ revokeSponsorship: 18,
21
+ clawback: 19,
22
+ clawbackClaimableBalance: 20,
23
+ setTrustLineFlags: 21,
24
+ liquidityPoolDeposit: 22,
25
+ liquidityPoolWithdraw: 23,
26
+ invokeHostFunction: 24,
27
+ bumpFootprintExpiration: 25,
28
+ restoreFootprint: 26
29
+ }
30
+
31
+ export class TypeFilterMatcher {
32
+ /**
33
+ * @param {String[]} filters
34
+ */
35
+ constructor(filters) {
36
+ if (!filters?.length)
37
+ return
38
+ this.types = new Set()
39
+ for (const filter of filters) {
40
+ this.processFilter(filter)
41
+ }
42
+ }
43
+
44
+ /**
45
+ * @type {Set}
46
+ * @readonly
47
+ * @private
48
+ */
49
+ types
50
+
51
+ /**
52
+ * Get unique set of type filters
53
+ * @return {Boolean}
54
+ */
55
+ match(operationType) {
56
+ return !this.types || this.types.has(operatinTypeMap[operationType])
57
+ }
58
+
59
+ /**
60
+ * @param {String} filter
61
+ * @private
62
+ */
63
+ processFilter(filter) {
64
+ for (const type of this.decodeFilter(filter)) {
65
+ this.types.add(type)
66
+ }
67
+ }
68
+
69
+ /**
70
+ * @param {String} filter
71
+ * @private
72
+ */
73
+ decodeFilter(filter) {
74
+ switch (filter) {
75
+ case 'payments':
76
+ return [
77
+ operatinTypeMap.createAccount,
78
+ operatinTypeMap.accountMerge,
79
+ operatinTypeMap.payment,
80
+ operatinTypeMap.pathPaymentStrictReceive,
81
+ operatinTypeMap.pathPaymentStrictSend,
82
+ operatinTypeMap.createClaimableBalance,
83
+ operatinTypeMap.claimClaimableBalance,
84
+ operatinTypeMap.clawback,
85
+ operatinTypeMap.clawbackClaimableBalance,
86
+ operatinTypeMap.inflation
87
+ ]
88
+ case 'trustlines':
89
+ return [
90
+ operatinTypeMap.changeTrust,
91
+ operatinTypeMap.allowTrust,
92
+ operatinTypeMap.setTrustLineFlags
93
+ ]
94
+ case 'dex':
95
+ return [
96
+ operatinTypeMap.manageSellOffer,
97
+ operatinTypeMap.manageBuyOffer,
98
+ operatinTypeMap.createPassiveSellOffer,
99
+ operatinTypeMap.liquidityPoolDeposit,
100
+ operatinTypeMap.liquidityPoolWithdraw
101
+ ]
102
+ case 'settings':
103
+ return [
104
+ operatinTypeMap.createAccount,
105
+ operatinTypeMap.setOptions,
106
+ operatinTypeMap.changeTrust,
107
+ operatinTypeMap.allowTrust,
108
+ operatinTypeMap.accountMerge,
109
+ operatinTypeMap.inflation,
110
+ operatinTypeMap.manageData,
111
+ operatinTypeMap.bumpSequence,
112
+ operatinTypeMap.beginSponsoringFutureReserves,
113
+ operatinTypeMap.endSponsoringFutureReserves,
114
+ operatinTypeMap.revokeSponsorship,
115
+ operatinTypeMap.setTrustLineFlags
116
+ ]
117
+ case 'contracts':
118
+ return [
119
+ operatinTypeMap.invokeHostFunction,
120
+ operatinTypeMap.bumpFootprintExpiration,
121
+ operatinTypeMap.restoreFootprint
122
+ ]
123
+ default:
124
+ return [parseInt(filter, 10)]
125
+ }
126
+ }
127
+ }
@@ -0,0 +1,14 @@
1
+ import React from 'react'
2
+ import {Amount} from '../asset/amount'
3
+ import {fromStroops} from '@stellar-expert/formatter'
4
+
5
+ export default function TxFeeEffect({feeEffect, compact}) {
6
+ const {charged, bid, bump} = feeEffect
7
+ if (charged)
8
+ return <div>
9
+ <Amount amount={fromStroops(charged)} asset="XLM" issuer={!compact}/> transaction {!!bump && 'bump'} fee charged
10
+ </div>
11
+ return <div>
12
+ Charge transaction {!!bump && 'bump'} fee <Amount amount={fromStroops(bid)} asset="XLM" issuer={!compact}/>
13
+ </div>
14
+ }
@@ -0,0 +1,19 @@
1
+ import {useExplorerPaginatedApi} from '../api/explorer-api-paginated-list-hooks'
2
+ import {useExplorerApi} from '../api/explorer-api-hooks'
3
+
4
+ export function useTxHistory({filters, order = 'desc', rows = 40, updateLocation = true}) {
5
+ return useExplorerPaginatedApi(
6
+ {
7
+ path: 'tx',
8
+ query: filters
9
+ }, {
10
+ defaultSortOrder: order,
11
+ limit: rows,
12
+ updateLocation,
13
+ defaultQueryParams: {order}
14
+ })
15
+ }
16
+
17
+ export function useTxInfo(idOrHash) {
18
+ return useExplorerApi(`tx/${idOrHash}`)
19
+ }
@@ -0,0 +1,105 @@
1
+ import React, {useCallback, useState} from 'react'
2
+ import {Spoiler} from '../interaction/spoiler'
3
+ import {OpEffectsView} from '../effect/op-effects-view'
4
+ import {OpAccountingChanges, TxFeeAccountingChanges} from './op-accounting-changes'
5
+ import TxFeeEffect from './tx-fee-effect'
6
+ import {OpDescriptionView} from './op-description-view'
7
+ import {OpIcon} from './op-icon'
8
+ import './op-description.scss'
9
+
10
+ /*
11
+ Every operation can be in one of the following states (detected automatically):
12
+ - Ephemeral
13
+ - Pending (not processed by StellarCore yet)
14
+ - Rejected (rejected by Horizon or StellarCore during execution)
15
+ - Applied
16
+ - Processed (applied to the ledger and resulted in some on-chain state changes)
17
+ - Processed without effects (applied to the ledger but yielded no changes in the ledger state)
18
+ - Failed during the execution
19
+
20
+ Display context
21
+ - Transaction context
22
+ - All operations displayed
23
+ - Source account always visible
24
+ - No fee charges
25
+ - Effects displayed
26
+ - Unfiltered view context
27
+ - All operations displayed
28
+ - Source account always visible
29
+ - No fee charges
30
+ - All effects displayed
31
+ - Filtered view context
32
+ - Only operations related to the filter context (account/asset/order)
33
+ - Source account always visible
34
+ - Fee charges ony for account context
35
+ - Effects related to the filter context
36
+ - Account history view context
37
+ - Only operations DIRECTLY related to the current account (skip third-party operations for claimable balances, sponsorship, etc)
38
+ - Source account displayed only when operation (or transaction) source account not matches current account
39
+ - Fee charge effects displayed only if charged from the current account
40
+ - Effects related to the current account
41
+ */
42
+
43
+ const TxChargedFee = React.memo(function TxChargedFee({parsedTx, compact}) {
44
+ if (parsedTx.isEphemeral || !parsedTx.context?.account)
45
+ return null
46
+ const fee = parsedTx.effects.find(e => e.type === 'feeCharged')
47
+ if (!fee || !parsedTx.context.account.includes(fee.source))
48
+ return null
49
+ return <div className="op-container">
50
+ <div className="op-layout">
51
+ <OpIcon op="feeCharge"/>
52
+ <TxFeeEffect feeEffect={fee} compact={compact}/>
53
+ {!!compact && !parsedTx.isEphemeral && <TxFeeAccountingChanges amount={fee.charged}/>}
54
+ </div>
55
+ </div>
56
+ })
57
+
58
+ /**
59
+ * @param {ParsedTxDetails} parsedTx - Transaction descriptor
60
+ * @param {Function} [filter] - Filter matcher function
61
+ * @param {Boolean} [showFees] - Whether to display transaction fees
62
+ * @param {Boolean} [showEffects] - Whether to show operation effects
63
+ * @param {Boolean} [compact] - Compact view (without fee charges and accounting changes effects)
64
+ */
65
+ export const TxOperationsList = React.memo(function TxOperationsList({
66
+ parsedTx,
67
+ filter = null,
68
+ showFees = true,
69
+ compact = false,
70
+ showEffects
71
+ }) {
72
+ const [effectsExpanded, setEffectsExpanded] = useState(false)
73
+ const toggleEffects = useCallback(e => setEffectsExpanded(e.expanded), [])
74
+ let {operations} = parsedTx
75
+ let opdiff = 0
76
+ //filter operations if filtered output is requested
77
+ if (filter) {
78
+ operations = operations.filter(filter)
79
+ opdiff = parsedTx.operations.length - operations.length
80
+ }
81
+
82
+ return <div>
83
+ {!compact && showEffects !== false &&
84
+ <div className="tx-effects-toggle">
85
+ <Spoiler micro active expanded={effectsExpanded} showLess="Hide operation effects" showMore="Show operation effects"
86
+ onChange={toggleEffects} style={{margin: '0.2em'}}/>
87
+ </div>}
88
+ <div className="condensed">
89
+ {operations.map((op, i) => <div className="op-container" key={op.txHash + op.order + op.isEphemeral}>
90
+ <div className="op-layout">
91
+ <OpIcon op={op}/>
92
+ <div>
93
+ <OpDescriptionView key={parsedTx.txHash + op.order} op={op} compact={compact}/>
94
+ </div>
95
+ {!!compact && !op.isEphemeral && <OpAccountingChanges op={op}/>}
96
+ </div>
97
+ {effectsExpanded && <OpEffectsView effects={op.operation.effects}/>}
98
+ </div>)}
99
+ {showFees && <TxChargedFee {...{parsedTx, compact}}/>}
100
+ {opdiff > 0 && <div className="dimmed text-small block-indent">
101
+ {opdiff} more operation{opdiff > 1 && 's'} hidden
102
+ </div>}
103
+ </div>
104
+ </div>
105
+ })