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