@stellar-expert/ui-framework 1.13.5 → 1.13.6

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.
@@ -63,15 +63,23 @@ export const ScVal = React.memo(function ScVal({value, nested = false, indent =
63
63
  case 'nonceKey':
64
64
  return <>{val.nonce()._value.toString()}<ScValType type="nonce"/></>
65
65
  case 'instance':
66
- return <>{val.executable.wasmHash().toString('hex')}<ScValType type="wasm"/></>
66
+ return <>{val._attributes.executable.wasmHash().toString('hex')}<ScValType type="wasm"/></>
67
67
  case 'error':
68
68
  const errMessage = value.toXDR('base64')
69
69
  return <><span className="condensed" title={errMessage}>{shortenString(errMessage, 50)}</span><ScValType type="error"/></>
70
70
  case 'contractId':
71
71
  return <AccountAddress account={xdrParserUtils.xdrParseContractAddress(value._value)}/>
72
72
  default:
73
- if (value.switch().name === 'scvVoid')
74
- return '()'
73
+ switch (value._switch.name) {
74
+ case 'scvVoid':
75
+ return '()'
76
+ case 'scvContractInstance':
77
+ return '<ContractInstance>'
78
+ case 'scvLedgerKeyContractInstance':
79
+ return '<LedgerKeyContractInstance>'
80
+ case 'scvLedgerKeyNonce':
81
+ return '<LedgerKeyNonce>'
82
+ }
75
83
  return <span className="dimmed">(unknown)</span>
76
84
  }
77
85
  })
@@ -14,6 +14,7 @@ import InvocationInfoView from '../contract/invocation-info-view'
14
14
 
15
15
  /**
16
16
  * @param {{}} effect
17
+ * @param {{}} operation
17
18
  * @return {JSX.Element}
18
19
  * @constructor
19
20
  */
