@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
|
-
{
|
|
8
|
-
|
|
9
|
-
|
|
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
|
+
}
|
package/package.json
CHANGED
|
@@ -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)
|
package/tx/tx-operations-list.js
CHANGED
|
@@ -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
|
|
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">
|