@stellar-expert/ui-framework 1.16.8 → 1.19.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 (85) hide show
  1. package/README.md +1168 -3
  2. package/account/account-address.js +127 -127
  3. package/account/available-balance.js +22 -22
  4. package/account/identicon.js +90 -90
  5. package/account/signer-key.js +65 -64
  6. package/api/explorer-api-hooks.js +209 -202
  7. package/api/explorer-api-paginated-list-hooks.js +441 -440
  8. package/api/explorer-batch-info-loader.js +111 -85
  9. package/api/explorer-tx-api.js +28 -28
  10. package/api/ledger-stream.js +15 -0
  11. package/asset/amount.js +56 -56
  12. package/asset/asset-icon.js +41 -41
  13. package/asset/asset-issuer.js +21 -21
  14. package/asset/asset-link.js +107 -107
  15. package/asset/asset-list-hooks.js +59 -59
  16. package/asset/asset-meta-hooks.js +88 -88
  17. package/asset/asset-selector.js +72 -71
  18. package/claimable-balance/claimable-balance-claimants.js +23 -18
  19. package/contract/contract-api.js +15 -0
  20. package/contract/invocation-info-view.js +10 -2
  21. package/contract/sc-val.js +131 -107
  22. package/controls/button-group.js +25 -19
  23. package/controls/button.js +93 -78
  24. package/controls/code-block.js +42 -34
  25. package/controls/dropdown.js +318 -287
  26. package/controls/external-link.js +10 -4
  27. package/controls/info-tooltip.js +23 -16
  28. package/controls/slider.js +29 -19
  29. package/controls/tabs.js +94 -94
  30. package/controls/tooltip.js +244 -240
  31. package/controls/update-highlighter.js +32 -27
  32. package/date/date-selector.js +56 -54
  33. package/date/elapsed-time.js +28 -21
  34. package/dex/price-dynamic.js +53 -44
  35. package/directory/directory-hooks.js +109 -97
  36. package/effect/effect-description.js +346 -344
  37. package/errors/error-boundary.js +110 -97
  38. package/filter/editors/account-filter-view.js +20 -0
  39. package/filter/editors/asset-filter-view.js +9 -0
  40. package/filter/editors/timestamp-filter-view.js +34 -0
  41. package/filter/editors/type-filter-view.js +148 -0
  42. package/filter/filter-editors.js +102 -0
  43. package/filter/filter-view.js +218 -0
  44. package/filter/filter.scss +57 -0
  45. package/horizon/horizon-account-helpers.js +104 -104
  46. package/horizon/horizon-ledger-helpers.js +35 -35
  47. package/horizon/horizon-trades-helper.js +88 -88
  48. package/horizon/horizon-transaction-helpers.js +36 -36
  49. package/index.d.ts +1271 -0
  50. package/index.js +95 -93
  51. package/interaction/accordion.js +43 -35
  52. package/interaction/autofocus.js +13 -9
  53. package/interaction/block-select.js +64 -53
  54. package/interaction/copy-to-clipboard.js +25 -18
  55. package/interaction/inline-progress.js +20 -15
  56. package/interaction/qr-code.js +34 -34
  57. package/interaction/responsive.js +20 -20
  58. package/interaction/spoiler.js +52 -39
  59. package/interaction/theme-selector.js +13 -10
  60. package/ledger/ledger-entry-href-formatter.js +27 -26
  61. package/ledger/ledger-entry-link.js +93 -58
  62. package/ledger/ledger-info-parser.js +46 -18
  63. package/meta/page-meta-tags.js +243 -238
  64. package/module/dynamic-module.js +47 -47
  65. package/package.json +71 -40
  66. package/state/on-screen-hooks.js +22 -22
  67. package/state/page-visibility-helpers.js +29 -16
  68. package/state/page-visibility-hooks.js +13 -11
  69. package/state/screen-orientation-hooks.js +19 -15
  70. package/state/state-hooks.js +76 -76
  71. package/state/stellar-network-hooks.js +56 -44
  72. package/state/theme.js +29 -28
  73. package/stellar/key-type.js +92 -91
  74. package/stellar/ledger-generic-id.js +39 -39
  75. package/stellar/signature-hint-utils.js +65 -65
  76. package/toast/toast-notifications-block.js +59 -59
  77. package/tx/op-description-view.js +945 -942
  78. package/tx/op-icon.js +98 -98
  79. package/tx/parser/op-balance-changes.js +66 -66
  80. package/tx/parser/op-descriptor.js +51 -48
  81. package/tx/parser/tx-details-parser.js +81 -81
  82. package/tx/parser/tx-matcher.js +371 -371
  83. package/tx/parser/type-filter-matcher.js +126 -126
  84. package/tx/tx-list-hooks.js +32 -18
  85. package/tx/tx-operations-list.js +117 -116
