@stellar-expert/ui-framework 1.12.1 → 1.12.3

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,108 @@
1
+ import {formatWithPrecision} from '@stellar-expert/formatter'
2
+ import './soroban-tx-metrics.scss'
3
+
4
+ export default function SorobanTxMetricsView({metrics}) {
5
+ if (!metrics)
6
+ return null
7
+ return <div className="soroban-tx-metrics-info row micro-space text-tiny text-monospace condensed">
8
+ {Object.entries(parseMetrics(metrics)).map(([key, value]) => <div key={key} className="column column-25">
9
+ <span className="dimmed">{key}: </span><MetricValue value={value}/>
10
+ </div>)}
11
+ </div>
12
+ }
13
+
14
+ const MetricValue = React.memo(function ({value}) {
15
+ const match = /\D+$/.exec(value)
16
+ if (match)
17
+ return <>{value.substring(0, match.index)}<span className="dimmed value-suffix">{value.substring(match.index)}</span></>
18
+ return <>{value}</>
19
+ })
20
+
21
+ function parseMetrics(metrics) {
22
+ const res = {}
23
+ for (const [key, value] of Object.entries(metrics)) {
24
+ switch (key) {
25
+ case 'cpu_insn':
26
+ res['Instructions'] = formatWithPrecision(value, 0)
27
+ break
28
+ case 'emit_event':
29
+ if (value > 0) {
30
+ res['Emitted events'] = value.toString() + ' ' + formatBytes(value)
31
+ }
32
+ break
33
+ case 'invoke_time_nsecs':
34
+ res['Invoke time'] = formatWithPrecision(value / 1000, 3) + 'µs'
35
+ break
36
+ case 'mem_byte':
37
+ res['Memory usage'] = formatBytes(value)
38
+ break
39
+ case 'read_code_byte':
40
+ if (value > 0) {
41
+ res['Code read'] = formatBytes(value)
42
+ }
43
+ break
44
+ case 'write_code_byte':
45
+ if (value > 0) {
46
+ res['Code write'] = formatBytes(value)
47
+ }
48
+ break
49
+ case 'ledger_read_byte':
50
+ if (value > 0) {
51
+ res['Ledger read'] = formatBytes(value)
52
+ }
53
+ break
54
+ case 'ledger_write_byte':
55
+ if (value > 0) {
56
+ res['Ledger write'] = formatBytes(value)
57
+ }
58
+ break
59
+ case 'read_data_byte':
60
+ if (value > 0) {
61
+ res['Data read'] = formatBytes(value)
62
+ }
63
+ break
64
+ case 'write_data_byte':
65
+ if (value > 0) {
66
+ res['Data write'] = formatBytes(value)
67
+ }
68
+ break
69
+ case 'read_key_byte':
70
+ if (value > 0) {
71
+ res['Key read'] = formatBytes(value)
72
+ }
73
+ break
74
+ case 'write_key_byte':
75
+ if (value > 0) {
76
+ res['Key write'] = formatBytes(value)
77
+ }
78
+ break
79
+ case 'read_entry':
80
+ if (value > 0) {
81
+ res['Entries read'] = value.toString()
82
+ }
83
+ break
84
+ case 'write_entry':
85
+ if (value > 0) {
86
+ res['Entries write'] = value.toString()
87
+ }
88
+ break
89
+ case 'emit_event_byte': //ignore these metrics
90
+ case 'max_emit_event_byte':
91
+ case 'max_rw_code_byte':
92
+ case 'max_rw_data_byte':
93
+ case 'max_rw_key_byte':
94
+ case 'type': //ignore common effect props
95
+ case 'source':
96
+ case 'contract':
97
+ break
98
+ default:
99
+ console.log('Unsupported metric: ' + key)
100
+ break
101
+ }
102
+ }
103
+ return res
104
+ }
105
+
106
+ function formatBytes(value) {
107
+ return formatWithPrecision(value, 3) + 'B'
108
+ }
@@ -0,0 +1,7 @@
1
+ .soroban-tx-metrics-info {
2
+ line-height: 1.2;
3
+
4
+ .value-suffix {
5
+ padding-left: 2px;
6
+ }
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stellar-expert/ui-framework",
3
- "version": "1.12.1",
3
+ "version": "1.12.3",
4
4
  "description": "StellarExpert shared UI components library",
5
5
  "main": "index.js",
6
6
  "module": "./index.js",
@@ -43,7 +43,7 @@ import TxMatcher from './tx-matcher'
43
43
  * @return {ParsedTxDetails}
44
44
  */
45
45
  export function parseTxDetails({network, txEnvelope, result, meta, id, context, createdAt, skipUnrelated, protocol}) {
46
- const parsedTx = parseTxOperationsMeta({network, tx: txEnvelope, meta, result, protocol})
46
+ const parsedTx = parseTxOperationsMeta({network, tx: txEnvelope, meta, result, protocol, processSystemEvents: true})
47
47
  const {tx, effects, operations, isEphemeral, failed} = parsedTx
48
48
  const txHash = tx.hash().toString('hex')
49
49
  const txMatcher = new TxMatcher(context, skipUnrelated)
@@ -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">