@stellar-expert/ui-framework 1.12.0 → 1.12.2

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.
@@ -11,6 +11,11 @@ import {Amount} from '../asset/amount'
11
11
  import {CopyToClipboard} from '../interaction/copy-to-clipboard'
12
12
  import {ScVal} from '../contract/sc-val'
13
13
 
14
+ /**
15
+ * @param {{}} effect
16
+ * @return {JSX.Element}
17
+ * @constructor
18
+ */
14
19
  export function EffectDescription({effect}) {
15
20
  switch (effect.type) {
16
21
  case 'accountCreated':
@@ -223,6 +228,9 @@ export function EffectDescription({effect}) {
223
228
  </>
224
229
  case 'contractDataRemoved':
225
230
  return <>Contract <AccountAddress account={effect.owner}/> removed data <ScVal value={effect.key}/></>
231
+ case 'contractError':
232
+ return <>Execution error {effect.code ? effect.code + ': ' : ''}"{effect.details[0]}"{' '}
233
+ <code>{JSON.stringify(effect.details.slice(1))}</code> in <AccountAddress account={effect.contract}/></>
226
234
  case 'feeCharged':
227
235
  return <><Amount asset="XLM" amount={effect.charged} adjust/> fee charged from <AccountAddress account={effect.source}/> (
228
236
  bid <Amount asset="XLM" amount={effect.bid} adjust/>)</>
@@ -1,11 +1,21 @@
1
1
  import React from 'react'
2
2
  import {EffectDescription} from './effect-description'
3
+ import SorobanTxMetricsView from './soroban-tx-metrics-view'
3
4
  import './op-effects.scss'
4
5
 
5
6
  export function OpEffectsView({effects}) {
7
+ if (!effects.length)
8
+ return <div className="op-effects">
9
+ <div className="dimmed">(no effects)</div>
10
+ </div>
6
11
  return <div className="op-effects">
7
- {!effects.length ?
8
- <div className="dimmed">(no effects)</div> :
9
- effects.map(e => <div><i className="icon-puzzle"/> <EffectDescription effect={e}/></div>)}
12
+ {effects.map((e, i) => {
13
+ if (e.type === 'contractMetrics')
14
+ return null
15
+ return <div key={i}>
16
+ <i className={e.type === 'contractError' ? 'icon-warning' : 'icon-puzzle'}/> <EffectDescription effect={e}/>
17
+ </div>
18
+ })}
19
+ <SorobanTxMetricsView metrics={effects.find(e => e.type === 'contractMetrics')}/>
10
20
  </div>
11
21
  }
@@ -0,0 +1,105 @@
1
+ import {formatWithPrecision} from '@stellar-expert/formatter'
2
+
3
+ export default function SorobanTxMetricsView({metrics}) {
4
+ if (!metrics)
5
+ return null
6
+ return <div className="text-tiny row micro-space text-monospace">
7
+ {Object.entries(parseMetrics(metrics)).map(([key, value])=><div key={key} className="column column-33">
8
+ <span className="dimmed">{key}: </span>{value}
9
+ </div>)}
10
+ </div>
11
+ }
12
+
13
+ function parseMetrics(metrics) {
14
+ const res = {}
15
+ for (const [key, value] of Object.entries(metrics)) {
16
+ switch (key) {
17
+ case 'cpu_insn':
18
+ res['CPU instructions'] = formatWithPrecision(value, 0)
19
+ break
20
+ case 'emit_event':
21
+ if (value > 0) {
22
+ res['Emitted events'] = value.toString()
23
+ }
24
+ break
25
+ case 'emit_event_byte':
26
+ if (value > 0) {
27
+ res['Emitted events'] += ' ' + formatBytes(value)
28
+ }
29
+ break
30
+ case 'invoke_time_nsecs':
31
+ res['Invoke time'] = formatWithPrecision(value / 1000, 3) + 'µs'
32
+ break
33
+ case 'mem_byte':
34
+ res['Memory utilization'] = formatBytes(value)
35
+ break
36
+ case 'read_code_byte':
37
+ if (value > 0) {
38
+ res['Code read'] = formatBytes(value)
39
+ }
40
+ break
41
+ case 'write_code_byte':
42
+ if (value > 0) {
43
+ res['Code write'] = formatBytes(value)
44
+ }
45
+ break
46
+ case 'ledger_read_byte':
47
+ if (value > 0) {
48
+ res['Ledger read'] = formatBytes(value)
49
+ }
50
+ break
51
+ case 'ledger_write_byte':
52
+ if (value > 0) {
53
+ res['Ledger write'] = formatBytes(value)
54
+ }
55
+ break
56
+ case 'read_data_byte':
57
+ if (value > 0) {
58
+ res['Data read'] = formatBytes(value)
59
+ }
60
+ break
61
+ case 'write_data_byte':
62
+ if (value > 0) {
63
+ res['Data write'] = formatBytes(value)
64
+ }
65
+ break
66
+ case 'read_key_byte':
67
+ if (value > 0) {
68
+ res['Key read'] = formatBytes(value)
69
+ }
70
+ break
71
+ case 'write_key_byte':
72
+ if (value > 0) {
73
+ res['Key write'] = formatBytes(value)
74
+ }
75
+ break
76
+ case 'read_entry':
77
+ if (value > 0) {
78
+ res['Entries read'] = value
79
+ }
80
+ break
81
+ case 'write_entry':
82
+ if (value > 0) {
83
+ res['Entries write'] = value
84
+ }
85
+ break
86
+ case 'max_emit_event_byte':
87
+ case 'max_rw_code_byte':
88
+ case 'max_rw_data_byte':
89
+ case 'max_rw_key_byte': //ignore these metrics
90
+ break
91
+ case 'type':
92
+ case 'source':
93
+ case 'contract': //ignore other effect params
94
+ break
95
+ default:
96
+ console.log('Unsupported metric: ' + key)
97
+ break
98
+ }
99
+ }
100
+ return res
101
+ }
102
+
103
+ function formatBytes(value) {
104
+ return formatWithPrecision(value, 3) + 'B'
105
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stellar-expert/ui-framework",
3
- "version": "1.12.0",
3
+ "version": "1.12.2",
4
4
  "description": "StellarExpert shared UI components library",
5
5
  "main": "index.js",
6
6
  "module": "./index.js",
@@ -39,10 +39,12 @@ import TxMatcher from './tx-matcher'
39
39
  * @param {TxFiltersContext} [context] - Filters applied to transactions search
40
40
  * @param {String} [createdAt] - Ledger execution timestamp
41
41
  * @param {Boolean} [skipUnrelated] - Ledger execution timestamp
42
+ * @param {Number} [protocol] – Specific Stellar protocol version for the executed transaction
42
43
  * @return {ParsedTxDetails}
43
44
  */
44
- export function parseTxDetails({network, txEnvelope, result, meta, id, context, createdAt, skipUnrelated}) {
45
- const {tx, effects, operations, isEphemeral, failed} = parseTxOperationsMeta({network, tx: txEnvelope, meta, result})
45
+ export function parseTxDetails({network, txEnvelope, result, meta, id, context, createdAt, skipUnrelated, protocol}) {
46
+ const parsedTx = parseTxOperationsMeta({network, tx: txEnvelope, meta, result, protocol, processSystemEvents: true})
47
+ const {tx, effects, operations, isEphemeral, failed} = parsedTx
46
48
  const txHash = tx.hash().toString('hex')
47
49
  const txMatcher = new TxMatcher(context, skipUnrelated)
48
50
  let parsedOps = OperationDescriptor.parseOperations(operations, txHash, isEphemeral, !isEphemeral && !failed)
@@ -94,7 +94,7 @@ export const TxOperationsList = React.memo(function TxOperationsList({
94
94
  return <div>
95
95
  {!compact && showEffects !== false &&
96
96
  <div className="tx-effects-toggle">
97
- <Spoiler micro active expanded={effectsExpanded} showLess="Hide operation effects" showMore="Show operation effects"
97
+ <Spoiler micro active expanded={effectsExpanded} showLess="Hide operation details" showMore="Show operation details"
98
98
  onChange={toggleEffects} style={{margin: '0.2em'}}/>
99
99
  </div>}
100
100
  <div className="condensed">