@stellar-expert/ui-framework 1.15.4 → 1.15.5

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.
@@ -1,942 +1,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
- 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})
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
942
  })