@stellar-expert/tx-meta-effects-parser 5.0.0-beta10
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/.eslintrc.js +8 -0
- package/.npmignore +6 -0
- package/LICENSE +21 -0
- package/README.MD +1145 -0
- package/babel.config.js +3 -0
- package/jest.config.js +6 -0
- package/package.json +21 -0
- package/src/analyzer-primitives.js +150 -0
- package/src/asset-supply-processor.js +130 -0
- package/src/contract-preimage-encoder.js +43 -0
- package/src/effect-types.js +97 -0
- package/src/effect.js +24 -0
- package/src/errors.js +13 -0
- package/src/events-analyzer.js +308 -0
- package/src/index.js +161 -0
- package/src/ledger-entry-changes-parser.js +333 -0
- package/src/signer-changes-analyzer.js +147 -0
- package/src/tx-effects-analyzer.js +826 -0
- package/src/tx-meta-changes-parser.js +35 -0
- package/src/tx-result-parser.js +127 -0
- package/src/tx-xdr-parser-utils.js +276 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
const {StrKey} = require('@stellar/stellar-base')
|
|
2
|
+
const {xdrParseScVal} = require('./tx-xdr-parser-utils')
|
|
3
|
+
const effectTypes = require('./effect-types')
|
|
4
|
+
|
|
5
|
+
const EVENT_TYPES = {
|
|
6
|
+
SYSTEM: 0,
|
|
7
|
+
CONTRACT: 1,
|
|
8
|
+
DIAGNOSTIC: 2
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class EventsAnalyzer {
|
|
12
|
+
constructor(effectAnalyzer) {
|
|
13
|
+
this.effectAnalyzer = effectAnalyzer
|
|
14
|
+
this.callStack = []
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @type {[]}
|
|
19
|
+
* @private
|
|
20
|
+
*/
|
|
21
|
+
callStack
|
|
22
|
+
|
|
23
|
+
analyze() {
|
|
24
|
+
this.analyzeDiagnosticEvents()
|
|
25
|
+
this.analyzeEvents()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @private
|
|
30
|
+
*/
|
|
31
|
+
analyzeEvents() {
|
|
32
|
+
const {events} = this.effectAnalyzer
|
|
33
|
+
if (!events)
|
|
34
|
+
return
|
|
35
|
+
//contract-generated events
|
|
36
|
+
for (const evt of events) {
|
|
37
|
+
const body = evt.body().value()
|
|
38
|
+
const rawTopics = body.topics()
|
|
39
|
+
const topics = rawTopics.map(xdrParseScVal)
|
|
40
|
+
if (topics[0] === 'DATA' && topics[1] === 'set')
|
|
41
|
+
continue //skip data entries modifications
|
|
42
|
+
const rawData = body.data()
|
|
43
|
+
//add event to the pipeline
|
|
44
|
+
this.effectAnalyzer.addEffect({
|
|
45
|
+
type: effectTypes.contractEvent,
|
|
46
|
+
contract: StrKey.encodeContract(evt.contractId()),
|
|
47
|
+
topics,
|
|
48
|
+
rawTopics: rawTopics.map(v => v.toXDR('base64')),
|
|
49
|
+
data: processEventBodyValue(rawData),
|
|
50
|
+
rawData: rawData.toXDR('base64')
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @private
|
|
58
|
+
*/
|
|
59
|
+
analyzeDiagnosticEvents() {
|
|
60
|
+
const {diagnosticEvents} = this.effectAnalyzer
|
|
61
|
+
if (!diagnosticEvents)
|
|
62
|
+
return
|
|
63
|
+
//diagnostic events
|
|
64
|
+
for (const evt of diagnosticEvents) {
|
|
65
|
+
if (!evt.inSuccessfulContractCall())
|
|
66
|
+
return //throw new UnexpectedTxMetaChangeError({type: 'diagnostic_event', action: 'failed'})
|
|
67
|
+
//parse event
|
|
68
|
+
const event = evt.event()
|
|
69
|
+
const contractId = event.contractId()
|
|
70
|
+
this.processDiagnosticEvent(event.body().value(), event.type().value, contractId ? StrKey.encodeContract(contractId) : null)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {xdr.ContractEventV0} body
|
|
76
|
+
* @param {Number} type
|
|
77
|
+
* @param {String} contractId
|
|
78
|
+
* @private
|
|
79
|
+
*/
|
|
80
|
+
processDiagnosticEvent(body, type, contractId) {
|
|
81
|
+
const topics = body.topics()
|
|
82
|
+
switch (xdrParseScVal(topics[0])) {
|
|
83
|
+
case 'fn_call': // contract call
|
|
84
|
+
const rawArgs = body.data()
|
|
85
|
+
const parsedEvent = {
|
|
86
|
+
type: effectTypes.contractInvoked,
|
|
87
|
+
contract: xdrParseScVal(topics[1], true),
|
|
88
|
+
function: xdrParseScVal(topics[2]),
|
|
89
|
+
args: processEventBodyValue(rawArgs),
|
|
90
|
+
rawArgs: rawArgs.toXDR('base64')
|
|
91
|
+
}
|
|
92
|
+
//add the invocation to the call stack
|
|
93
|
+
if (this.callStack.length) {
|
|
94
|
+
parsedEvent.depth = this.callStack.length
|
|
95
|
+
}
|
|
96
|
+
this.callStack.push(parsedEvent)
|
|
97
|
+
this.effectAnalyzer.addEffect(parsedEvent)
|
|
98
|
+
break
|
|
99
|
+
case 'fn_return':
|
|
100
|
+
if (type !== EVENT_TYPES.DIAGNOSTIC)
|
|
101
|
+
return // skip non-diagnostic events
|
|
102
|
+
//attach execution result to the contract invocation event
|
|
103
|
+
const funcCall = this.callStack.pop()
|
|
104
|
+
const result = body.data()
|
|
105
|
+
if (result.switch().name !== 'scvVoid') {
|
|
106
|
+
funcCall.result = result.toXDR('base64')
|
|
107
|
+
}
|
|
108
|
+
break
|
|
109
|
+
//handle standard token contract events
|
|
110
|
+
//see https://github.com/stellar/rs-soroban-sdk/blob/71170fba76e1aa4d50224316f1157f0fb10e6d79/soroban-sdk/src/token.rs
|
|
111
|
+
case 'transfer': {
|
|
112
|
+
if (!matchEventTopicsShape(topics, ['address', 'address', 'str?']))
|
|
113
|
+
return
|
|
114
|
+
const from = xdrParseScVal(topics[1])
|
|
115
|
+
const to = xdrParseScVal(topics[2])
|
|
116
|
+
const asset = contractId //topics[3]? xdrParseScVal(topics[3]) || contractId
|
|
117
|
+
const amount = processEventBodyValue(body.data())
|
|
118
|
+
if (!this.matchInvocationEffect(e =>
|
|
119
|
+
(e.function === 'transfer' && matchArrays([from, to, amount], e.args)) ||
|
|
120
|
+
(e.function === 'transferFrom' && matchArrays([undefined, from, to, amount], e.args))
|
|
121
|
+
))
|
|
122
|
+
return
|
|
123
|
+
const isSorobanAsset = isContractAddress(asset)
|
|
124
|
+
if (!isSorobanAsset || isContractAddress(from)) {
|
|
125
|
+
this.debit(from, asset, amount)
|
|
126
|
+
}
|
|
127
|
+
if (!isSorobanAsset || isContractAddress(to)) {
|
|
128
|
+
this.credit(to, asset, amount)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
break
|
|
132
|
+
case 'mint': {
|
|
133
|
+
if (!matchEventTopicsShape(topics, ['address', 'address', 'str?']))
|
|
134
|
+
return //throw new Error('Non-standard event')
|
|
135
|
+
const to = xdrParseScVal(topics[1])
|
|
136
|
+
const amount = processEventBodyValue(body.data())
|
|
137
|
+
if (!this.matchInvocationEffect(e => e.function === 'mint' && matchArrays([to, amount], e.args)))
|
|
138
|
+
return
|
|
139
|
+
this.effectAnalyzer.addEffect({
|
|
140
|
+
type: effectTypes.assetMinted,
|
|
141
|
+
asset: contractId,
|
|
142
|
+
amount
|
|
143
|
+
})
|
|
144
|
+
this.credit(to, contractId, amount)
|
|
145
|
+
}
|
|
146
|
+
break
|
|
147
|
+
case 'burn': {
|
|
148
|
+
if (!matchEventTopicsShape(topics, ['address', 'str?']))
|
|
149
|
+
return //throw new Error('Non-standard event')
|
|
150
|
+
const from = xdrParseScVal(topics[1])
|
|
151
|
+
const amount = processEventBodyValue(body.data())
|
|
152
|
+
if (!this.matchInvocationEffect(e =>
|
|
153
|
+
(e.function === 'burn' && matchArrays([from, amount], e.args)) ||
|
|
154
|
+
(e.function === 'burn_from' && matchArrays([undefined, from, amount], e.args))
|
|
155
|
+
))
|
|
156
|
+
return
|
|
157
|
+
this.debit(from, contractId, amount)
|
|
158
|
+
this.effectAnalyzer.addEffect({
|
|
159
|
+
type: effectTypes.assetBurned,
|
|
160
|
+
asset: contractId,
|
|
161
|
+
amount
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
break
|
|
165
|
+
case 'clawback': {
|
|
166
|
+
if (!matchEventTopicsShape(topics, ['address', 'address', 'str?']))
|
|
167
|
+
return //throw new Error('Non-standard event')
|
|
168
|
+
const admin = xdrParseScVal(topics[1])
|
|
169
|
+
const from = xdrParseScVal(topics[2])
|
|
170
|
+
const amount = processEventBodyValue(body.data())
|
|
171
|
+
if (!this.matchInvocationEffect(e => e.function === 'clawback' && matchArrays([from, amount], e.args)))
|
|
172
|
+
return
|
|
173
|
+
this.debit(from, contractId, amount)
|
|
174
|
+
this.effectAnalyzer.addEffect({
|
|
175
|
+
type: effectTypes.assetBurned,
|
|
176
|
+
asset: contractId,
|
|
177
|
+
amount
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
break
|
|
181
|
+
//TODO: process token allowance, authorization approval, and admin modification for SAC contracts
|
|
182
|
+
/*case 'approve': {
|
|
183
|
+
if (!matchEventTopicsShape(topics, ['address', 'address', 'str?']))
|
|
184
|
+
throw new Error('Non-standard event')
|
|
185
|
+
const from = xdrParseScVal(topics[1])
|
|
186
|
+
const spender = xdrParseScVal(topics[2])
|
|
187
|
+
}
|
|
188
|
+
break
|
|
189
|
+
|
|
190
|
+
case 'set_authorized': {
|
|
191
|
+
throw new Error('Not implemented')
|
|
192
|
+
//trustlineAuthorizationUpdated
|
|
193
|
+
if (!matchEventTopicsShape(topics, ['address', 'address', 'bool', 'str?']))
|
|
194
|
+
throw new Error('Non-standard event')
|
|
195
|
+
const admin = xdrParseScVal(topics[1])
|
|
196
|
+
const id = xdrParseScVal(topics[2])
|
|
197
|
+
const authorize = xdrParseScVal(topics[3])
|
|
198
|
+
}
|
|
199
|
+
break
|
|
200
|
+
case 'set_admin': {
|
|
201
|
+
throw new Error('Not implemented')
|
|
202
|
+
if (!matchEventTopicsShape(topics, ['address']))
|
|
203
|
+
throw new Error('Non-standard event')
|
|
204
|
+
const prevAdmin = xdrParseScVal(topics[1])
|
|
205
|
+
const newAdmin = processEventBodyValue(topics[2])
|
|
206
|
+
}
|
|
207
|
+
break*/
|
|
208
|
+
default:
|
|
209
|
+
//console.log(`Event ` + xdrParseScVal(topics[0]))
|
|
210
|
+
break
|
|
211
|
+
}
|
|
212
|
+
return null
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* @param {String} from
|
|
217
|
+
* @param {String} asset
|
|
218
|
+
* @param {String} amount
|
|
219
|
+
* @private
|
|
220
|
+
*/
|
|
221
|
+
debit(from, asset, amount) {
|
|
222
|
+
this.effectAnalyzer.debit(amount, asset, from)
|
|
223
|
+
|
|
224
|
+
//debit from account
|
|
225
|
+
//TODO: check debits of Soroban assets from account
|
|
226
|
+
//if (token.anchoredAsset)
|
|
227
|
+
//return //skip processing changes for classic assets - they are processed elsewhere
|
|
228
|
+
/*this.effectAnalyzer.addEffect({
|
|
229
|
+
type: effectTypes.accountDebited,
|
|
230
|
+
source: from,
|
|
231
|
+
asset: token.asset,
|
|
232
|
+
amount
|
|
233
|
+
})*/
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @param {String} to
|
|
238
|
+
* @param {String} asset
|
|
239
|
+
* @param {String} amount
|
|
240
|
+
* @private
|
|
241
|
+
*/
|
|
242
|
+
credit(to, asset, amount) {
|
|
243
|
+
this.effectAnalyzer.credit(amount, asset, to)
|
|
244
|
+
|
|
245
|
+
//credit account
|
|
246
|
+
//TODO: check credits of Soroban assets
|
|
247
|
+
//if (token.anchoredAsset)
|
|
248
|
+
//return //skip processing changes for classic assets - they are processed elsewhere
|
|
249
|
+
/*this.effectAnalyzer.addEffect({
|
|
250
|
+
type: effectTypes.accountCredited,
|
|
251
|
+
source: to,
|
|
252
|
+
asset: token.asset,
|
|
253
|
+
amount
|
|
254
|
+
})*/
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
matchInvocationEffect(cb) {
|
|
258
|
+
return this.effectAnalyzer.effects.find(e => e.type === effectTypes.contractInvoked && cb(e))
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function matchEventTopicsShape(topics, shape) {
|
|
263
|
+
if (topics.length > shape.length + 1)
|
|
264
|
+
return false
|
|
265
|
+
//we ignore the first topic because it's an event name
|
|
266
|
+
for (let i = 0; i < shape.length; i++) {
|
|
267
|
+
let match = shape[i]
|
|
268
|
+
let optional = false
|
|
269
|
+
if (match.endsWith('?')) {
|
|
270
|
+
match = match.substring(0, match.length - 1)
|
|
271
|
+
optional = true
|
|
272
|
+
}
|
|
273
|
+
const topic = topics[i + 1]
|
|
274
|
+
if (topic) {
|
|
275
|
+
if (topic._arm !== match)
|
|
276
|
+
return false
|
|
277
|
+
} else if (!optional)
|
|
278
|
+
return false
|
|
279
|
+
}
|
|
280
|
+
return true
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function matchArrays(a, b) {
|
|
284
|
+
if (!a || !b)
|
|
285
|
+
return false
|
|
286
|
+
if (a.length !== b.length)
|
|
287
|
+
return false
|
|
288
|
+
for (let i = a.length; i--;) {
|
|
289
|
+
if (a[i] !== undefined && a[i] !== b[i]) //undefined serves as * substitution
|
|
290
|
+
return false
|
|
291
|
+
}
|
|
292
|
+
return true
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function processEventBodyValue(value) {
|
|
296
|
+
const innerValue = value.value()
|
|
297
|
+
/*if (innerValue instanceof Array) //handle simple JS arrays
|
|
298
|
+
return innerValue.map(xdrParseScVal)*/
|
|
299
|
+
if (!innerValue) //scVoid
|
|
300
|
+
return undefined
|
|
301
|
+
return xdrParseScVal(value) //other scValue
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function isContractAddress(address) {
|
|
305
|
+
return address.length === 56 && address[0] === 'C'
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
module.exports = EventsAnalyzer
|
package/src/index.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
const {TransactionBuilder, xdr} = require('@stellar/stellar-base')
|
|
2
|
+
const {processFeeChargedEffect, analyzeOperationEffects, EffectsAnalyzer} = require('./tx-effects-analyzer')
|
|
3
|
+
const {parseTxResult} = require('./tx-result-parser')
|
|
4
|
+
const {parseLedgerEntryChanges} = require('./ledger-entry-changes-parser')
|
|
5
|
+
const {parseTxMetaChanges} = require('./tx-meta-changes-parser')
|
|
6
|
+
const {analyzeSignerChanges} = require('./signer-changes-analyzer')
|
|
7
|
+
const contractPreimageEncoder = require('./contract-preimage-encoder')
|
|
8
|
+
const xdrParserUtils = require('./tx-xdr-parser-utils')
|
|
9
|
+
const effectTypes = require('./effect-types')
|
|
10
|
+
const {TxMetaEffectParserError, UnexpectedTxMetaChangeError} = require('./errors')
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Retrieve effects from transaction execution result metadata
|
|
14
|
+
* @param {String} network - Network passphrase
|
|
15
|
+
* @param {String|Buffer|xdr.TransactionEnvelope} tx - Base64-encoded tx envelope xdr
|
|
16
|
+
* @param {String|Buffer|xdr.TransactionResult} result? - Base64-encoded tx envelope result
|
|
17
|
+
* @param {String|Buffer|xdr.TransactionMeta} meta? - Base64-encoded tx envelope meta
|
|
18
|
+
* @return {ParsedTxOperationsMetadata}
|
|
19
|
+
*/
|
|
20
|
+
function parseTxOperationsMeta({network, tx, result, meta}) {
|
|
21
|
+
if (!network)
|
|
22
|
+
throw new TypeError(`Network passphrase argument is required.`)
|
|
23
|
+
if (typeof network !== 'string')
|
|
24
|
+
throw new TypeError(`Invalid network passphrase or identifier: "${network}".`)
|
|
25
|
+
if (!tx)
|
|
26
|
+
throw new TypeError(`Transaction envelope argument is required.`)
|
|
27
|
+
const isEphemeral = !meta
|
|
28
|
+
//parse tx, result, and meta xdr
|
|
29
|
+
try {
|
|
30
|
+
tx = ensureXdrInputType(tx, xdr.TransactionEnvelope)
|
|
31
|
+
} catch (e) {
|
|
32
|
+
throw new TxMetaEffectParserError('Invalid transaction envelope XDR. ' + e.message)
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
result = ensureXdrInputType(result, xdr.TransactionResult)
|
|
36
|
+
} catch (e) {
|
|
37
|
+
try {
|
|
38
|
+
const pair = ensureXdrInputType(result, xdr.TransactionResultPair)
|
|
39
|
+
result = pair.result()
|
|
40
|
+
} catch {
|
|
41
|
+
throw new TxMetaEffectParserError('Invalid transaction result XDR. ' + e.message)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
tx = TransactionBuilder.fromXDR(tx, network)
|
|
46
|
+
|
|
47
|
+
let parsedTx = tx
|
|
48
|
+
let parsedResult = result
|
|
49
|
+
|
|
50
|
+
const isFeeBump = !!parsedTx.innerTransaction
|
|
51
|
+
let feeBumpSuccess
|
|
52
|
+
|
|
53
|
+
const res = {
|
|
54
|
+
tx,
|
|
55
|
+
isEphemeral
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
//take inner transaction if parsed tx is a fee bump tx
|
|
59
|
+
if (isFeeBump) {
|
|
60
|
+
parsedTx = parsedTx.innerTransaction
|
|
61
|
+
if (parsedTx.innerTransaction)
|
|
62
|
+
throw new TxMetaEffectParserError('Failed to process FeeBumpTransaction wrapped with another FeeBumpTransaction')
|
|
63
|
+
if (!isEphemeral) {
|
|
64
|
+
parsedResult = result.result().innerResultPair().result()
|
|
65
|
+
feeBumpSuccess = parsedResult.result().switch().value >= 0
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
//normalize operation source and effects container
|
|
70
|
+
if (parsedTx.operations) {
|
|
71
|
+
res.operations = parsedTx.operations
|
|
72
|
+
|
|
73
|
+
for (const op of parsedTx.operations) {
|
|
74
|
+
if (!op.source) {
|
|
75
|
+
op.source = parsedTx.source
|
|
76
|
+
}
|
|
77
|
+
op.effects = []
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
//process fee charge
|
|
82
|
+
res.effects = [processFeeChargedEffect(tx, tx.feeSource || parsedTx.source, result.feeCharged().toString(), isFeeBump)]
|
|
83
|
+
|
|
84
|
+
//check execution result
|
|
85
|
+
const {success, opResults} = parseTxResult(parsedResult)
|
|
86
|
+
if (!success || isFeeBump && !feeBumpSuccess) {
|
|
87
|
+
res.failed = true
|
|
88
|
+
return res
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
//do not parse meta for unsubmitted/rejected transactions
|
|
92
|
+
if (isEphemeral)
|
|
93
|
+
return res
|
|
94
|
+
|
|
95
|
+
//retrieve operations result metadata
|
|
96
|
+
try {
|
|
97
|
+
meta = ensureXdrInputType(meta, xdr.TransactionMeta)
|
|
98
|
+
} catch {
|
|
99
|
+
throw new TxMetaEffectParserError('Invalid transaction metadata XDR. ' + e.message)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
//add tx-level effects
|
|
103
|
+
for (const {before, after} of parseTxMetaChanges(meta)) {
|
|
104
|
+
if (before.entry !== 'account')
|
|
105
|
+
throw new UnexpectedTxMetaChangeError({type: before.entry, action: 'update'})
|
|
106
|
+
for (const effect of analyzeSignerChanges(before, after)) {
|
|
107
|
+
effect.source = (before || after).address
|
|
108
|
+
res.effects.push(effect)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const metaValue = meta.value()
|
|
112
|
+
const opMeta = metaValue.operations()
|
|
113
|
+
|
|
114
|
+
//analyze operation effects for each operation
|
|
115
|
+
for (let i = 0; i < parsedTx.operations.length; i++) {
|
|
116
|
+
const operation = parsedTx.operations[i]
|
|
117
|
+
if (success) {
|
|
118
|
+
const params = {
|
|
119
|
+
operation,
|
|
120
|
+
meta:opMeta[i]?.changes(),
|
|
121
|
+
result:opResults[i],network
|
|
122
|
+
}
|
|
123
|
+
//only for Soroban contract invocation
|
|
124
|
+
if (operation.type === 'invokeHostFunction') {
|
|
125
|
+
const sorobanMeta = metaValue.sorobanMeta()
|
|
126
|
+
params.events = sorobanMeta.events()
|
|
127
|
+
params.diagnosticEvents = sorobanMeta.diagnosticEvents()
|
|
128
|
+
}
|
|
129
|
+
const analyzer = new EffectsAnalyzer(params)
|
|
130
|
+
operation.effects = analyzer.analyze()
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return res
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Convert base64/raw XDR representation to XDR type
|
|
138
|
+
* @param {String|Buffer|Uint8Array|xdrType} value
|
|
139
|
+
* @param xdrType
|
|
140
|
+
* @return {xdrType|*}
|
|
141
|
+
* @internal
|
|
142
|
+
*/
|
|
143
|
+
function ensureXdrInputType(value, xdrType) {
|
|
144
|
+
if (value?.toXDR) // duck-typing check XDR types
|
|
145
|
+
return value
|
|
146
|
+
|
|
147
|
+
if (!value || (typeof value !== 'string' && !(value instanceof Uint8Array)))
|
|
148
|
+
throw new TypeError(`Invalid input format. Expected xdr.${xdrType.name} (raw, buffer, or bas64-encoded).`)
|
|
149
|
+
return xdrType.fromXDR(value, typeof value === 'string' ? 'base64' : 'raw')
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* @typedef {{}} ParsedTxOperationsMetadata
|
|
154
|
+
* @property {Transaction|FeeBumpTransaction} tx
|
|
155
|
+
* @property {BaseOperation[]} operations
|
|
156
|
+
* @property {Boolean} isEphemeral
|
|
157
|
+
* @property {Boolean} [failed]
|
|
158
|
+
* @property {{}[]} [effects]
|
|
159
|
+
*/
|
|
160
|
+
|
|
161
|
+
module.exports = {parseTxOperationsMeta, parseTxResult, analyzeOperationEffects, parseLedgerEntryChanges, parseTxMetaChanges, effectTypes, xdrParserUtils, contractPreimageEncoder}
|