@@ -1,942 +1,945 @@
1
- import React from 'react'
2
- import {Networks, AuthRequiredFlag, AuthRevocableFlag, AuthClawbackEnabledFlag, AuthImmutableFlag} from '@stellar/stellar-base'
3
- import {AssetDescriptor} from '@stellar-expert/asset-descriptor'
4
- import {formatPrice, formatWithAutoPrecision, fromStroops, toStroops, shortenString} from '@stellar-expert/formatter'
5
- import {xdrParserUtils, contractPreimageEncoder} from '@stellar-expert/tx-meta-effects-parser'
6
- import {AccountAddress} from '../account/account-address'
7
- import {AccountIdenticon} from '../account/identicon'
8
- import {SignerKey} from '../account/signer-key'
9
- import {Amount} from '../asset/amount'
10
- import {AssetLink} from '../asset/asset-link'
11
- import {useAssetMeta} from '../asset/asset-meta-hooks'
12
- import {OfferLink} from '../ledger/ledger-entry-link'
13
- import {formatExplorerLink} from '../ledger/ledger-entry-href-formatter'
14
- import {ClaimableBalanceClaimants} from '../claimable-balance/claimable-balance-claimants'
15
- import {CopyToClipboard} from '../interaction/copy-to-clipboard'
16
- import {useStellarNetwork} from '../state/stellar-network-hooks'
17
- import InvocationInfoView from '../contract/invocation-info-view'
18
- import {ClaimableBalanceId} from '../claimable-balance/claimable-balance-id'
19
-
20
- function getAccountPredefinedDisplayName(address) {
21
- if (!window.predefinedAccountDisplayNames)
22
- return undefined
23
- return window.predefinedAccountDisplayNames[address]
24
- }
25
-
26
- /**
27
- * @param {OperationDescriptor} op
28
- * @param {Boolean} compact
29
- * @constructor
30
- */
31
- function OpSourceAccount({op, compact}) {
32
- const network = useStellarNetwork()
33
- const {source} = op.operation
34
- const txSource = op.tx.tx.source
35
- if (op.isEphemeral && (!source || source === txSource))
36
- return null
37
- const predefined = getAccountPredefinedDisplayName(source)
38
- const accountItem = predefined ?
39
- <a href={formatExplorerLink('account', source, network)} target="_blank" rel="nofollow noreferrer">
40
- <AccountIdenticon address={source}/> {predefined}
41
- </a> :
42
- <AccountAddress account={source}/>
43
-
44
- if (!op.isEphemeral)
45
- return accountItem
46
- return <> for account {accountItem}</>
47
- }
48
-
49
-
50
- /**
51
- * @param {OperationDescriptor} op
52
- * @param {Boolean} compact
53
- * @constructor
54
- */
55
- function CreateAccountDescriptionView({op, compact}) {
56
- const {destination, startingBalance} = op.operation
57
- return op.isEphemeral ? <>
58
- <b>Create account</b> <AccountAddress account={destination}/>{' '}
59
- with starting balance <Amount amount={startingBalance} asset="XLM" issuer={!compact}/>
60
- <OpSourceAccount op={op}/>
61
- </> : <>
62
- <OpSourceAccount op={op}/> created account <AccountAddress account={destination}/>{' '}
63
- with starting balance <Amount amount={startingBalance} asset="XLM" issuer={!compact}/>
64
- </>
65
- }
66
-
67
- /**
68
- * Type: 1
69
- * @param {OperationDescriptor} op
70
- * @param {Boolean} compact
71
- * @constructor
72
- */
73
- function PaymentDescriptionView({op, compact}) {
74
- const {destination, asset, amount} = op.operation
75
- return op.isEphemeral ? <>
76
- <b>Send</b> <Amount amount={amount} asset={asset}/> to <AccountAddress account={destination}/>
77
- <OpSourceAccount op={op}/>
78
- </> : <>
79
- <OpSourceAccount op={op}/> sent{' '}
80
- <Amount amount={amount} asset={AssetDescriptor.parse(asset)} issuer={!compact}/> to{' '}
81
- <AccountAddress account={destination}/>
82
- </>
83
- }
84
-
85
- /**
86
- * Type: 2, 13
87
- * @param {OperationDescriptor} op
88
- * @param {Boolean} compact
89
- * @constructor
90
- */
91
- function PathPaymentDescriptionView({op, compact}) {
92
- let {source, destination, sendAsset, sendMax, sendAmount, destAsset, destAmount, destMin, path, effects} = op.operation
93
- const isSwap = source === destination
94
- let src = sendAmount || sendMax
95
- let dst = destAmount || destMin
96
- sendAsset = AssetDescriptor.parse(sendAsset)
97
- destAsset = AssetDescriptor.parse(destAsset)
98
- const sendAssetId = sendAsset.toFQAN()
99
- const destAssetId = destAsset.toFQAN()
100
- const pathData = <>
101
- <i className="icon icon-shuffle color-primary"/>{' '}
102
- {!compact && path.map((asset, i) => <span key={i + '-' + asset.toString()}>
103
- <AssetLink asset={asset}/> <i className="icon icon-shuffle color-primary"/>{' '}
104
- </span>)}
105
- </>
106
- if (op.isEphemeral)
107
- return <>
108
- <b>{isSwap ? 'Swap' : 'Send'}</b> <Amount amount={src} asset={sendAsset} issuer={!compact}/>{' '}
109
- {pathData}
110
- <Amount amount={dst} asset={destAsset} issuer={!compact}/>
111
- {!isSwap && <> to <AccountAddress account={destination}/></>}
112
- <OpSourceAccount op={op}/>
113
- </>
114
- if (sendMax !== undefined) {
115
- if (isSwap && sendAssetId === destAssetId && effects.length) { //circular trades yield accountCredited/Debited effect
116
- const changeEffect = effects.find(e => e.source === source && e.asset === sendAssetId && (e.type === 'accountCredited' || e.type === 'accountDebited'))
117
- if (changeEffect) { //arbitrage successful, otherwise src = dst and no account changes happened
118
- const change = BigInt(changeEffect.amount)
119
- src = fromStroops(toStroops(destAmount) + (changeEffect.type === 'accountCredited' ? -change : change))
120
- }
121
- } else {
122
- const debitedEffect = effects.find(e => e.source === source && e.type === 'accountDebited' && e.asset === sendAssetId)
123
- if (debitedEffect) {
124
- src = fromStroops(debitedEffect.amount)
125
- }
126
- }
127
- }
128
- if (destMin !== undefined) {
129
- if (isSwap && sendAssetId === destAssetId && effects.length) { //circular trades yield accountCredited/Debited effect
130
- const changeEffect = effects.find(e => e.source === source && e.asset === destAssetId && (e.type === 'accountCredited' || e.type === 'accountDebited'))
131
- if (changeEffect) { //arbitrage successful, otherwise src = dst and no account changes happened
132
- const change = BigInt(changeEffect.amount)
133
- dst = fromStroops(toStroops(sendAmount) + (changeEffect.type === 'accountCredited' ? change : -change))
134
- }
135
- } else {
136
- const creditedEffect = effects.find(e => e.source === destination && e.type === 'accountCredited' && e.asset === destAssetId)
137
- if (creditedEffect) {
138
- dst = fromStroops(creditedEffect.amount)
139
- }
140
- }
141
- }
142
- return <>
143
- <OpSourceAccount op={op}/> {isSwap ? 'swapped ' : 'sent '}
144
- <Amount amount={src} asset={sendAsset} issuer={!compact}/>{' '}
145
- {!compact && sendMax > 0 && op.successful && <><span className="dimmed nowrap">
146
- (max <Amount amount={sendMax} asset={AssetDescriptor.parse(sendAsset)}/>)
147
- </span> </>}
148
- {pathData}
149
- <Amount amount={dst} asset={destAsset} issuer={!compact}/>{' '}
150
- {!compact && destMin > 0 && op.successful && <><span className="dimmed nowrap">
151
- (min <Amount amount={destMin} asset={destAsset}/>)
152
- </span> </>}
153
- {!isSwap && <>to <AccountAddress account={destination}/></>}
154
- </>
155
- }
156
-
157
- /**
158
- * Type: 3, 4, 12
159
- * @param {OperationDescriptor} op
160
- * @param {Boolean} compact
161
- * @constructor
162
- */
163
- function ManageOfferDescriptionView({op, compact}) {
164
- let {offerId, amount, buyAmount, selling, buying, price, type} = op.operation
165
- const passive = type === 'createPassiveSellOffer'
166
- const isCancelled = !passive && offerId > 0 && parseFloat(amount || buyAmount) === 0
167
-
168
- const direction = type === 'manageBuyOffer' ? ' buy' : ' sell'
169
- let description = ''
170
- if (offerId > 0) { //manage existing offer
171
- description = 'Update'
172
- } else {
173
- description = 'Create'
174
- }
175
- if (!op.isEphemeral) {
176
- description += 'd'
177
- }
178
- if (passive) {
179
- description += ' passive'
180
- }
181
- description += direction + ' offer'
182
- const amountInfo = <>
183
- {amount !== undefined ?
184
- <Amount amount={amount} asset={selling} issuer={!compact}/> :
185
- <AssetLink asset={selling} issuer={!compact}/>}{' '}
186
- <i className="icon icon-shuffle color-primary"/>{' '}
187
- {buyAmount !== undefined ?
188
- <Amount amount={buyAmount} asset={buying} issuer={!compact}/> :
189
- <AssetLink asset={buying} issuer={!compact}/>}{' '}
190
- at <span className="nowrap">
191
- {formatWithAutoPrecision(parseFloat(price))} {AssetDescriptor.parse(buying).toCurrency()}/{AssetDescriptor.parse(selling).toCurrency()}
192
- </span>
193
- </>
194
- if (op.isEphemeral) {
195
- if (isCancelled)
196
- return <>
197
- <b>Cancel DEX offer</b> #<OfferLink offer={offerId}/><OpSourceAccount op={op}/>
198
- </>
199
- return <>
200
- <b>{description}</b> {offerId > 0 && <>#<OfferLink offer={offerId}/></>} {amountInfo}<OpSourceAccount op={op}/>
201
- </>
202
- }
203
-
204
- if (isCancelled)
205
- return <>
206
- <OpSourceAccount op={op}/> cancelled DEX offer <OfferLink offer={offerId}/>
207
- </>
208
- return <>
209
- <OpSourceAccount op={op}/> {description.toLowerCase()} {offerId > 0 && <OfferLink offer={offerId}/>} {amountInfo}
210
- </>
211
- }
212
-
213
- function AccountFlags({flags}) {
214
- if (!flags)
215
- return null
216
- const res = []
217
- if (flags & AuthRequiredFlag) {
218
- res.push('auth_required')
219
- }
220
- if (flags & AuthRevocableFlag) {
221
- res.push('auth_revocable')
222
- }
223
- if (flags & AuthImmutableFlag) {
224
- res.push('auth_immutable')
225
- }
226
- if (flags & AuthClawbackEnabledFlag) {
227
- res.push('auth_clawback_enabled')
228
- }
229
- return <>
230
- {res.map((f, i) => <span key={f}>{i > 0 && <>, </>}<code>{f}</code></span>)} flag{res.length > 1 && <>s</>}
231
- </>
232
- }
233
-
234
- /**
235
- * Type: 5
236
- * @param {OperationDescriptor} op
237
- * @param {Boolean} compact
238
- * @constructor
239
- */
240
- function SetOptionsDescriptionView({op, compact}) {
241
- const {
242
- setFlags,
243
- clearFlags,
244
- homeDomain,
245
- inflationDest,
246
- lowThreshold,
247
- medThreshold,
248
- highThreshold,
249
- signer,
250
- masterWeight,
251
- order,
252
- txHash
253
- } = op.operation
254
-
255
- const settings = []
256
- if (!!setFlags) {
257
- settings.push(<>Set <AccountFlags flags={setFlags}/></>)
258
- }
259
- if (!!clearFlags) {
260
- settings.push(<>Unset <AccountFlags flags={clearFlags}/></>)
261
- }
262
- if (homeDomain !== undefined) {
263
- settings.push(homeDomain ? <>Set home domain <code className="nowrap">{homeDomain}</code></> : <>Reset home domain</>)
264
- }
265
- if (inflationDest !== undefined) {
266
- settings.push(<>Set inflation destination to <AccountAddress account={inflationDest}/></>)
267
- }
268
- if (lowThreshold !== undefined || medThreshold !== undefined || highThreshold !== undefined) {
269
- const thresholds = []
270
- if (lowThreshold !== undefined) {
271
- thresholds.push('low=' + lowThreshold)
272
- }
273
- if (medThreshold !== undefined) {
274
- thresholds.push('medium=' + medThreshold)
275
- }
276
- if (highThreshold !== undefined) {
277
- thresholds.push('high=' + highThreshold)
278
- }
279
- settings.push(<>Set thresholds {thresholds.map((t, i) => <span key={i + t}>{i > 0 && <>, </>}<code>{t}</code></span>)}</>)
280
- }
281
- if (masterWeight !== undefined) {
282
- settings.push(<>Set master key weight to <code>{masterWeight}</code></>)
283
- }
284
- if (signer !== undefined) {
285
- if (signer.weight > 0) {
286
- settings.push(<>Add account signer <SignerKey signer={signer}/></>)
287
- } else {
288
- settings.push(<>Remove account signer <SignerKey signer={signer} showWeight={false}/></>)
289
- }
290
- }
291
- const details = <div className="block-indent text-small">
292
- {settings.map((s, i) => <div key={txHash + order + i}>{s}</div>)}
293
- </div>
294
- if (op.isEphemeral)
295
- return <>
296
- <b>Set account options</b><OpSourceAccount op={op}/>
297
- {details}
298
- </>
299
- return <>
300
- <OpSourceAccount op={op}/> updated account options.
301
- <div className="block-indent">
302
- {details}
303
- </div>
304
- </>
305
- }
306
-
307
- /**
308
- * Type: 6
309
- * @param {OperationDescriptor} op
310
- * @param {Boolean} compact
311
- * @constructor
312
- */
313
- function ChangeTrustDescriptionView({op, compact}) {
314
- const {line, limit, effects} = op.operation
315
- const trustAsset = AssetDescriptor.parse(line)
316
- const established = limit > 0
317
- if (op.isEphemeral) {
318
- if (established)
319
- return <>
320
- <b>Establish trustline</b> to <AssetLink asset={trustAsset} issuer={!compact}/>
321
- {limit !== '922337203685.4775807' && <> with limit <Amount amount={limit} asset={trustAsset} issuer={!compact}/></>}
322
- <OpSourceAccount op={op}/>
323
- </>
324
- return <>
325
- <b>Remove trustline</b> to <AssetLink asset={trustAsset} issuer={!compact}/><OpSourceAccount op={op}/>
326
- </>
327
- }
328
- if (established) {
329
- const description = effects.some(e => e.type === 'trustlineCreated') ? 'established' : 'updated'
330
- return <>
331
- <OpSourceAccount op={op}/> {description} trustline to <AssetLink asset={line} issuer={!compact}/>
332
- {limit !== '922337203685.4775807' && <> with limit <Amount amount={limit} asset={line} issuer={!compact}/></>}
333
- </>
334
- }
335
- return <>
336
- <OpSourceAccount op={op}/> removed trustline to <AssetLink asset={line} issuer={!compact}/>
337
- </>
338
- }
339
-
340
- /**
341
- * Type: 7
342
- * @param {OperationDescriptor} op
343
- * @param {Boolean} compact
344
- * @constructor
345
- */
346
- function AllowTrustDescriptionView({op, compact}) {
347
- const {source, assetCode, trustor, authorize} = op.operation
348
- const asset = AssetDescriptor.parse({code: assetCode, issuer: source})
349
-
350
- let description = authorize ? 'Authorize' : 'Deauthorize'
351
- if (op.isEphemeral)
352
- return <>
353
- <b>{description} trustline</b> <AssetLink asset={asset} issuer={!compact}/> for account <AccountAddress account={trustor}/>
354
- <OpSourceAccount op={op}/>
355
- </>
356
- return <>
357
- <OpSourceAccount op={op}/> {description.toLowerCase() + 'd'} trustline{' '}
358
- <AssetLink asset={asset} issuer={!compact}/> for account <AccountAddress account={trustor}/>
359
- </>
360
- }
361
-
362
- /**
363
- * Type: 8
364
- * @param {OperationDescriptor} op
365
- * @param {Boolean} compact
366
- * @constructor
367
- */
368
- function MergeAccountDescriptionView({op, compact}) {
369
- const {destination} = op.operation
370
- if (op.isEphemeral)
371
- return <>
372
- <b>Merge account</b> <OpSourceAccount op={op}/> into account <AccountAddress account={destination}/>
373
- <OpSourceAccount op={op}/>
374
- </>
375
- return <>
376
- <OpSourceAccount op={op}/> merged into account <AccountAddress account={destination}/>
377
- </>
378
- }
379
-
380
- /**
381
- * Type: 9
382
- * @param {OperationDescriptor} op
383
- * @param {Boolean} compact
384
- * @constructor
385
- */
386
- function InflationDescriptionView({op, compact}) {
387
- if (op.isEphemeral)
388
- return <>
389
- <b>Initiate inflation</b><OpSourceAccount op={op}/>
390
- </>
391
- return <>
392
- <OpSourceAccount op={op}/> initiated inflation
393
- </>
394
- }
395
-
396
- /**
397
- * Type: 10
398
- * @param {OperationDescriptor} op
399
- * @param {Boolean} compact
400
- * @constructor
401
- */
402
- function ManageDataDescriptionView({op, compact}) {
403
- const {name, value, effects} = op.operation
404
- const dataEntryDesc = <span className="word-break condensed text-small">
405
- <code>"{name}"</code>
406
- {!!value && <> = <code>"{value.toString()}"</code></>}
407
- </span>
408
-
409
- if (op.isEphemeral) {
410
- const descr = !value ? 'Delete' : 'Set'
411
- return <>
412
- <b>{descr} data entry</b> {dataEntryDesc}<OpSourceAccount op={op}/>
413
- </>
414
- }
415
- let descr = !value ? 'deleted' : 'set'
416
- if (effects.some(e => e.type === 'dataEntryUpdated')) {
417
- descr = 'updated'
418
- }
419
- return <>
420
- <OpSourceAccount op={op}/> {descr} data entry {dataEntryDesc}
421
- </>
422
- }
423
-
424
- /**
425
- * Type: 11
426
- * @param {OperationDescriptor} op
427
- * @param {Boolean} compact
428
- * @constructor
429
- */
430
- function BumpSequenceDescriptionView({op, compact}) {
431
- const {bumpTo} = op.operation
432
- if (op.isEphemeral)
433
- return <>
434
- <b>Bump account sequence</b> to {bumpTo}<OpSourceAccount op={op}/>
435
- </>
436
- return <>
437
- <OpSourceAccount op={op}/> bumped account sequence to {bumpTo}
438
- </>
439
- }
440
-
441
- /**
442
- * Type: 14
443
- * @param {OperationDescriptor} op
444
- * @param {Boolean} compact
445
- * @constructor
446
- */
447
- function CreateClaimableBalanceDescriptionView({op, compact}) {
448
- const {asset, amount, claimants, effects} = op.operation
449
- const balanceId = effects.find(e => e.type === 'claimableBalanceCreated')?.balance
450
- if (op.isEphemeral)
451
- return <>
452
- <b>Create claimable balance</b> <Amount amount={amount} asset={AssetDescriptor.parse(asset)} issuer={!compact}/>
453
- <ClaimableBalanceId balance={balanceId}/>
454
- <OpSourceAccount op={op}/>{' '}
455
- claimable by <ClaimableBalanceClaimants claimants={claimants}/>
456
-
457
- </>
458
- return <>
459
- <OpSourceAccount op={op}/> created claimable balance
460
- <ClaimableBalanceId balance={balanceId}/>{' '}
461
- <Amount amount={amount} asset={AssetDescriptor.parse(asset)} issuer={!compact}/>{' '}
462
- claimable by <ClaimableBalanceClaimants claimants={claimants}/>
463
- </>
464
- }
465
-
466
- /**
467
- * Type: 15
468
- * @param {OperationDescriptor} op
469
- * @param {Boolean} compact
470
- * @constructor
471
- */
472
- function ClaimClaimableBalanceDescriptionView({op, compact}) {
473
- const {balanceId} = op.operation
474
- if (op.isEphemeral)
475
- return <>
476
- <b>Claim balance</b> <ClaimableBalanceId balance={balanceId}/>
477
- <OpSourceAccount op={op}/>
478
- </>
479
- return <>
480
- <OpSourceAccount op={op}/> claimed balance <ClaimableBalanceId balance={balanceId}/>
481
- </>
482
- }
483
-
484
- /**
485
- * Type: 16
486
- * @param {OperationDescriptor} op
487
- * @param {Boolean} compact
488
- * @constructor
489
- */
490
- function BeginSponsoringFutureReservesDescriptionView({op, compact}) {
491
- const {sponsoredId} = op.operation
492
- if (op.isEphemeral)
493
- return <>
494
- <b>Sponsor reserves</b> for <AccountAddress account={sponsoredId}/><OpSourceAccount op={op}/>
495
- </>
496
- return <>
497
- <OpSourceAccount op={op}/> sponsored reserves for <AccountAddress account={sponsoredId}/>
498
- </>
499
- }
500
-
501
- /**
502
- * Type: 17
503
- * @param {OperationDescriptor} op
504
- * @param {Boolean} compact
505
- * @constructor
506
- */
507
- function EndSponsoringFutureReservesDescriptionView({op, compact}) {
508
- if (op.isEphemeral)
509
- return <>
510
- <b>Finish sponsoring reserves</b><OpSourceAccount op={op}/>
511
- </>
512
- return <>
513
- Finished sponsoring reserves for <OpSourceAccount op={op}/>
514
- </>
515
- }
516
-
517
- /**
518
- * Type: 18
519
- * @param {OperationDescriptor} op
520
- * @param {Boolean} compact
521
- * @constructor
522
- */
523
- function RevokeAccountSponsorshipDescriptionView({op, compact}) {
524
- const {account} = op.operation
525
- if (op.isEphemeral)
526
- return <>
527
- <b>Revoke sponsorship</b> for account <AccountAddress account={account}/><OpSourceAccount op={op}/>
528
- </>
529
- return <>
530
- <OpSourceAccount op={op}/> revoked sponsorship for account <AccountAddress account={account}/>
531
- </>
532
- }
533
-
534
- /**
535
- * Type: 18
536
- * @param {OperationDescriptor} op
537
- * @param {Boolean} compact
538
- * @constructor
539
- */
540
- function RevokeSignerSponsorshipDescriptionView({op, compact}) {
541
- const {account, signer} = op.operation
542
- if (op.isEphemeral)
543
- return <>
544
- <b>Revoke sponsorship</b> on signer <SignerKey signer={signer} showWeight={false}/>{' '}
545
- for account <AccountAddress account={account}/>
546
- <OpSourceAccount op={op}/>
547
- </>
548
- return <>
549
- <OpSourceAccount op={op}/> revoked sponsorship on signer <SignerKey signer={signer} showWeight={false}/>
550
- {' '}for account <AccountAddress account={account}/>
551
- </>
552
- }
553
-
554
- /**
555
- * Type: 18
556
- * @param {OperationDescriptor} op
557
- * @param {Boolean} compact
558
- * @constructor
559
- */
560
- function RevokeTrustlineSponsorshipDescriptionView({op, compact}) {
561
- const {account, asset} = op.operation
562
- if (op.isEphemeral)
563
- return <>
564
- <b>Revoke sponsorship</b> on <AssetLink asset={asset} issuer={!compact}/> trustline for account{' '}
565
- <AccountAddress account={account}/><OpSourceAccount op={op}/>
566
- </>
567
- return <>
568
- <OpSourceAccount op={op}/> revoked sponsorship on <AssetLink asset={asset} issuer={!compact}/> trustline for account{' '}
569
- <AccountAddress account={account}/>
570
- </>
571
- }
572
-
573
- /**
574
- * Type: 18
575
- * @param {OperationDescriptor} op
576
- * @param {Boolean} compact
577
- * @constructor
578
- */
579
- function RevokeOfferSponsorshipDescriptionView({op, compact}) {
580
- const {seller, offerId} = op.operation
581
- if (op.isEphemeral)
582
- return <>
583
- <b>Revoke sponsorship</b> on offer <OfferLink offer={offerId}/> for account <AccountAddress account={seller}/>
584
- <OpSourceAccount op={op}/>
585
- </>
586
- return <>
587
- <OpSourceAccount op={op}/> revoked sponsorship on offer <OfferLink offer={offerId}/>{' '}
588
- for account <AccountAddress account={seller}/>
589
- </>
590
- }
591
-
592
- /**
593
- * Type: 18
594
- * @param {OperationDescriptor} op
595
- * @param {Boolean} compact
596
- * @constructor
597
- */
598
- function RevokeDataSponsorshipDescriptionView({op, compact}) {
599
- const {account, name} = op.operation
600
- if (op.isEphemeral)
601
- return <>
602
- <b>Revoke sponsorship</b> on data entry <code>{name}</code> for account <AccountAddress account={account}/>
603
- <OpSourceAccount op={op}/>
604
- </>
605
- return <>
606
- <OpSourceAccount op={op}/> revoked sponsorship on data entry <code>{name}</code> for account{' '}
607
- <AccountAddress account={account}/>
608
- </>
609
- }
610
-
611
- /**
612
- * Type: 18
613
- * @param {OperationDescriptor} op
614
- * @param {Boolean} compact
615
- * @constructor
616
- */
617
- function RevokeClaimableBalanceSponsorshipDescriptionView({op, compact}) {
618
- const {balanceId} = op.operation
619
- if (op.isEphemeral)
620
- return <>
621
- <b>Revoke sponsorship</b> on claimable balance <ClaimableBalanceId balance={balanceId}/>
622
- <OpSourceAccount op={op}/>
623
- </>
624
- return <>
625
- <OpSourceAccount op={op}/> revoked sponsorship on claimable balance <ClaimableBalanceId balance={balanceId}/>
626
- </>
627
- }
628
-
629
- /**
630
- * Type: 18
631
- * @param {OperationDescriptor} op
632
- * @param {Boolean} compact
633
- * @constructor
634
- */
635
- function RevokeLiquidityPoolSponsorshipDescriptionView({op, compact}) {
636
- const {liquidityPoolId} = op.operation
637
- if (op.isEphemeral)
638
- return <>
639
- <b>Revoke sponsorship</b> on liquidity pool <AssetLink asset={liquidityPoolId} issuer={!compact}/>
640
- <OpSourceAccount op={op}/>
641
- </>
642
- return <>
643
- <OpSourceAccount op={op}/> revoked sponsorship on liquidity pool <AssetLink asset={liquidityPoolId} issuer={!compact}/>
644
- </>
645
- }
646
-
647
- /**
648
- * Type: 19
649
- * @param {OperationDescriptor} op
650
- * @param {Boolean} compact
651
- * @constructor
652
- */
653
- function ClawbackDescriptionView({op, compact}) {
654
- const {from, amount, asset} = op.operation
655
- if (op.isEphemeral)
656
- return <>
657
- <b>Clawback</b> <Amount amount={amount} asset={asset} issuer={!compact}/> from <AccountAddress account={from}/>
658
- <OpSourceAccount op={op}/>
659
- </>
660
- return <>
661
- <OpSourceAccount op={op}/> clawedback <Amount asset={asset} amount={amount} issuer={!compact}/> from <AccountAddress
662
- account={from}/>
663
- </>
664
- }
665
-
666
- /**
667
- * Type: 20
668
- * @param {OperationDescriptor} op
669
- * @param {Boolean} compact
670
- * @constructor
671
- */
672
- function ClawbackClaimableBalanceDescriptionView({op, compact}) {
673
- const {balanceId} = op.operation
674
- if (op.isEphemeral)
675
- return <>
676
- <b>Clawback claimable balance</b> <ClaimableBalanceId balance={balanceId}/><OpSourceAccount op={op}/>
677
- </>
678
- return <>
679
- <OpSourceAccount op={op}/> clawedback claimable balance <ClaimableBalanceId balance={balanceId}/>
680
- </>
681
- }
682
-
683
- /**
684
- * Type: 21
685
- * @param {OperationDescriptor} op
686
- * @param {Boolean} compact
687
- * @constructor
688
- */
689
- function SetTrustLineFlagsDescriptionView({op, compact}) {
690
- const {trustor, asset, flags} = op.operation
691
- const flagsMapping = {
692
- authorized: 'authorized',
693
- authorizedToMaintainLiabilities: 'authorized to maintain liabilities',
694
- clawbackEnabled: 'clawback enabled'
695
- }
696
- const setFlags = []
697
- const clearFlags = []
698
- for (let flag of Object.keys(flagsMapping)) {
699
- const container = flags[flag] ? setFlags : clearFlags
700
- container.push(flagsMapping[flag])
701
- }
702
- const flagsInfo = <>
703
- <code>[{setFlags.join(', ')}]</code>
704
- {clearFlags.length > 0 && <span>, clear{!op.isEphemeral && 'ed'} flags <code>[{clearFlags.join(', ')}]</code></span>}{' '}
705
- on asset <AssetLink asset={asset} issuer={!compact}/> for account <AccountAddress account={trustor}/>
706
- </>
707
- if (op.isEphemeral)
708
- return <>
709
- <b>Set trustline flags</b> {flagsInfo}<OpSourceAccount op={op}/>
710
- </>
711
- return <>
712
- <OpSourceAccount op={op}/> set trustline flags {flagsInfo}
713
- </>
714
- }
715
-
716
- /**
717
- * Type: 22
718
- * @param {OperationDescriptor} op
719
- * @param {Boolean} compact
720
- * @constructor
721
- */
722
- function DepositLiquidityDescriptionView({op, compact}) {
723
- const {liquidityPoolId, maxAmountA, maxAmountB, minPrice, maxPrice} = op.operation
724
- const meta = useAssetMeta(liquidityPoolId)
725
- const [assetA, assetB] = meta?.assets.map(a => a.asset) || ['tokens A', 'tokens B']
726
- const depositInfo = <>
727
- <Amount asset={assetA} amount={maxAmountA} issuer={!compact}/> and {' '}
728
- <Amount asset={assetB} amount={maxAmountB} issuer={!compact}/> to the pool{' '}
729
- <AssetLink asset={liquidityPoolId} issuer={!compact}/>{' '}
730
- {!compact && <span className="dimmed">(price range {formatPrice(minPrice)} - {formatPrice(maxPrice)})</span>}
731
- </>
732
- if (op.isEphemeral)
733
- return <>
734
- <b>Deposit liquidity</b> {depositInfo}<OpSourceAccount op={op}/>
735
- </>
736
- return <>
737
- <OpSourceAccount op={op}/> deposited liquidity {depositInfo}
738
- </>
739
- }
740
-
741
- /**
742
- * Type: 23
743
- * @param {OperationDescriptor} op
744
- * @param {Boolean} compact
745
- * @constructor
746
- */
747
- function WithdrawLiquidityDescriptionView({op, compact}) {
748
- const {liquidityPoolId, amount, minAmountA, minAmountB} = op.operation
749
- const meta = useAssetMeta(liquidityPoolId)
750
- const [assetA, assetB] = meta?.assets.map(a => a.asset) || ['tokens A', 'tokens B']
751
- const withdrawInfo = <>
752
- {formatWithAutoPrecision(amount)} shares from the pool <AssetLink asset={liquidityPoolId} issuer={!compact}/>{' '}
753
- {!compact && <span className="dimmed">
754
- (minimum <Amount asset={assetA} amount={minAmountA}/> + <Amount asset={assetB} amount={minAmountB}/>)
755
- </span>}
756
- </>
757
- if (op.isEphemeral)
758
- return <>
759
- <b>Withdraw liquidity</b> {withdrawInfo}<OpSourceAccount op={op}/>
760
- </>
761
- return <>
762
- <OpSourceAccount op={op}/> withdrew {withdrawInfo}
763
- </>
764
- }
765
-
766
- /**
767
- * Type: 24
768
- * @param {OperationDescriptor} op
769
- * @param {Boolean} compact
770
- * @constructor
771
- */
772
- function InvokeHostFunctionView({op, compact}) {
773
- const network = useStellarNetwork()
774
- const {func} = op.operation
775
- const value = func.value()
776
- switch (func.arm()) {
777
- case 'invokeContract':
778
- const contractAddress = xdrParserUtils.xdrParseScVal(value._attributes.contractAddress)
779
- const sac = op.operation.sacMap?.[contractAddress]
780
- const invocationArgs = {
781
- contract: contractAddress,
782
- func: value.functionName().toString(),
783
- args: value.args(),
784
- sac
785
- }
786
- if (op.isEphemeral)
787
- return <>
788
- <b>Invoke contract</b> <AccountAddress account={contractAddress}/>{' '}
789
- <InvocationInfoView {...invocationArgs}/>
790
- <OpSourceAccount op={op}/>
791
- </>
792
- const invocationEffect = op.operation.effects.find(e => e.type === 'contractInvoked' && e.contract === contractAddress)
793
- return <>
794
- <OpSourceAccount op={op}/> invoked contract <AccountAddress account={contractAddress}/>{' '}
795
- <InvocationInfoView {...invocationArgs} result={invocationEffect?.result}/>
796
- </>
797
- case 'wasm':
798
- const wasmCode = value.toString('base64')
799
- const codeReference = <>
800
- <code title={wasmCode}>{shortenString(value.toString('base64'), 16)}</code>
801
- <CopyToClipboard text={wasmCode}/>
802
- </>
803
- if (op.isEphemeral)
804
- return <>
805
- <b>Upload contract code</b> {codeReference}<OpSourceAccount op={op}/>
806
- </>
807
- return <>
808
- <OpSourceAccount op={op}/> uploaded contract code {codeReference}
809
- </>
810
- case 'createContract':
811
- case 'createContractV2':
812
- const preimage = value.contractIdPreimage()
813
- const executable = value.executable()
814
- const executableType = executable.switch().name
815
- const contract = contractPreimageEncoder.contractIdFromPreimage(preimage, Networks[network.toUpperCase()])
816
- let contractProps
817
- switch (executableType) {
818
- case 'contractExecutableWasm':
819
- const wasmHash = executable.wasmHash().toString('hex')
820
- contractProps = <>
821
- WASM code <code title={wasmHash}>{shortenString(wasmHash, 16)}</code>
822
- <CopyToClipboard text={wasmHash}/>
823
- </>
824
- break
825
- case 'contractExecutableStellarAsset':
826
- const preimageParams = preimage.value()
827
- switch (preimage.switch().name) {
828
- case 'contractIdPreimageFromAddress':
829
- const issuerAddress = xdrParserUtils.xdrParseAccountAddress(preimageParams.address().value())
830
- const salt = preimageParams.salt().toString('base64')
831
- contractProps = <>address <AccountAddress account={issuerAddress}/> with salt {salt}</>
832
- break
833
- case 'contractIdPreimageFromAsset':
834
- contractProps = <>asset <AssetLink asset={xdrParserUtils.xdrParseAsset(preimageParams)}/></>
835
- break
836
- }
837
- break
838
- }
839
- if (func.arm() === 'createContractV2') {
840
- const args = value.constructorArgs() //array
841
- if (args.length > 0) {
842
- contractProps = <>{contractProps} with <InvocationInfoView func="__constructor" args={args}/></>
843
- }
844
- }
845
- if (op.isEphemeral)
846
- return <>
847
- <b>Create contract</b> <AccountAddress account={contract}/> from {contractProps}<OpSourceAccount op={op}/>
848
- </>
849
- return <>
850
- <OpSourceAccount op={op}/> created contract <AccountAddress account={contract}/> from {contractProps}
851
- </>
852
- break
853
- default:
854
- return <>Unknown invocation type</>
855
- }
856
- }
857
-
858
- /**
859
- * Type: 25
860
- * @param {OperationDescriptor} op
861
- * @param {Boolean} compact
862
- * @constructor
863
- */
864
- function ExtendFootprintTTLView({op}) {
865
- const {extendTo} = op.operation
866
- if (op.isEphemeral)
867
- return <>
868
- <b>Extend ledger state expiration</b> to sequence {extendTo}
869
- <OpSourceAccount op={op}/>
870
- </>
871
- return <>
872
- <OpSourceAccount op={op}/> extended ledger state expiration to sequence {extendTo}
873
- </>
874
- }
875
-
876
- /**
877
- * Type: 26
878
- * @param {OperationDescriptor} op
879
- * @param {Boolean} compact
880
- * @constructor
881
- */
882
- function RestoreFootprintView({op}) {
883
- const {ledgersToExpire} = op.operation
884
- if (op.isEphemeral)
885
- return <>
886
- <b>Restore ledger state entry</b> <OpSourceAccount op={op}/>
887
- </>
888
- return <>
889
- <OpSourceAccount op={op}/> restored ledger state entry
890
- </>
891
- }
892
-
893
- const typeMapping = {
894
- createAccount: CreateAccountDescriptionView,
895
- payment: PaymentDescriptionView,
896
- pathPaymentStrictReceive: PathPaymentDescriptionView,
897
- manageSellOffer: ManageOfferDescriptionView,
898
- createPassiveSellOffer: ManageOfferDescriptionView,
899
- setOptions: SetOptionsDescriptionView,
900
- changeTrust: ChangeTrustDescriptionView,
901
- allowTrust: AllowTrustDescriptionView,
902
- accountMerge: MergeAccountDescriptionView,
903
- inflation: InflationDescriptionView,
904
- manageData: ManageDataDescriptionView,
905
- bumpSequence: BumpSequenceDescriptionView,
906
- manageBuyOffer: ManageOfferDescriptionView,
907
- pathPaymentStrictSend: PathPaymentDescriptionView,
908
- createClaimableBalance: CreateClaimableBalanceDescriptionView,
909
- claimClaimableBalance: ClaimClaimableBalanceDescriptionView,
910
- beginSponsoringFutureReserves: BeginSponsoringFutureReservesDescriptionView,
911
- endSponsoringFutureReserves: EndSponsoringFutureReservesDescriptionView,
912
- revokeAccountSponsorship: RevokeAccountSponsorshipDescriptionView,
913
- revokeTrustlineSponsorship: RevokeTrustlineSponsorshipDescriptionView,
914
- revokeOfferSponsorship: RevokeOfferSponsorshipDescriptionView,
915
- revokeDataSponsorship: RevokeDataSponsorshipDescriptionView,
916
- revokeClaimableBalanceSponsorship: RevokeClaimableBalanceSponsorshipDescriptionView,
917
- revokeLiquidityPoolSponsorship: RevokeLiquidityPoolSponsorshipDescriptionView,
918
- revokeSignerSponsorship: RevokeSignerSponsorshipDescriptionView,
919
- clawback: ClawbackDescriptionView,
920
- clawbackClaimableBalance: ClawbackClaimableBalanceDescriptionView,
921
- setTrustLineFlags: SetTrustLineFlagsDescriptionView,
922
- liquidityPoolDeposit: DepositLiquidityDescriptionView,
923
- liquidityPoolWithdraw: WithdrawLiquidityDescriptionView,
924
- invokeHostFunction: InvokeHostFunctionView,
925
- extendFootprintTtl: ExtendFootprintTTLView,
926
- restoreFootprint: RestoreFootprintView
927
- }
928
-
929
- /**
930
- * Text description of a tx operation
931
- * @param {OperationDescriptor} op
932
- * @param {Boolean} [compact]
933
- * @constructor
934
- */
935
- export const OpDescriptionView = React.memo(function OpDescriptionView({op, compact = false}) {
936
- const render = typeMapping[op.operation.type]
937
- if (!render) {
938
- console.warn(`No operation text type mapping for operation ${op.operation.type}`)
939
- return null
940
- }
941
- return React.createElement(render, {op, compact})
942
- })
1
+ import React from 'react'
2
+ import {Networks, AuthRequiredFlag, AuthRevocableFlag, AuthClawbackEnabledFlag, AuthImmutableFlag} from '@stellar/stellar-base'
3
+ import {AssetDescriptor} from '@stellar-expert/asset-descriptor'
4
+ import {formatPrice, formatWithAutoPrecision, fromStroops, toStroops, shortenString} from '@stellar-expert/formatter'
5
+ import {xdrParserUtils, contractPreimageEncoder} from '@stellar-expert/tx-meta-effects-parser'
6
+ import {AccountAddress} from '../account/account-address'
7
+ import {AccountIdenticon} from '../account/identicon'
8
+ import {SignerKey} from '../account/signer-key'
9
+ import {Amount} from '../asset/amount'
10
+ import {AssetLink} from '../asset/asset-link'
11
+ import {useAssetMeta} from '../asset/asset-meta-hooks'
12
+ import {OfferLink} from '../ledger/ledger-entry-link'
13
+ import {formatExplorerLink} from '../ledger/ledger-entry-href-formatter'
14
+ import {ClaimableBalanceClaimants} from '../claimable-balance/claimable-balance-claimants'
15
+ import {CopyToClipboard} from '../interaction/copy-to-clipboard'
16
+ import {useStellarNetwork} from '../state/stellar-network-hooks'
17
+ import InvocationInfoView from '../contract/invocation-info-view'
18
+ import {ClaimableBalanceId} from '../claimable-balance/claimable-balance-id'
19
+
20
+ /**
21
+ * Detailed operation description within a transaction
22
+ * @param {OperationDescriptor} op
23
+ * @param {boolean} [compact]
24
+ * @constructor
25
+ */
26
+ export const OpDescriptionView = React.memo(function OpDescriptionView({op, compact = false}) {
27
+ const render = typeMapping[op.operation.type]
28
+ if (!render) {
29
+ console.warn(`No operation text type mapping for operation ${op.operation.type}`)
30
+ return null
31
+ }
32
+ return React.createElement(render, {op, compact})
33
+ })
34
+
35
+ /**
36
+ * Renders operation source account
37
+ * @param {OperationDescriptor} op
38
+ * @param {boolean} compact
39
+ * @constructor
40
+ */
41
+ function OpSourceAccount({op, compact}) {
42
+ const network = useStellarNetwork()
43
+ const {source} = op.operation
44
+ const txSource = op.tx.tx.source
45
+ if (op.isEphemeral && (!source || source === txSource))
46
+ return null
47
+ const predefined = getAccountPredefinedDisplayName(source)
48
+ const accountItem = predefined ?
49
+ <a href={formatExplorerLink('account', source, network)} target="_blank" rel="nofollow noreferrer">
50
+ <AccountIdenticon address={source}/> {predefined}
51
+ </a> :
52
+ <AccountAddress account={source}/>
53
+
54
+ if (!op.isEphemeral)
55
+ return accountItem
56
+ return <> for account {accountItem}</>
57
+ }
58
+
59
+
60
+ /**
61
+ * CreateAccount operation (type 0)
62
+ * @param {OperationDescriptor} op
63
+ * @param {boolean} compact
64
+ * @constructor
65
+ */
66
+ function CreateAccountDescriptionView({op, compact}) {
67
+ const {destination, startingBalance} = op.operation
68
+ return op.isEphemeral ? <>
69
+ <b>Create account</b> <AccountAddress account={destination}/>{' '}
70
+ with starting balance <Amount amount={startingBalance} asset="XLM" issuer={!compact}/>
71
+ <OpSourceAccount op={op}/>
72
+ </> : <>
73
+ <OpSourceAccount op={op}/> created account <AccountAddress account={destination}/>{' '}
74
+ with starting balance <Amount amount={startingBalance} asset="XLM" issuer={!compact}/>
75
+ </>
76
+ }
77
+
78
+ /**
79
+ * Payment operation (type 1)
80
+ * @param {OperationDescriptor} op
81
+ * @param {boolean} compact
82
+ * @constructor
83
+ */
84
+ function PaymentDescriptionView({op, compact}) {
85
+ const {destination, asset, amount} = op.operation
86
+ return op.isEphemeral ? <>
87
+ <b>Send</b> <Amount amount={amount} asset={asset}/> to <AccountAddress account={destination}/>
88
+ <OpSourceAccount op={op}/>
89
+ </> : <>
90
+ <OpSourceAccount op={op}/> sent{' '}
91
+ <Amount amount={amount} asset={AssetDescriptor.parse(asset)} issuer={!compact}/> to{' '}
92
+ <AccountAddress account={destination}/>
93
+ </>
94
+ }
95
+
96
+ /**
97
+ * Description view for PathPayment operations (types 2, 13)
98
+ * @param {OperationDescriptor} op
99
+ * @param {boolean} compact
100
+ * @constructor
101
+ */
102
+ function PathPaymentDescriptionView({op, compact}) {
103
+ let {source, destination, sendAsset, sendMax, sendAmount, destAsset, destAmount, destMin, path, effects} = op.operation
104
+ const isSwap = source === destination
105
+ let src = sendAmount || sendMax
106
+ let dst = destAmount || destMin
107
+ sendAsset = AssetDescriptor.parse(sendAsset)
108
+ destAsset = AssetDescriptor.parse(destAsset)
109
+ const sendAssetId = sendAsset.toFQAN()
110
+ const destAssetId = destAsset.toFQAN()
111
+ const pathData = <>
112
+ <i className="icon icon-shuffle color-primary"/>{' '}
113
+ {!compact && path.map((asset, i) => <span key={i + '-' + asset.toString()}>
114
+ <AssetLink asset={asset}/> <i className="icon icon-shuffle color-primary"/>{' '}
115
+ </span>)}
116
+ </>
117
+ if (op.isEphemeral)
118
+ return <>
119
+ <b>{isSwap ? 'Swap' : 'Send'}</b> <Amount amount={src} asset={sendAsset} issuer={!compact}/>{' '}
120
+ {pathData}
121
+ <Amount amount={dst} asset={destAsset} issuer={!compact}/>
122
+ {!isSwap && <> to <AccountAddress account={destination}/></>}
123
+ <OpSourceAccount op={op}/>
124
+ </>
125
+ if (sendMax !== undefined) {
126
+ if (isSwap && sendAssetId === destAssetId && effects.length) { //circular trades yield accountCredited/Debited effect
127
+ const changeEffect = effects.find(e => e.source === source && e.asset === sendAssetId && (e.type === 'accountCredited' || e.type === 'accountDebited'))
128
+ if (changeEffect) { //arbitrage successful, otherwise src = dst and no account changes happened
129
+ const change = BigInt(changeEffect.amount)
130
+ src = fromStroops(toStroops(destAmount) + (changeEffect.type === 'accountCredited' ? -change : change))
131
+ }
132
+ } else {
133
+ const debitedEffect = effects.find(e => e.source === source && e.type === 'accountDebited' && e.asset === sendAssetId)
134
+ if (debitedEffect) {
135
+ src = fromStroops(debitedEffect.amount)
136
+ }
137
+ }
138
+ }
139
+ if (destMin !== undefined) {
140
+ if (isSwap && sendAssetId === destAssetId && effects.length) { //circular trades yield accountCredited/Debited effect
141
+ const changeEffect = effects.find(e => e.source === source && e.asset === destAssetId && (e.type === 'accountCredited' || e.type === 'accountDebited'))
142
+ if (changeEffect) { //arbitrage successful, otherwise src = dst and no account changes happened
143
+ const change = BigInt(changeEffect.amount)
144
+ dst = fromStroops(toStroops(sendAmount) + (changeEffect.type === 'accountCredited' ? change : -change))
145
+ }
146
+ } else {
147
+ const creditedEffect = effects.find(e => e.source === destination && e.type === 'accountCredited' && e.asset === destAssetId)
148
+ if (creditedEffect) {
149
+ dst = fromStroops(creditedEffect.amount)
150
+ }
151
+ }
152
+ }
153
+ return <>
154
+ <OpSourceAccount op={op}/> {isSwap ? 'swapped ' : 'sent '}
155
+ <Amount amount={src} asset={sendAsset} issuer={!compact}/>{' '}
156
+ {!compact && sendMax > 0 && op.successful && <><span className="dimmed nowrap">
157
+ (max <Amount amount={sendMax} asset={AssetDescriptor.parse(sendAsset)}/>)
158
+ </span> </>}
159
+ {pathData}
160
+ <Amount amount={dst} asset={destAsset} issuer={!compact}/>{' '}
161
+ {!compact && destMin > 0 && op.successful && <><span className="dimmed nowrap">
162
+ (min <Amount amount={destMin} asset={destAsset}/>)
163
+ </span> </>}
164
+ {!isSwap && <>to <AccountAddress account={destination}/></>}
165
+ </>
166
+ }
167
+
168
+ /**
169
+ * Description view for ManageSellOffer/ManageBuyOffer/CreatePassiveSellOffer operations (types 3, 4, 12)
170
+ * @param {OperationDescriptor} op
171
+ * @param {boolean} compact
172
+ * @constructor
173
+ */
174
+ function ManageOfferDescriptionView({op, compact}) {
175
+ let {offerId, amount, buyAmount, selling, buying, price, type} = op.operation
176
+ const passive = type === 'createPassiveSellOffer'
177
+ const isCancelled = !passive && offerId > 0 && parseFloat(amount || buyAmount) === 0
178
+
179
+ const direction = type === 'manageBuyOffer' ? ' buy' : ' sell'
180
+ let description = ''
181
+ if (offerId > 0) { //manage existing offer
182
+ description = 'Update'
183
+ } else {
184
+ description = 'Create'
185
+ }
186
+ if (!op.isEphemeral) {
187
+ description += 'd'
188
+ }
189
+ if (passive) {
190
+ description += ' passive'
191
+ }
192
+ description += direction + ' offer'
193
+ const amountInfo = <>
194
+ {amount !== undefined ?
195
+ <Amount amount={amount} asset={selling} issuer={!compact}/> :
196
+ <AssetLink asset={selling} issuer={!compact}/>}{' '}
197
+ <i className="icon icon-shuffle color-primary"/>{' '}
198
+ {buyAmount !== undefined ?
199
+ <Amount amount={buyAmount} asset={buying} issuer={!compact}/> :
200
+ <AssetLink asset={buying} issuer={!compact}/>}{' '}
201
+ at <span className="nowrap">
202
+ {formatWithAutoPrecision(parseFloat(price))} {AssetDescriptor.parse(buying).toCurrency()}/{AssetDescriptor.parse(selling).toCurrency()}
203
+ </span>
204
+ </>
205
+ if (op.isEphemeral) {
206
+ if (isCancelled)
207
+ return <>
208
+ <b>Cancel DEX offer</b> #<OfferLink offer={offerId}/><OpSourceAccount op={op}/>
209
+ </>
210
+ return <>
211
+ <b>{description}</b> {offerId > 0 && <>#<OfferLink offer={offerId}/></>} {amountInfo}<OpSourceAccount op={op}/>
212
+ </>
213
+ }
214
+
215
+ if (isCancelled)
216
+ return <>
217
+ <OpSourceAccount op={op}/> cancelled DEX offer <OfferLink offer={offerId}/>
218
+ </>
219
+ return <>
220
+ <OpSourceAccount op={op}/> {description.toLowerCase()} {offerId > 0 && <OfferLink offer={offerId}/>} {amountInfo}
221
+ </>
222
+ }
223
+
224
+ function AccountFlags({flags}) {
225
+ if (!flags)
226
+ return null
227
+ const res = []
228
+ if (flags & AuthRequiredFlag) {
229
+ res.push('auth_required')
230
+ }
231
+ if (flags & AuthRevocableFlag) {
232
+ res.push('auth_revocable')
233
+ }
234
+ if (flags & AuthImmutableFlag) {
235
+ res.push('auth_immutable')
236
+ }
237
+ if (flags & AuthClawbackEnabledFlag) {
238
+ res.push('auth_clawback_enabled')
239
+ }
240
+ return <>
241
+ {res.map((f, i) => <span key={f}>{i > 0 && <>, </>}<code>{f}</code></span>)} flag{res.length > 1 && <>s</>}
242
+ </>
243
+ }
244
+
245
+ /**
246
+ * SetOptions operation (type 5)
247
+ * @param {OperationDescriptor} op
248
+ * @param {boolean} compact
249
+ * @constructor
250
+ */
251
+ function SetOptionsDescriptionView({op, compact}) {
252
+ const {
253
+ setFlags,
254
+ clearFlags,
255
+ homeDomain,
256
+ inflationDest,
257
+ lowThreshold,
258
+ medThreshold,
259
+ highThreshold,
260
+ signer,
261
+ masterWeight,
262
+ order,
263
+ txHash
264
+ } = op.operation
265
+
266
+ const settings = []
267
+ if (!!setFlags) {
268
+ settings.push(<>Set <AccountFlags flags={setFlags}/></>)
269
+ }
270
+ if (!!clearFlags) {
271
+ settings.push(<>Unset <AccountFlags flags={clearFlags}/></>)
272
+ }
273
+ if (homeDomain !== undefined) {
274
+ settings.push(homeDomain ? <>Set home domain <code className="nowrap">{homeDomain}</code></> : <>Reset home domain</>)
275
+ }
276
+ if (inflationDest !== undefined) {
277
+ settings.push(<>Set inflation destination to <AccountAddress account={inflationDest}/></>)
278
+ }
279
+ if (lowThreshold !== undefined || medThreshold !== undefined || highThreshold !== undefined) {
280
+ const thresholds = []
281
+ if (lowThreshold !== undefined) {
282
+ thresholds.push('low=' + lowThreshold)
283
+ }
284
+ if (medThreshold !== undefined) {
285
+ thresholds.push('medium=' + medThreshold)
286
+ }
287
+ if (highThreshold !== undefined) {
288
+ thresholds.push('high=' + highThreshold)
289
+ }
290
+ settings.push(<>Set thresholds {thresholds.map((t, i) => <span key={i + t}>{i > 0 && <>, </>}<code>{t}</code></span>)}</>)
291
+ }
292
+ if (masterWeight !== undefined) {
293
+ settings.push(<>Set master key weight to <code>{masterWeight}</code></>)
294
+ }
295
+ if (signer !== undefined) {
296
+ if (signer.weight > 0) {
297
+ settings.push(<>Add account signer <SignerKey signer={signer}/></>)
298
+ } else {
299
+ settings.push(<>Remove account signer <SignerKey signer={signer} showWeight={false}/></>)
300
+ }
301
+ }
302
+ const details = <div className="block-indent text-small">
303
+ {settings.map((s, i) => <div key={txHash + order + i}>{s}</div>)}
304
+ </div>
305
+ if (op.isEphemeral)
306
+ return <>
307
+ <b>Set account options</b><OpSourceAccount op={op}/>
308
+ {details}
309
+ </>
310
+ return <>
311
+ <OpSourceAccount op={op}/> updated account options.
312
+ <div className="block-indent">
313
+ {details}
314
+ </div>
315
+ </>
316
+ }
317
+
318
+ /**
319
+ * ChangeTrust operation (type 6)
320
+ * @param {OperationDescriptor} op
321
+ * @param {boolean} compact
322
+ * @constructor
323
+ */
324
+ function ChangeTrustDescriptionView({op, compact}) {
325
+ const {line, limit, effects} = op.operation
326
+ const trustAsset = AssetDescriptor.parse(line)
327
+ const established = limit > 0
328
+ if (op.isEphemeral) {
329
+ if (established)
330
+ return <>
331
+ <b>Establish trustline</b> to <AssetLink asset={trustAsset} issuer={!compact}/>
332
+ {limit !== '922337203685.4775807' && <> with limit <Amount amount={limit} asset={trustAsset} issuer={!compact}/></>}
333
+ <OpSourceAccount op={op}/>
334
+ </>
335
+ return <>
336
+ <b>Remove trustline</b> to <AssetLink asset={trustAsset} issuer={!compact}/><OpSourceAccount op={op}/>
337
+ </>
338
+ }
339
+ if (established) {
340
+ const description = effects.some(e => e.type === 'trustlineCreated') ? 'established' : 'updated'
341
+ return <>
342
+ <OpSourceAccount op={op}/> {description} trustline to <AssetLink asset={line} issuer={!compact}/>
343
+ {limit !== '922337203685.4775807' && <> with limit <Amount amount={limit} asset={line} issuer={!compact}/></>}
344
+ </>
345
+ }
346
+ return <>
347
+ <OpSourceAccount op={op}/> removed trustline to <AssetLink asset={line} issuer={!compact}/>
348
+ </>
349
+ }
350
+
351
+ /**
352
+ * AllowTrust operation (type 7)
353
+ * @param {OperationDescriptor} op
354
+ * @param {boolean} compact
355
+ * @constructor
356
+ */
357
+ function AllowTrustDescriptionView({op, compact}) {
358
+ const {source, assetCode, trustor, authorize} = op.operation
359
+ const asset = AssetDescriptor.parse({code: assetCode, issuer: source})
360
+
361
+ let description = authorize ? 'Authorize' : 'Deauthorize'
362
+ if (op.isEphemeral)
363
+ return <>
364
+ <b>{description} trustline</b> <AssetLink asset={asset} issuer={!compact}/> for account <AccountAddress account={trustor}/>
365
+ <OpSourceAccount op={op}/>
366
+ </>
367
+ return <>
368
+ <OpSourceAccount op={op}/> {description.toLowerCase() + 'd'} trustline{' '}
369
+ <AssetLink asset={asset} issuer={!compact}/> for account <AccountAddress account={trustor}/>
370
+ </>
371
+ }
372
+
373
+ /**
374
+ * AccountMerge operation (type 8)
375
+ * @param {OperationDescriptor} op
376
+ * @param {boolean} compact
377
+ * @constructor
378
+ */
379
+ function MergeAccountDescriptionView({op, compact}) {
380
+ const {destination} = op.operation
381
+ if (op.isEphemeral)
382
+ return <>
383
+ <b>Merge account</b> <OpSourceAccount op={op}/> into account <AccountAddress account={destination}/>
384
+ <OpSourceAccount op={op}/>
385
+ </>
386
+ return <>
387
+ <OpSourceAccount op={op}/> merged into account <AccountAddress account={destination}/>
388
+ </>
389
+ }
390
+
391
+ /**
392
+ * Inflation operation (type 9)
393
+ * @param {OperationDescriptor} op
394
+ * @param {boolean} compact
395
+ * @constructor
396
+ */
397
+ function InflationDescriptionView({op, compact}) {
398
+ if (op.isEphemeral)
399
+ return <>
400
+ <b>Initiate inflation</b><OpSourceAccount op={op}/>
401
+ </>
402
+ return <>
403
+ <OpSourceAccount op={op}/> initiated inflation
404
+ </>
405
+ }
406
+
407
+ /**
408
+ * ManageData operation (type 10)
409
+ * @param {OperationDescriptor} op
410
+ * @param {boolean} compact
411
+ * @constructor
412
+ */
413
+ function ManageDataDescriptionView({op, compact}) {
414
+ const {name, value, effects} = op.operation
415
+ const dataEntryDesc = <span className="word-break condensed text-small">
416
+ <code>"{name}"</code>
417
+ {!!value && <> = <code>"{value.toString()}"</code></>}
418
+ </span>
419
+
420
+ if (op.isEphemeral) {
421
+ const descr = !value ? 'Delete' : 'Set'
422
+ return <>
423
+ <b>{descr} data entry</b> {dataEntryDesc}<OpSourceAccount op={op}/>
424
+ </>
425
+ }
426
+ let descr = !value ? 'deleted' : 'set'
427
+ if (effects.some(e => e.type === 'dataEntryUpdated')) {
428
+ descr = 'updated'
429
+ }
430
+ return <>
431
+ <OpSourceAccount op={op}/> {descr} data entry {dataEntryDesc}
432
+ </>
433
+ }
434
+
435
+ /**
436
+ * BumpSequence operation (type 11)
437
+ * @param {OperationDescriptor} op
438
+ * @param {boolean} compact
439
+ * @constructor
440
+ */
441
+ function BumpSequenceDescriptionView({op, compact}) {
442
+ const {bumpTo} = op.operation
443
+ if (op.isEphemeral)
444
+ return <>
445
+ <b>Bump account sequence</b> to {bumpTo}<OpSourceAccount op={op}/>
446
+ </>
447
+ return <>
448
+ <OpSourceAccount op={op}/> bumped account sequence to {bumpTo}
449
+ </>
450
+ }
451
+
452
+ /**
453
+ * CreateClaimableBalance operation (type 14)
454
+ * @param {OperationDescriptor} op
455
+ * @param {boolean} compact
456
+ * @constructor
457
+ */
458
+ function CreateClaimableBalanceDescriptionView({op, compact}) {
459
+ const {asset, amount, claimants, effects} = op.operation
460
+ const balanceId = effects.find(e => e.type === 'claimableBalanceCreated')?.balance
461
+ if (op.isEphemeral)
462
+ return <>
463
+ <b>Create claimable balance</b> <Amount amount={amount} asset={AssetDescriptor.parse(asset)} issuer={!compact}/>
464
+ <ClaimableBalanceId balance={balanceId}/>
465
+ <OpSourceAccount op={op}/>{' '}
466
+ claimable by <ClaimableBalanceClaimants claimants={claimants}/>
467
+
468
+ </>
469
+ return <>
470
+ <OpSourceAccount op={op}/> created claimable balance
471
+ <ClaimableBalanceId balance={balanceId}/>{' '}
472
+ <Amount amount={amount} asset={AssetDescriptor.parse(asset)} issuer={!compact}/>{' '}
473
+ claimable by <ClaimableBalanceClaimants claimants={claimants}/>
474
+ </>
475
+ }
476
+
477
+ /**
478
+ * ClaimClaimableBalance operation (type 15)
479
+ * @param {OperationDescriptor} op
480
+ * @param {boolean} compact
481
+ * @constructor
482
+ */
483
+ function ClaimClaimableBalanceDescriptionView({op, compact}) {
484
+ const {balanceId} = op.operation
485
+ if (op.isEphemeral)
486
+ return <>
487
+ <b>Claim balance</b> <ClaimableBalanceId balance={balanceId}/>
488
+ <OpSourceAccount op={op}/>
489
+ </>
490
+ return <>
491
+ <OpSourceAccount op={op}/> claimed balance <ClaimableBalanceId balance={balanceId}/>
492
+ </>
493
+ }
494
+
495
+ /**
496
+ * BeginSponsoringFutureReserves operation (type 16)
497
+ * @param {OperationDescriptor} op
498
+ * @param {boolean} compact
499
+ * @constructor
500
+ */
501
+ function BeginSponsoringFutureReservesDescriptionView({op, compact}) {
502
+ const {sponsoredId} = op.operation
503
+ if (op.isEphemeral)
504
+ return <>
505
+ <b>Sponsor reserves</b> for <AccountAddress account={sponsoredId}/><OpSourceAccount op={op}/>
506
+ </>
507
+ return <>
508
+ <OpSourceAccount op={op}/> sponsored reserves for <AccountAddress account={sponsoredId}/>
509
+ </>
510
+ }
511
+
512
+ /**
513
+ * EndSponsoringFutureReserves operation (type 17)
514
+ * @param {OperationDescriptor} op
515
+ * @param {boolean} compact
516
+ * @constructor
517
+ */
518
+ function EndSponsoringFutureReservesDescriptionView({op, compact}) {
519
+ if (op.isEphemeral)
520
+ return <>
521
+ <b>Finish sponsoring reserves</b><OpSourceAccount op={op}/>
522
+ </>
523
+ return <>
524
+ Finished sponsoring reserves for <OpSourceAccount op={op}/>
525
+ </>
526
+ }
527
+
528
+ /**
529
+ * RevokeAccountSponsorship operation (type 18)
530
+ * @param {OperationDescriptor} op
531
+ * @param {boolean} compact
532
+ * @constructor
533
+ */
534
+ function RevokeAccountSponsorshipDescriptionView({op, compact}) {
535
+ const {account} = op.operation
536
+ if (op.isEphemeral)
537
+ return <>
538
+ <b>Revoke sponsorship</b> for account <AccountAddress account={account}/><OpSourceAccount op={op}/>
539
+ </>
540
+ return <>
541
+ <OpSourceAccount op={op}/> revoked sponsorship for account <AccountAddress account={account}/>
542
+ </>
543
+ }
544
+
545
+ /**
546
+ * RevokeSignerSponsorship operation (type 18)
547
+ * @param {OperationDescriptor} op
548
+ * @param {boolean} compact
549
+ * @constructor
550
+ */
551
+ function RevokeSignerSponsorshipDescriptionView({op, compact}) {
552
+ const {account, signer} = op.operation
553
+ if (op.isEphemeral)
554
+ return <>
555
+ <b>Revoke sponsorship</b> on signer <SignerKey signer={signer} showWeight={false}/>{' '}
556
+ for account <AccountAddress account={account}/>
557
+ <OpSourceAccount op={op}/>
558
+ </>
559
+ return <>
560
+ <OpSourceAccount op={op}/> revoked sponsorship on signer <SignerKey signer={signer} showWeight={false}/>
561
+ {' '}for account <AccountAddress account={account}/>
562
+ </>
563
+ }
564
+
565
+ /**
566
+ * RevokeTrustlineSponsorship operation (type 18)
567
+ * @param {OperationDescriptor} op
568
+ * @param {boolean} compact
569
+ * @constructor
570
+ */
571
+ function RevokeTrustlineSponsorshipDescriptionView({op, compact}) {
572
+ const {account, asset} = op.operation
573
+ if (op.isEphemeral)
574
+ return <>
575
+ <b>Revoke sponsorship</b> on <AssetLink asset={asset} issuer={!compact}/> trustline for account{' '}
576
+ <AccountAddress account={account}/><OpSourceAccount op={op}/>
577
+ </>
578
+ return <>
579
+ <OpSourceAccount op={op}/> revoked sponsorship on <AssetLink asset={asset} issuer={!compact}/> trustline for account{' '}
580
+ <AccountAddress account={account}/>
581
+ </>
582
+ }
583
+
584
+ /**
585
+ * RevokeOfferSponsorship operation (type 18)
586
+ * @param {OperationDescriptor} op
587
+ * @param {boolean} compact
588
+ * @constructor
589
+ */
590
+ function RevokeOfferSponsorshipDescriptionView({op, compact}) {
591
+ const {seller, offerId} = op.operation
592
+ if (op.isEphemeral)
593
+ return <>
594
+ <b>Revoke sponsorship</b> on offer <OfferLink offer={offerId}/> for account <AccountAddress account={seller}/>
595
+ <OpSourceAccount op={op}/>
596
+ </>
597
+ return <>
598
+ <OpSourceAccount op={op}/> revoked sponsorship on offer <OfferLink offer={offerId}/>{' '}
599
+ for account <AccountAddress account={seller}/>
600
+ </>
601
+ }
602
+
603
+ /**
604
+ * RevokeDataSponsorship operation (type 18)
605
+ * @param {OperationDescriptor} op
606
+ * @param {boolean} compact
607
+ * @constructor
608
+ */
609
+ function RevokeDataSponsorshipDescriptionView({op, compact}) {
610
+ const {account, name} = op.operation
611
+ if (op.isEphemeral)
612
+ return <>
613
+ <b>Revoke sponsorship</b> on data entry <code>{name}</code> for account <AccountAddress account={account}/>
614
+ <OpSourceAccount op={op}/>
615
+ </>
616
+ return <>
617
+ <OpSourceAccount op={op}/> revoked sponsorship on data entry <code>{name}</code> for account{' '}
618
+ <AccountAddress account={account}/>
619
+ </>
620
+ }
621
+
622
+ /**
623
+ * RevokeClaimableBalanceSponsorship operation (type 18)
624
+ * @param {OperationDescriptor} op
625
+ * @param {boolean} compact
626
+ * @constructor
627
+ */
628
+ function RevokeClaimableBalanceSponsorshipDescriptionView({op, compact}) {
629
+ const {balanceId} = op.operation
630
+ if (op.isEphemeral)
631
+ return <>
632
+ <b>Revoke sponsorship</b> on claimable balance <ClaimableBalanceId balance={balanceId}/>
633
+ <OpSourceAccount op={op}/>
634
+ </>
635
+ return <>
636
+ <OpSourceAccount op={op}/> revoked sponsorship on claimable balance <ClaimableBalanceId balance={balanceId}/>
637
+ </>
638
+ }
639
+
640
+ /**
641
+ * RevokeLiquidityPoolSponsorship operation (type 18)
642
+ * @param {OperationDescriptor} op
643
+ * @param {boolean} compact
644
+ * @constructor
645
+ */
646
+ function RevokeLiquidityPoolSponsorshipDescriptionView({op, compact}) {
647
+ const {liquidityPoolId} = op.operation
648
+ if (op.isEphemeral)
649
+ return <>
650
+ <b>Revoke sponsorship</b> on liquidity pool <AssetLink asset={liquidityPoolId} issuer={!compact}/>
651
+ <OpSourceAccount op={op}/>
652
+ </>
653
+ return <>
654
+ <OpSourceAccount op={op}/> revoked sponsorship on liquidity pool <AssetLink asset={liquidityPoolId} issuer={!compact}/>
655
+ </>
656
+ }
657
+
658
+ /**
659
+ * Clawback operation (type 19)
660
+ * @param {OperationDescriptor} op
661
+ * @param {boolean} compact
662
+ * @constructor
663
+ */
664
+ function ClawbackDescriptionView({op, compact}) {
665
+ const {from, amount, asset} = op.operation
666
+ if (op.isEphemeral)
667
+ return <>
668
+ <b>Clawback</b> <Amount amount={amount} asset={asset} issuer={!compact}/> from <AccountAddress account={from}/>
669
+ <OpSourceAccount op={op}/>
670
+ </>
671
+ return <>
672
+ <OpSourceAccount op={op}/> clawedback <Amount asset={asset} amount={amount} issuer={!compact}/> from <AccountAddress
673
+ account={from}/>
674
+ </>
675
+ }
676
+
677
+ /**
678
+ * ClawbackClaimableBalance operation (type 20)
679
+ * @param {OperationDescriptor} op
680
+ * @param {boolean} compact
681
+ * @constructor
682
+ */
683
+ function ClawbackClaimableBalanceDescriptionView({op, compact}) {
684
+ const {balanceId} = op.operation
685
+ if (op.isEphemeral)
686
+ return <>
687
+ <b>Clawback claimable balance</b> <ClaimableBalanceId balance={balanceId}/><OpSourceAccount op={op}/>
688
+ </>
689
+ return <>
690
+ <OpSourceAccount op={op}/> clawedback claimable balance <ClaimableBalanceId balance={balanceId}/>
691
+ </>
692
+ }
693
+
694
+ /**
695
+ * SetTrustLineFlags operation (type 21)
696
+ * @param {OperationDescriptor} op
697
+ * @param {boolean} compact
698
+ * @constructor
699
+ */
700
+ function SetTrustLineFlagsDescriptionView({op, compact}) {
701
+ const {trustor, asset, flags} = op.operation
702
+ const flagsMapping = {
703
+ authorized: 'authorized',
704
+ authorizedToMaintainLiabilities: 'authorized to maintain liabilities',
705
+ clawbackEnabled: 'clawback enabled'
706
+ }
707
+ const setFlags = []
708
+ const clearFlags = []
709
+ for (let flag of Object.keys(flagsMapping)) {
710
+ const container = flags[flag] ? setFlags : clearFlags
711
+ container.push(flagsMapping[flag])
712
+ }
713
+ const flagsInfo = <>
714
+ <code>[{setFlags.join(', ')}]</code>
715
+ {clearFlags.length > 0 && <span>, clear{!op.isEphemeral && 'ed'} flags <code>[{clearFlags.join(', ')}]</code></span>}{' '}
716
+ on asset <AssetLink asset={asset} issuer={!compact}/> for account <AccountAddress account={trustor}/>
717
+ </>
718
+ if (op.isEphemeral)
719
+ return <>
720
+ <b>Set trustline flags</b> {flagsInfo}<OpSourceAccount op={op}/>
721
+ </>
722
+ return <>
723
+ <OpSourceAccount op={op}/> set trustline flags {flagsInfo}
724
+ </>
725
+ }
726
+
727
+ /**
728
+ * LiquidityPoolDeposit operation (type 22)
729
+ * @param {OperationDescriptor} op
730
+ * @param {boolean} compact
731
+ * @constructor
732
+ */
733
+ function DepositLiquidityDescriptionView({op, compact}) {
734
+ const {liquidityPoolId, maxAmountA, maxAmountB, minPrice, maxPrice} = op.operation
735
+ const meta = useAssetMeta(liquidityPoolId)
736
+ const [assetA, assetB] = meta?.assets.map(a => a.asset) || ['tokens A', 'tokens B']
737
+ const depositInfo = <>
738
+ <Amount asset={assetA} amount={maxAmountA} issuer={!compact}/> and {' '}
739
+ <Amount asset={assetB} amount={maxAmountB} issuer={!compact}/> to the pool{' '}
740
+ <AssetLink asset={liquidityPoolId} issuer={!compact}/>{' '}
741
+ {!compact && <span className="dimmed">(price range {formatPrice(minPrice)} - {formatPrice(maxPrice)})</span>}
742
+ </>
743
+ if (op.isEphemeral)
744
+ return <>
745
+ <b>Deposit liquidity</b> {depositInfo}<OpSourceAccount op={op}/>
746
+ </>
747
+ return <>
748
+ <OpSourceAccount op={op}/> deposited liquidity {depositInfo}
749
+ </>
750
+ }
751
+
752
+ /**
753
+ * LiquidityPoolWithdraw operation (type 23)
754
+ * @param {OperationDescriptor} op
755
+ * @param {boolean} compact
756
+ * @constructor
757
+ */
758
+ function WithdrawLiquidityDescriptionView({op, compact}) {
759
+ const {liquidityPoolId, amount, minAmountA, minAmountB} = op.operation
760
+ const meta = useAssetMeta(liquidityPoolId)
761
+ const [assetA, assetB] = meta?.assets.map(a => a.asset) || ['tokens A', 'tokens B']
762
+ const withdrawInfo = <>
763
+ {formatWithAutoPrecision(amount)} shares from the pool <AssetLink asset={liquidityPoolId} issuer={!compact}/>{' '}
764
+ {!compact && <span className="dimmed">
765
+ (minimum <Amount asset={assetA} amount={minAmountA}/> + <Amount asset={assetB} amount={minAmountB}/>)
766
+ </span>}
767
+ </>
768
+ if (op.isEphemeral)
769
+ return <>
770
+ <b>Withdraw liquidity</b> {withdrawInfo}<OpSourceAccount op={op}/>
771
+ </>
772
+ return <>
773
+ <OpSourceAccount op={op}/> withdrew {withdrawInfo}
774
+ </>
775
+ }
776
+
777
+ /**
778
+ * InvokeHostFunction operation (type 24) — Soroban contract calls
779
+ * @param {OperationDescriptor} op
780
+ * @param {boolean} compact
781
+ * @constructor
782
+ */
783
+ function InvokeHostFunctionView({op, compact}) {
784
+ const network = useStellarNetwork()
785
+ const {func} = op.operation
786
+ const value = func.value()
787
+ switch (func.arm()) {
788
+ case 'invokeContract':
789
+ const contractAddress = xdrParserUtils.xdrParseScVal(value._attributes.contractAddress)
790
+ const sac = op.operation.sacMap?.[contractAddress]
791
+ const invocationArgs = {
792
+ contract: contractAddress,
793
+ func: value.functionName().toString(),
794
+ args: value.args(),
795
+ sac
796
+ }
797
+ if (op.isEphemeral)
798
+ return <>
799
+ <b>Invoke contract</b> <AccountAddress account={contractAddress}/>{' '}
800
+ <InvocationInfoView {...invocationArgs}/>
801
+ <OpSourceAccount op={op}/>
802
+ </>
803
+ const invocationEffect = op.operation.effects.find(e => e.type === 'contractInvoked' && e.contract === contractAddress)
804
+ return <>
805
+ <OpSourceAccount op={op}/> invoked contract <AccountAddress account={contractAddress}/>{' '}
806
+ <InvocationInfoView {...invocationArgs} result={invocationEffect?.result}/>
807
+ </>
808
+ case 'wasm':
809
+ const wasmCode = value.toString('base64')
810
+ const codeReference = <>
811
+ <code title={wasmCode}>{shortenString(value.toString('base64'), 16)}</code>
812
+ <CopyToClipboard text={wasmCode}/>
813
+ </>
814
+ if (op.isEphemeral)
815
+ return <>
816
+ <b>Upload contract code</b> {codeReference}<OpSourceAccount op={op}/>
817
+ </>
818
+ return <>
819
+ <OpSourceAccount op={op}/> uploaded contract code {codeReference}
820
+ </>
821
+ case 'createContract':
822
+ case 'createContractV2':
823
+ const preimage = value.contractIdPreimage()
824
+ const executable = value.executable()
825
+ const executableType = executable.switch().name
826
+ const contract = contractPreimageEncoder.contractIdFromPreimage(preimage, Networks[network.toUpperCase()])
827
+ let contractProps
828
+ switch (executableType) {
829
+ case 'contractExecutableWasm':
830
+ const wasmHash = executable.wasmHash().toString('hex')
831
+ contractProps = <>
832
+ WASM code <code title={wasmHash}>{shortenString(wasmHash, 16)}</code>
833
+ <CopyToClipboard text={wasmHash}/>
834
+ </>
835
+ break
836
+ case 'contractExecutableStellarAsset':
837
+ const preimageParams = preimage.value()
838
+ switch (preimage.switch().name) {
839
+ case 'contractIdPreimageFromAddress':
840
+ const issuerAddress = xdrParserUtils.xdrParseAccountAddress(preimageParams.address().value())
841
+ const salt = preimageParams.salt().toString('base64')
842
+ contractProps = <>address <AccountAddress account={issuerAddress}/> with salt {salt}</>
843
+ break
844
+ case 'contractIdPreimageFromAsset':
845
+ contractProps = <>asset <AssetLink asset={xdrParserUtils.xdrParseAsset(preimageParams)}/></>
846
+ break
847
+ }
848
+ break
849
+ }
850
+ if (func.arm() === 'createContractV2') {
851
+ const args = value.constructorArgs() //array
852
+ if (args.length > 0) {
853
+ contractProps = <>{contractProps} with <InvocationInfoView func="__constructor" args={args}/></>
854
+ }
855
+ }
856
+ if (op.isEphemeral)
857
+ return <>
858
+ <b>Create contract</b> <AccountAddress account={contract}/> from {contractProps}<OpSourceAccount op={op}/>
859
+ </>
860
+ return <>
861
+ <OpSourceAccount op={op}/> created contract <AccountAddress account={contract}/> from {contractProps}
862
+ </>
863
+ break
864
+ default:
865
+ return <>Unknown invocation type</>
866
+ }
867
+ }
868
+
869
+ /**
870
+ * ExtendFootprintTTL operation (type 25)
871
+ * @param {OperationDescriptor} op
872
+ * @param {boolean} compact
873
+ * @constructor
874
+ */
875
+ function ExtendFootprintTTLView({op}) {
876
+ const {extendTo} = op.operation
877
+ if (op.isEphemeral)
878
+ return <>
879
+ <b>Extend ledger state expiration</b> to sequence {extendTo}
880
+ <OpSourceAccount op={op}/>
881
+ </>
882
+ return <>
883
+ <OpSourceAccount op={op}/> extended ledger state expiration to sequence {extendTo}
884
+ </>
885
+ }
886
+
887
+ /**
888
+ * RestoreFootprint operation (type 26)
889
+ * @param {OperationDescriptor} op
890
+ * @param {boolean} compact
891
+ * @constructor
892
+ */
893
+ function RestoreFootprintView({op}) {
894
+ const {ledgersToExpire} = op.operation
895
+ if (op.isEphemeral)
896
+ return <>
897
+ <b>Restore ledger state entry</b> <OpSourceAccount op={op}/>
898
+ </>
899
+ return <>
900
+ <OpSourceAccount op={op}/> restored ledger state entry
901
+ </>
902
+ }
903
+
904
+
905
+ function getAccountPredefinedDisplayName(address) {
906
+ if (!window.predefinedAccountDisplayNames)
907
+ return undefined
908
+ return window.predefinedAccountDisplayNames[address]
909
+ }
910
+
911
+ const typeMapping = {
912
+ createAccount: CreateAccountDescriptionView,
913
+ payment: PaymentDescriptionView,
914
+ pathPaymentStrictReceive: PathPaymentDescriptionView,
915
+ manageSellOffer: ManageOfferDescriptionView,
916
+ createPassiveSellOffer: ManageOfferDescriptionView,
917
+ setOptions: SetOptionsDescriptionView,
918
+ changeTrust: ChangeTrustDescriptionView,
919
+ allowTrust: AllowTrustDescriptionView,
920
+ accountMerge: MergeAccountDescriptionView,
921
+ inflation: InflationDescriptionView,
922
+ manageData: ManageDataDescriptionView,
923
+ bumpSequence: BumpSequenceDescriptionView,
924
+ manageBuyOffer: ManageOfferDescriptionView,
925
+ pathPaymentStrictSend: PathPaymentDescriptionView,
926
+ createClaimableBalance: CreateClaimableBalanceDescriptionView,
927
+ claimClaimableBalance: ClaimClaimableBalanceDescriptionView,
928
+ beginSponsoringFutureReserves: BeginSponsoringFutureReservesDescriptionView,
929
+ endSponsoringFutureReserves: EndSponsoringFutureReservesDescriptionView,
930
+ revokeAccountSponsorship: RevokeAccountSponsorshipDescriptionView,
931
+ revokeTrustlineSponsorship: RevokeTrustlineSponsorshipDescriptionView,
932
+ revokeOfferSponsorship: RevokeOfferSponsorshipDescriptionView,
933
+ revokeDataSponsorship: RevokeDataSponsorshipDescriptionView,
934
+ revokeClaimableBalanceSponsorship: RevokeClaimableBalanceSponsorshipDescriptionView,
935
+ revokeLiquidityPoolSponsorship: RevokeLiquidityPoolSponsorshipDescriptionView,
936
+ revokeSignerSponsorship: RevokeSignerSponsorshipDescriptionView,
937
+ clawback: ClawbackDescriptionView,
938
+ clawbackClaimableBalance: ClawbackClaimableBalanceDescriptionView,
939
+ setTrustLineFlags: SetTrustLineFlagsDescriptionView,
940
+ liquidityPoolDeposit: DepositLiquidityDescriptionView,
941
+ liquidityPoolWithdraw: WithdrawLiquidityDescriptionView,
942
+ invokeHostFunction: InvokeHostFunctionView,
943
+ extendFootprintTtl: ExtendFootprintTTLView,
944
+ restoreFootprint: RestoreFootprintView
945
+ }