@stellar-expert/ui-framework 1.9.0

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