@stellar-expert/tx-meta-effects-parser 5.3.0 → 5.5.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stellar-expert/tx-meta-effects-parser",
3
- "version": "5.3.0",
3
+ "version": "5.5.0",
4
4
  "description": "Low-level effects parser for Stellar transaction results and meta XDR",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -9,7 +9,7 @@
9
9
  "author": "team@stellar.expert",
10
10
  "license": "MIT",
11
11
  "peerDependencies": {
12
- "@stellar/stellar-base": "^11.0.0"
12
+ "@stellar/stellar-base": "^12.0.0"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@babel/core": "^7.22.9",
@@ -61,13 +61,13 @@ class EventsAnalyzer {
61
61
  * @private
62
62
  */
63
63
  analyzeDiagnosticEvents() {
64
- const {diagnosticEvents} = this.effectsAnalyzer
64
+ const {diagnosticEvents, processSystemEvents} = this.effectsAnalyzer
65
65
  if (!diagnosticEvents)
66
66
  return
67
67
  //diagnostic events
68
68
  for (const evt of diagnosticEvents) {
69
- if (!evt.inSuccessfulContractCall())
70
- return //throw new UnexpectedTxMetaChangeError({type: 'diagnostic_event', action: 'failed'})
69
+ if (!processSystemEvents && !evt.inSuccessfulContractCall())
70
+ continue //throw new UnexpectedTxMetaChangeError({type: 'diagnostic_event', action: 'failed'})
71
71
  //parse event
72
72
  const event = evt.event()
73
73
  const contractId = event.contractId()
@@ -90,7 +90,7 @@ class EventsAnalyzer {
90
90
  if (type !== EVENT_TYPES.DIAGNOSTIC)
91
91
  return // skip non-diagnostic events
92
92
  const rawArgs = body.data()
93
- const parsedEvent = {
93
+ const funcCall = {
94
94
  type: effectTypes.contractInvoked,
95
95
  contract: xdrParseScVal(topics[1], true),
96
96
  function: xdrParseScVal(topics[2]),
@@ -99,21 +99,35 @@ class EventsAnalyzer {
99
99
  }
100
100
  //add the invocation to the call stack
101
101
  if (this.callStack.length) {
102
- parsedEvent.depth = this.callStack.length
102
+ funcCall.depth = this.callStack.length
103
103
  }
104
- this.callStack.push(parsedEvent)
105
- this.effectsAnalyzer.addEffect(parsedEvent)
104
+ this.callStack.push(funcCall)
105
+ this.effectsAnalyzer.addEffect(funcCall)
106
106
  break
107
107
  case 'fn_return':
108
108
  if (type !== EVENT_TYPES.DIAGNOSTIC)
109
109
  return // skip non-diagnostic events
110
110
  //attach execution result to the contract invocation event
111
- const funcCall = this.callStack.pop()
111
+ const lastFuncCall = this.callStack.pop()
112
112
  const result = body.data()
113
113
  if (result.switch().name !== 'scvVoid') {
114
- funcCall.result = result.toXDR('base64')
114
+ lastFuncCall.result = result.toXDR('base64')
115
115
  }
116
116
  break
117
+ case 'error':
118
+ if (type !== EVENT_TYPES.DIAGNOSTIC)
119
+ return // skip non-diagnostic events
120
+ this.effectsAnalyzer.addEffect({
121
+ type: effectTypes.contractError,
122
+ code: topics[1].value().value(),
123
+ details: processEventBodyValue(body.data())
124
+ })
125
+ break
126
+ case 'core_metrics':
127
+ if (type !== EVENT_TYPES.DIAGNOSTIC)
128
+ return // skip non-diagnostic events
129
+ this.effectsAnalyzer.addMetric(xdrParseScVal(topics[1]), parseInt(processEventBodyValue(body.data())))
130
+ break
117
131
  //handle standard token contract events
118
132
  //see https://github.com/stellar/rs-soroban-sdk/blob/main/soroban-sdk/src/token.rs
119
133
  case 'transfer': {
@@ -86,12 +86,14 @@ const effectTypes = {
86
86
  contractUpdated: 'contractUpdated',
87
87
 
88
88
  contractInvoked: 'contractInvoked',
89
+ contractError: 'contractError',
89
90
 
90
91
  contractDataCreated: 'contractDataCreated',
91
92
  contractDataUpdated: 'contractDataUpdated',
92
93
  contractDataRemoved: 'contractDataRemoved',
93
94
 
94
- contractEvent: 'contractEvent'
95
+ contractEvent: 'contractEvent',
96
+ contractMetrics: 'contractMetrics'
95
97
  }
96
98
 
97
99
  module.exports = effectTypes
@@ -9,7 +9,7 @@ const AssetSupplyAnalyzer = require('./aggregation/asset-supply-analyzer')
9
9
  const {UnexpectedTxMetaChangeError, TxMetaEffectParserError} = require('./errors')
10
10
 
11
11
  class EffectsAnalyzer {
12
- constructor({operation, meta, result, network, events, diagnosticEvents, mapSac}) {
12
+ constructor({operation, meta, result, network, events, diagnosticEvents, mapSac, processSystemEvents}) {
13
13
  //set execution context
14
14
  if (!operation.source)
15
15
  throw new TxMetaEffectParserError('Operation source is not explicitly defined')
@@ -21,6 +21,9 @@ class EffectsAnalyzer {
21
21
  this.events = events
22
22
  if (diagnosticEvents?.length) {
23
23
  this.diagnosticEvents = diagnosticEvents
24
+ if (processSystemEvents) {
25
+ this.processSystemEvents = true
26
+ }
24
27
  }
25
28
  this.network = network
26
29
  if (mapSac) {
@@ -69,10 +72,20 @@ class EffectsAnalyzer {
69
72
  */
70
73
  source = ''
71
74
  /**
72
- * @type {boolean}
75
+ * @type {Boolean}
73
76
  * @private
74
77
  */
75
78
  isContractCall = false
79
+ /**
80
+ * @type {Boolean}
81
+ * @readonly
82
+ */
83
+ processSystemEvents = false
84
+ /**
85
+ * @type {{}}
86
+ * @private
87
+ */
88
+ metrics
76
89
 
77
90
  analyze() {
78
91
  //find appropriate parser method
@@ -160,6 +173,17 @@ class EffectsAnalyzer {
160
173
  }, position)
161
174
  }
162
175
 
176
+ addMetric(metric, value) {
177
+ let {metrics} = this
178
+ if (!metrics) {
179
+ metrics = this.metrics = {
180
+ type: effectTypes.contractMetrics
181
+ }
182
+ this.addEffect(metrics)
183
+ }
184
+ metrics[metric] = value
185
+ }
186
+
163
187
  setOptions() {
164
188
  const sourceAccount = normalizeAddress(this.source)
165
189
  const {before, after} = this.changes.find(ch => ch.type === 'account' && ch.before.address === sourceAccount)
package/src/index.js CHANGED
@@ -17,9 +17,11 @@ const effectTypes = require('./effect-types')
17
17
  * @param {String|Buffer|xdr.TransactionResult} [result] - Base64-encoded tx envelope result
18
18
  * @param {String|Buffer|xdr.TransactionMeta} [meta] - Base64-encoded tx envelope meta
19
19
  * @param {Boolean} [mapSac] - Whether to create a map SAC->Asset
20
+ * @param {Boolean} [processSystemEvents] - Emit effects for contract errors and resource stats
21
+ * @param {Number} [protocol] - Specific Stellar protocol version for the executed transaction
20
22
  * @return {ParsedTxOperationsMetadata}
21
23
  */
22
- function parseTxOperationsMeta({network, tx, result, meta, mapSac = false}) {
24
+ function parseTxOperationsMeta({network, tx, result, meta, mapSac = false, processSystemEvents = false, protocol}) {
23
25
  if (!network)
24
26
  throw new TypeError(`Network passphrase argument is required.`)
25
27
  if (typeof network !== 'string')
@@ -112,7 +114,7 @@ function parseTxOperationsMeta({network, tx, result, meta, mapSac = false}) {
112
114
  effect.source = (before || after).address
113
115
  res.effects.push(effect)
114
116
  }
115
- if (isFeeBump && before.balance !== after.balance) { //fee bump fee calculation bug
117
+ if (isFeeBump && protocol === 20 && before.balance !== after.balance) { //bump fee calculation bug in protocol v20
116
118
  const currentFee = BigInt(feeEffect.charged)
117
119
  const diff = BigInt(after.balance) - BigInt(before.balance)
118
120
  if (diff < currentFee) { // do not allow negative fee
@@ -138,6 +140,7 @@ function parseTxOperationsMeta({network, tx, result, meta, mapSac = false}) {
138
140
  params.events = sorobanMeta.events()
139
141
  params.diagnosticEvents = sorobanMeta.diagnosticEvents()
140
142
  params.mapSac = mapSac
143
+ params.processSystemEvents = processSystemEvents
141
144
  }
142
145
  const analyzer = new EffectsAnalyzer(params)
143
146
  operation.effects = analyzer.analyze()