@@ -211,9 +212,11 @@ export function EffectDescription({effect, operation}) {
211
212
  case 'inflation':
212
213
  return <>Inflation distribution initialized</>
213
214
  case 'contractCodeUploaded':
214
- return <>Contract code <ContractCodeWasm wasm={effect.wasm}/> uploaded</>
215
+ return <>Contract code <LedgerKeyHint effect={effect}><ContractCodeWasm wasm={effect.wasm}/></LedgerKeyHint> uploaded</>
215
216
  case 'contractCreated':
216
217
  return <>Contract <AccountAddress account={effect.contract}/> created <ContractDetails effect={effect}/></>
218
+ case 'contractUpdated':
219
+ return <>Contract <AccountAddress account={effect.contract}/> updated <ContractDetails effect={effect}/></>
217
220
  case 'contractInvoked':
218
221
  return <>{effect.depth > 0 &&
219
222
  <i className="icon-level-down text-tiny color-primary" style={{paddingLeft: (effect.depth - 1) + 'em'}}/>}
@@ -227,10 +230,11 @@ export function EffectDescription({effect, operation}) {
227
230
  case 'contractDataUpdated':
228
231
  return <>Contract <AccountAddress account={effect.owner}/>
229
232
  {effect.type === 'contractDataCreated' ? ' created ' : ' updated '} {effect.durability} data{' '}
230
- <ScVal value={effect.key}/> with value <ScVal value={effect.value}/>
233
+ <LedgerKeyHint effect={effect}><ScVal value={effect.key}/></LedgerKeyHint> with value <ScVal value={effect.value}/>
231
234
  </>
232
235
  case 'contractDataRemoved':
233
- return <>Contract <AccountAddress account={effect.owner}/> removed {effect.durability} data <ScVal value={effect.key}/></>
236
+ return <>Contract <AccountAddress account={effect.owner}/> removed {effect.durability}{' '}
237
+ data <LedgerKeyHint effect={effect}><ScVal value={effect.key}/></LedgerKeyHint></>
234
238
  case 'contractError':
235
239
  let errCode = effect.code
236
240
  if (errCode?.name) {
@@ -238,6 +242,12 @@ export function EffectDescription({effect, operation}) {
238
242
  }
239
243
  return <>Execution error {errCode ? <><code>{errCode}</code> </> : null}in <AccountAddress account={effect.contract}/>{': '}
240
244
  <code>{JSON.stringify(effect.details)}</code> </>
245
+ case 'setTtl':
246
+ return <>Time-to-live extended to ledger {effect.ttl} for {!!effect.owner && <AccountAddress account={effect.owner}/>}{' '}
247
+ {ledgerEntryKind[effect.kind]}{' '}
248
+ <CopyToClipboard text={effect.keyHash}>
249
+ <code title={effect.keyHash + ' - click to copy'}>{shortenString(effect.keyHash, 12)}</code>
250
+ </CopyToClipboard></>
241
251
  case 'feeCharged':
242
252
  return <><Amount asset="XLM" amount={effect.charged} adjust/> fee charged from <AccountAddress account={effect.source}/> (
243
253
  bid <Amount asset="XLM" amount={effect.bid} adjust/>)</>
@@ -300,5 +310,19 @@ function ContractDetails({effect}) {
300
310
  }
301
311
 
302
312
  function ContractCodeWasm({wasm}) {
303
- return <><code title={wasm}>{shortenString(wasm, 16)}</code><CopyToClipboard text={wasm}/></>
313
+ return <>
314
+ <code title={wasm}>{shortenString(wasm, 16)}</code>
315
+ <CopyToClipboard text={wasm}/>
316
+ </>
317
+ }
318
+
319
+ function LedgerKeyHint({effect, children}) {
320
+ if (!effect.keyHash)
321
+ return children
322
+ return <span title={'Ledger key ' + effect.keyHash}>{children}</span>
323
+ }
324
+
325
+ const ledgerEntryKind = {
326
+ contractData: 'contract state entry',
327
+ contractCode: 'contract code WASM'
304
328
  }
@@ -37,10 +37,19 @@ function sortContractEffects(effects) {
37
37
  if (aInvocation && bInvocation)
38
38
  return 0
39
39
  if (!aInvocation && !bInvocation) {
40
- const aData = a.type.startsWith('contractData')
41
- const bData = b.type.startsWith('contractData')
42
- if (aData === bData)
40
+ const aData = a.type === 'setTtl' || a.type.startsWith('contractData')
41
+ const bData = b.type === 'setTtl' || b.type.startsWith('contractData')
42
+ if (aData === bData) {
43
+ if (a.type === 'setTtl')
44
+ return 1
45
+ if (b.type === 'setTtl')
46
+ return -1
47
+ if (a.durability === 'instance' && a.durability !== b.durability)
48
+ return -1
49
+ if (b.durability === 'instance' && a.durability !== b.durability)
50
+ return 1
43
51
  return 0
52
+ }
44
53
  return aData ? 1 : -1
45
54
  }
46
55
  return aInvocation ? -1 : 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stellar-expert/ui-framework",
3
- "version": "1.13.5",
3
+ "version": "1.13.6",
4
4
  "description": "StellarExpert shared UI components library",
5
5
  "main": "index.js",
6
6
  "module": "./index.js",
@@ -22,7 +22,7 @@
22
22
  "@stellar-expert/contract-wasm-interface-parser": "^3.1.0",
23
23
  "@stellar-expert/formatter": "^2.3.0",
24
24
  "@stellar-expert/navigation": "github:stellar-expert/navigation#v1.0.2",
25
- "@stellar-expert/tx-meta-effects-parser": "5.7.1",
25
+ "@stellar-expert/tx-meta-effects-parser": "6.2.0",
26
26
  "classnames": ">=2",
27
27
  "prop-types": ">=15",
28
28
  "qrcode.react": "^3.1.0",
@@ -31,6 +31,11 @@
31
31
  background: var(--color-primary);
32
32
  border-radius: 50%;
33
33
 
34
+ &.failed {
35
+ filter: grayscale(1);
36
+ opacity: 0.7;
37
+ }
38
+
34
39
  &:before {
35
40
  content: '';
36
41
  display: block;
package/tx/op-icon.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import React from 'react'
2
+ import cn from 'classnames'
2
3
 
3
4
  const opIconMapping = {
4
5
  feeCharge: 'send-circle',
@@ -50,15 +51,19 @@ const opIconMapping = {
50
51
  /**
51
52
  * Transaction operation icon
52
53
  * @param {OperationDescriptor|'feeCharge'} op - Operation descriptor
54
+ * @param {Boolean} failed - Whether the transaction failed during execution
53
55
  * @constructor
54
56
  */
55
- export const OpIcon = React.memo(function OpIcon({op}) {
57
+ export const OpIcon = React.memo(function OpIcon({op, failed = false}) {
56
58
  let type
59
+ let title
57
60
  if (op === 'feeCharge') {
58
61
  type = 'feeCharge'
62
+ title = 'Transaction fees charge'
59
63
  } else {
60
64
  const {operation} = op
61
- type = operation.type
65
+ title = type = operation.type
66
+ title = title[0].toUpperCase() + title.substring(1) + ' operation'
62
67
  switch (type) {
63
68
  case 'payment':
64
69
  case 'accountMerge':
@@ -84,10 +89,11 @@ export const OpIcon = React.memo(function OpIcon({op}) {
84
89
  break
85
90
  }
86
91
  }
87
-
88
-
92
+ if (failed) {
93
+ title += ' - Transaction failed'
94
+ }
89
95
  const icon = opIconMapping[type]
90
- return <div className="op-icon">
96
+ return <div className={cn('op-icon', {failed})} title={title}>
91
97
  <i className={`icon-${icon}`}/>
92
98
  </div>
93
99
  })
@@ -48,7 +48,7 @@ const TxChargedFee = React.memo(function TxChargedFee({parsedTx, compact}) {
48
48
  return null
49
49
  return <div className="op-container">
50
50
  <div className="op-layout">
51
- <OpIcon op="feeCharge"/>
51
+ <OpIcon op="feeCharge" failed={!parsedTx.successful}/>
52
52
  <TxFeeEffect feeEffect={fee} compact={compact}/>
53
53
  {!!compact && !parsedTx.isEphemeral && <TxFeeAccountingChanges amount={fee.charged}/>}
54
54
  </div>
@@ -73,7 +73,7 @@ export const TxOperationsList = React.memo(function TxOperationsList({
73
73
  const [opsExpanded, setOpsExpanded] = useState(false)
74
74
  const toggleEffects = useCallback(e => setEffectsExpanded(e.expanded), [])
75
75
  const toggleAdditionalOps = useCallback(e => setOpsExpanded(e.expanded), [])
76
- let {operations} = parsedTx
76
+ let {operations, successful} = parsedTx
77
77
  let opdiff = 0
78
78
  if (!opsExpanded) {
79
79
  //filter operations if filtered output is requested
@@ -100,7 +100,7 @@ export const TxOperationsList = React.memo(function TxOperationsList({
100
100
  <div className="condensed">
101
101
  {operations.map((op, i) => <div className="op-container" key={op.txHash + op.order + op.isEphemeral}>
102
102
  <div className="op-layout">
103
- <OpIcon op={op}/>
103
+ <OpIcon op={op} failed={!successful}/>
104
104
  <div>
105
105
  <OpDescriptionView key={parsedTx.txHash + op.order} op={op} compact={compact}/>
106
106
  </div>