@stellar-expert/tx-meta-effects-parser 5.1.1 → 5.2.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.1.1",
3
+ "version": "5.2.0",
4
4
  "description": "Low-level effects parser for Stellar transaction results and meta XDR",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -1,9 +1,9 @@
1
- const effectTypes = require('./effect-types')
1
+ const effectTypes = require('../effect-types')
2
2
 
3
3
  /**
4
4
  * Effect supply computation processor
5
5
  */
6
- class AssetSupplyProcessor {
6
+ class AssetSupplyAnalyzer {
7
7
  /**
8
8
  * @param {EffectsAnalyzer} effectsAnalyzer
9
9
  */
@@ -201,4 +201,4 @@ class CollapsibleMintsAnalyzer {
201
201
  }
202
202
  }
203
203
 
204
- module.exports = AssetSupplyProcessor
204
+ module.exports = AssetSupplyAnalyzer
@@ -1,7 +1,7 @@
1
1
  const {StrKey} = require('@stellar/stellar-base')
2
- const {xdrParseScVal, xdrParseAsset, isContractAddress, toStellarAsset} = require('./tx-xdr-parser-utils')
3
- const {contractIdFromAsset} = require('./contract-preimage-encoder')
4
- const effectTypes = require('./effect-types')
2
+ const effectTypes = require('../effect-types')
3
+ const {xdrParseScVal, xdrParseAsset, isContractAddress} = require('../parser/tx-xdr-parser-utils')
4
+ const {mapSacContract} = require('./sac-contract-mapper')
5
5
 
6
6
  const EVENT_TYPES = {
7
7
  SYSTEM: 0,
@@ -10,8 +10,11 @@ const EVENT_TYPES = {
10
10
  }
11
11
 
12
12
  class EventsAnalyzer {
13
- constructor(effectAnalyzer) {
14
- this.effectAnalyzer = effectAnalyzer
13
+ /**
14
+ * @param {EffectsAnalyzer} effectsAnalyzer
15
+ */
16
+ constructor(effectsAnalyzer) {
17
+ this.effectsAnalyzer = effectsAnalyzer
15
18
  this.callStack = []
16
19
  }
17
20
 
@@ -30,7 +33,7 @@ class EventsAnalyzer {
30
33
  * @private
31
34
  */
32
35
  analyzeEvents() {
33
- const {events} = this.effectAnalyzer
36
+ const {events} = this.effectsAnalyzer
34
37
  if (!events)
35
38
  return
36
39
  //contract-generated events
@@ -42,7 +45,7 @@ class EventsAnalyzer {
42
45
  continue //skip data entries modifications
43
46
  const rawData = body.data()
44
47
  //add event to the pipeline
45
- this.effectAnalyzer.addEffect({
48
+ this.effectsAnalyzer.addEffect({
46
49
  type: effectTypes.contractEvent,
47
50
  contract: StrKey.encodeContract(evt.contractId()),
48
51
  topics,
@@ -58,7 +61,7 @@ class EventsAnalyzer {
58
61
  * @private
59
62
  */
60
63
  analyzeDiagnosticEvents() {
61
- const {diagnosticEvents} = this.effectAnalyzer
64
+ const {diagnosticEvents} = this.effectsAnalyzer
62
65
  if (!diagnosticEvents)
63
66
  return
64
67
  //diagnostic events
@@ -81,9 +84,11 @@ class EventsAnalyzer {
81
84
  processDiagnosticEvent(body, type, contractId) {
82
85
  const topics = body.topics()
83
86
  if (!topics?.length)
84
- return null
87
+ return
85
88
  switch (xdrParseScVal(topics[0])) {
86
89
  case 'fn_call': // contract call
90
+ if (type !== EVENT_TYPES.DIAGNOSTIC)
91
+ return // skip non-diagnostic events
87
92
  const rawArgs = body.data()
88
93
  const parsedEvent = {
89
94
  type: effectTypes.contractInvoked,
@@ -97,7 +102,7 @@ class EventsAnalyzer {
97
102
  parsedEvent.depth = this.callStack.length
98
103
  }
99
104
  this.callStack.push(parsedEvent)
100
- this.effectAnalyzer.addEffect(parsedEvent)
105
+ this.effectsAnalyzer.addEffect(parsedEvent)
101
106
  break
102
107
  case 'fn_return':
103
108
  if (type !== EVENT_TYPES.DIAGNOSTIC)
@@ -118,7 +123,6 @@ class EventsAnalyzer {
118
123
  const to = xdrParseScVal(topics[2])
119
124
  if (to === from) //self transfer - nothing happens
120
125
  return // TODO: need additional checks
121
- const sorobanAsset = contractId
122
126
  const amount = processEventBodyValue(body.data())
123
127
  if (!this.matchInvocationEffect(e =>
124
128
  (e.function === 'transfer' && matchArrays([from, to, amount], e.args)) ||
@@ -128,31 +132,31 @@ class EventsAnalyzer {
128
132
  let classicAsset
129
133
  if (topics.length > 3) {
130
134
  classicAsset = xdrParseAsset(xdrParseScVal(topics[3]))
131
- //validate
132
- if (contractIdFromAsset(toStellarAsset(classicAsset), this.effectAnalyzer.network) !== sorobanAsset)
133
- return //not an SAC transfer
135
+ if (!mapSacContract(this.effectsAnalyzer, contractId, classicAsset)) {
136
+ classicAsset = null //not an SAC event
137
+ }
134
138
  }
135
139
  if (classicAsset && (classicAsset.includes(from) || classicAsset.includes(to))) { //SAC transfer by asset issuer
136
140
  if (classicAsset.includes(from)) {
137
- this.effectAnalyzer.mint(sorobanAsset, amount)
138
- this.effectAnalyzer.credit(amount, isContractAddress(to) ? sorobanAsset : classicAsset, to)
141
+ this.effectsAnalyzer.mint(contractId, amount)
142
+ this.effectsAnalyzer.credit(amount, isContractAddress(to) ? contractId : classicAsset, to)
139
143
  }
140
144
  if (classicAsset.includes(to)) {
141
- this.effectAnalyzer.debit(amount, isContractAddress(from) ? sorobanAsset : classicAsset, from)
142
- this.effectAnalyzer.burn(sorobanAsset, amount)
145
+ this.effectsAnalyzer.debit(amount, isContractAddress(from) ? contractId : classicAsset, from)
146
+ this.effectsAnalyzer.burn(contractId, amount)
143
147
  }
144
148
  } else { //other cases
145
149
  if (classicAsset && !isContractAddress(from)) { //classic asset bridged to Soroban
146
- this.effectAnalyzer.burn(classicAsset, amount)
147
- this.effectAnalyzer.mint(sorobanAsset, amount)
150
+ this.effectsAnalyzer.burn(classicAsset, amount)
151
+ this.effectsAnalyzer.mint(contractId, amount)
148
152
  } else {
149
- this.effectAnalyzer.debit(amount, sorobanAsset, from)
153
+ this.effectsAnalyzer.debit(amount, contractId, from)
150
154
  }
151
155
  if (classicAsset && !isContractAddress(to)) { //classic asset bridged from Soroban
152
- this.effectAnalyzer.burn(sorobanAsset, amount)
153
- this.effectAnalyzer.mint(classicAsset, amount)
156
+ this.effectsAnalyzer.burn(contractId, amount)
157
+ this.effectsAnalyzer.mint(classicAsset, amount)
154
158
  } else {
155
- this.effectAnalyzer.credit(amount, sorobanAsset, to)
159
+ this.effectsAnalyzer.credit(amount, contractId, to)
156
160
  }
157
161
  }
158
162
 
@@ -165,12 +169,15 @@ class EventsAnalyzer {
165
169
  const amount = processEventBodyValue(body.data())
166
170
  if (!this.matchInvocationEffect(e => e.function === 'mint' && matchArrays([to, amount], e.args)))
167
171
  return
168
- this.effectAnalyzer.addEffect({
172
+ this.effectsAnalyzer.addEffect({
169
173
  type: effectTypes.assetMinted,
170
174
  asset: contractId,
171
175
  amount
172
176
  })
173
- this.effectAnalyzer.credit(amount, contractId, to)
177
+ this.effectsAnalyzer.credit(amount, contractId, to)
178
+ if (topics.length > 3) {
179
+ mapSacContract(this.effectsAnalyzer, contractId, xdrParseAsset(xdrParseScVal(topics[3])))
180
+ }
174
181
  }
175
182
  break
176
183
  case 'burn': {
@@ -184,8 +191,11 @@ class EventsAnalyzer {
184
191
  ))
185
192
  return
186
193
 
187
- this.effectAnalyzer.debit(amount, contractId, from)
188
- this.effectAnalyzer.burn(contractId, amount)
194
+ this.effectsAnalyzer.debit(amount, contractId, from)
195
+ this.effectsAnalyzer.burn(contractId, amount)
196
+ if (topics.length > 2) {
197
+ mapSacContract(this.effectsAnalyzer, contractId, xdrParseAsset(xdrParseScVal(topics[2])))
198
+ }
189
199
  }
190
200
  break
191
201
  case 'clawback': {
@@ -195,46 +205,28 @@ class EventsAnalyzer {
195
205
  const amount = processEventBodyValue(body.data())
196
206
  if (!this.matchInvocationEffect(e => e.function === 'clawback' && matchArrays([from, amount], e.args)))
197
207
  return
198
- this.effectAnalyzer.debit(amount, contractId, from)
199
- this.effectAnalyzer.burn(contractId, amount)
208
+ this.effectsAnalyzer.debit(amount, contractId, from)
209
+ this.effectsAnalyzer.burn(contractId, amount)
210
+ if (topics.length > 3) {
211
+ mapSacContract(this.effectsAnalyzer, contractId, xdrParseAsset(xdrParseScVal(topics[3])))
212
+ }
200
213
  }
201
214
  break
202
- //TODO: process token allowance, authorization approval, and admin modification for SAC contracts
203
- /*case 'approve': {
215
+ /*case 'approve': { //TODO: think about processing this effect
204
216
  if (!matchEventTopicsShape(topics, ['address', 'address', 'str?']))
205
217
  throw new Error('Non-standard event')
206
218
  const from = xdrParseScVal(topics[1])
207
219
  const spender = xdrParseScVal(topics[2])
208
- }
209
- break
210
-
211
- case 'set_authorized': {
212
- throw new Error('Not implemented')
213
- //trustlineAuthorizationUpdated
214
- if (!matchEventTopicsShape(topics, ['address', 'address', 'bool', 'str?']))
215
- throw new Error('Non-standard event')
216
- const admin = xdrParseScVal(topics[1])
217
- const id = xdrParseScVal(topics[2])
218
- const authorize = xdrParseScVal(topics[3])
219
- }
220
- break
221
- case 'set_admin': {
222
- throw new Error('Not implemented')
223
- if (!matchEventTopicsShape(topics, ['address']))
224
- throw new Error('Non-standard event')
225
- const prevAdmin = xdrParseScVal(topics[1])
226
- const newAdmin = processEventBodyValue(topics[2])
220
+ if (topics.length > 3) {
221
+ mapSacContract(this.effectsAnalyzer, contractId, xdrParseAsset(xdrParseScVal(topics[3])))
222
+ }
227
223
  }
228
224
  break*/
229
- default:
230
- //console.log(`Event ` + xdrParseScVal(topics[0]))
231
- break
232
225
  }
233
- return null
234
226
  }
235
227
 
236
228
  matchInvocationEffect(cb) {
237
- return this.effectAnalyzer.effects.find(e => e.type === effectTypes.contractInvoked && cb(e))
229
+ return this.effectsAnalyzer.effects.find(e => e.type === effectTypes.contractInvoked && cb(e))
238
230
  }
239
231
  }
240
232
 
@@ -0,0 +1,40 @@
1
+ const TtlCache = require('../cache/ttl-cache')
2
+ const {toStellarAsset} = require('../parser/tx-xdr-parser-utils')
3
+ const {contractIdFromAsset} = require('../parser/contract-preimage-encoder')
4
+
5
+ const sacCache = new TtlCache()
6
+
7
+ /**
8
+ * Check and map SAC contract addresses with Classic assets
9
+ * @param {EffectsAnalyzer} effectsAnalyzer
10
+ * @param {String} contractAddress
11
+ * @param {String} classicAsset
12
+ * @return {Boolean}
13
+ */
14
+ function mapSacContract(effectsAnalyzer, contractAddress, classicAsset) {
15
+ if (!classicAsset)
16
+ return false
17
+ const {network, sacMap} = effectsAnalyzer
18
+ //try to load from cache first
19
+ const fromCache = sacCache.get(contractAddress + network)
20
+ if (!fromCache) {
21
+ const encodedContract = contractIdFromAsset(toStellarAsset(classicAsset), network)
22
+ sacCache.set(encodedContract + network, classicAsset)
23
+ if (encodedContract !== contractAddress)
24
+ return false
25
+ } else if (classicAsset !== fromCache)
26
+ return false //check whether validated contract from cache matches the asset
27
+ if (sacMap) {
28
+ sacMap[contractAddress] = classicAsset
29
+ }
30
+ return true
31
+ }
32
+
33
+ /**
34
+ * Dispose SAC cache mapping and release cleanup timers
35
+ */
36
+ function disposeSacCache() {
37
+ sacCache.dispose()
38
+ }
39
+
40
+ module.exports = {mapSacContract, disposeSacCache}
@@ -1,4 +1,4 @@
1
- const effectTypes = require('./effect-types')
1
+ const effectTypes = require('../effect-types')
2
2
 
3
3
  class SignerChangesAnalyzer {
4
4
  constructor(before, after) {
@@ -0,0 +1,82 @@
1
+ class TtlCache {
2
+ /**
3
+ * Create new instance of ClientCache with simple in-memory storage
4
+ * @param {Number} ttl - Uniform time-to-live (in seconds)
5
+ */
6
+ constructor(ttl = 2 * 60) {
7
+ this.ttl = ttl * 1000
8
+ this.storage = new Map()
9
+ }
10
+
11
+ /**
12
+ * @type {Number}
13
+ */
14
+ ttl
15
+ /**
16
+ * @type {Map<String,{ts:Number,value:*}>}
17
+ * @private
18
+ */
19
+ storage
20
+ /**
21
+ * @type {Number}
22
+ * @private
23
+ */
24
+ scheduledCleanup = 0
25
+
26
+ /**
27
+ * Try to retrieve an item from the cache
28
+ * @param {String} key
29
+ * @return {*}
30
+ */
31
+ get(key) {
32
+ let item = this.storage.get(key)
33
+ if (!item)
34
+ return null
35
+ item.ts = new Date().getTime()
36
+ return item.value
37
+ }
38
+
39
+ /**
40
+ * Add/replace cache object
41
+ * @param {String} key - Unique key
42
+ * @param {*} value - Associated value to store
43
+ */
44
+ set(key, value) {
45
+ this.storage.set(key, {value, ts: new Date().getTime()})
46
+ this.scheduleCleanup()
47
+ }
48
+
49
+ /**
50
+ * @private
51
+ */
52
+ cleanupApiCache() {
53
+ const {storage, ttl} = this
54
+ const expired = new Date().getTime() - ttl
55
+ for (const [key, item] of storage.entries()) {
56
+ if (item.ts < expired) {
57
+ storage.delete(key)
58
+ }
59
+ }
60
+ this.scheduledCleanup = 0
61
+ this.scheduleCleanup()
62
+ }
63
+
64
+ /**
65
+ * @private
66
+ */
67
+ scheduleCleanup() {
68
+ if (!this.scheduledCleanup && this.storage.size > 0) { //schedule new cleanup if storage is not empty
69
+ this.scheduledCleanup = setTimeout(() => this.cleanupApiCache(), this.ttl + 10)
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Stop scheduled cleanup task and free resources
75
+ */
76
+ dispose() {
77
+ clearTimeout(this.scheduledCleanup)
78
+ this.storage = null
79
+ }
80
+ }
81
+
82
+ module.exports = TtlCache
@@ -1,16 +1,15 @@
1
1
  const {StrKey, hash, xdr, nativeToScVal} = require('@stellar/stellar-base')
2
2
  const effectTypes = require('./effect-types')
3
- const {parseLedgerEntryChanges} = require('./ledger-entry-changes-parser')
4
- const {xdrParseAsset, xdrParseAccountAddress, xdrParseScVal} = require('./tx-xdr-parser-utils')
5
- const {encodeSponsorshipEffectName} = require('./analyzer-primitives')
6
- const {analyzeSignerChanges} = require('./signer-changes-analyzer')
7
- const {contractIdFromPreimage} = require('./contract-preimage-encoder')
8
- const EventsAnalyzer = require('./events-analyzer')
9
- const AssetSupplyProcessor = require('./asset-supply-processor')
3
+ const {parseLedgerEntryChanges} = require('./parser/ledger-entry-changes-parser')
4
+ const {xdrParseAsset, xdrParseAccountAddress, xdrParseScVal} = require('./parser/tx-xdr-parser-utils')
5
+ const {analyzeSignerChanges} = require('./aggregation/signer-changes-analyzer')
6
+ const {contractIdFromPreimage} = require('./parser/contract-preimage-encoder')
7
+ const EventsAnalyzer = require('./aggregation/events-analyzer')
8
+ const AssetSupplyAnalyzer = require('./aggregation/asset-supply-analyzer')
10
9
  const {UnexpectedTxMetaChangeError, TxMetaEffectParserError} = require('./errors')
11
10
 
12
11
  class EffectsAnalyzer {
13
- constructor({operation, meta, result, network, events, diagnosticEvents}) {
12
+ constructor({operation, meta, result, network, events, diagnosticEvents, mapSac}) {
14
13
  //set execution context
15
14
  if (!operation.source)
16
15
  throw new TxMetaEffectParserError('Operation source is not explicitly defined')
@@ -24,6 +23,9 @@ class EffectsAnalyzer {
24
23
  this.diagnosticEvents = diagnosticEvents
25
24
  }
26
25
  this.network = network
26
+ if (mapSac) {
27
+ this.sacMap = {}
28
+ }
27
29
  }
28
30
 
29
31
  /**
@@ -43,6 +45,11 @@ class EffectsAnalyzer {
43
45
  * @readonly
44
46
  */
45
47
  network
48
+ /**
49
+ * @type {{}}
50
+ * @readonly
51
+ */
52
+ sacMap
46
53
  /**
47
54
  * @type {ParsedLedgerEntryMeta[]}
48
55
  * @private
@@ -68,7 +75,7 @@ class EffectsAnalyzer {
68
75
  isContractCall = false
69
76
 
70
77
  analyze() {
71
- //find appropriate parsing method
78
+ //find appropriate parser method
72
79
  const parse = this[this.operation.type]
73
80
  if (parse) {
74
81
  parse.call(this)
@@ -80,7 +87,7 @@ class EffectsAnalyzer {
80
87
  //process Soroban events
81
88
  new EventsAnalyzer(this).analyze()
82
89
  //calculate minted/burned assets
83
- new AssetSupplyProcessor(this).analyze()
90
+ new AssetSupplyAnalyzer(this).analyze()
84
91
  //process state data changes in the end
85
92
  for (const change of this.changes)
86
93
  if (change.type === 'contractData') {
@@ -897,4 +904,28 @@ function normalizeAddress(address) {
897
904
  return StrKey.encodeEd25519PublicKey(rawBytes.subarray(0, 32))
898
905
  }
899
906
 
907
+
908
+ /**
909
+ * @param {String} action
910
+ * @param {String} type
911
+ * @return {String}
912
+ */
913
+ function encodeSponsorshipEffectName(action, type) {
914
+ let actionKey
915
+ switch (action) {
916
+ case 'created':
917
+ actionKey = 'Created'
918
+ break
919
+ case 'updated':
920
+ actionKey = 'Updated'
921
+ break
922
+ case 'removed':
923
+ actionKey = 'Removed'
924
+ break
925
+ default:
926
+ throw new UnexpectedTxMetaChangeError({action, type})
927
+ }
928
+ return effectTypes[`${type}Sponsorship${actionKey}`]
929
+ }
930
+
900
931
  module.exports = {EffectsAnalyzer, processFeeChargedEffect}
package/src/index.js CHANGED
@@ -1,23 +1,25 @@
1
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
2
  const {TxMetaEffectParserError, UnexpectedTxMetaChangeError} = require('./errors')
3
+ const {processFeeChargedEffect, analyzeOperationEffects, EffectsAnalyzer} = require('./effects-analyzer')
4
+ const {disposeSacCache} = require('./aggregation/sac-contract-mapper')
5
+ const {parseTxResult} = require('./parser/tx-result-parser')
6
+ const {parseLedgerEntryChanges} = require('./parser/ledger-entry-changes-parser')
7
+ const {parseTxMetaChanges} = require('./parser/tx-meta-changes-parser')
8
+ const {analyzeSignerChanges} = require('./aggregation/signer-changes-analyzer')
9
+ const contractPreimageEncoder = require('./parser/contract-preimage-encoder')
10
+ const xdrParserUtils = require('./parser/tx-xdr-parser-utils')
11
+ const effectTypes = require('./effect-types')
11
12
 
12
13
  /**
13
14
  * Retrieve effects from transaction execution result metadata
14
15
  * @param {String} network - Network passphrase
15
16
  * @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
17
+ * @param {String|Buffer|xdr.TransactionResult} [result] - Base64-encoded tx envelope result
18
+ * @param {String|Buffer|xdr.TransactionMeta} [meta] - Base64-encoded tx envelope meta
19
+ * @param {Boolean} [mapSac] - Whether to create a map SAC->Asset
18
20
  * @return {ParsedTxOperationsMetadata}
19
21
  */
20
- function parseTxOperationsMeta({network, tx, result, meta}) {
22
+ function parseTxOperationsMeta({network, tx, result, meta, mapSac = false}) {
21
23
  if (!network)
22
24
  throw new TypeError(`Network passphrase argument is required.`)
23
25
  if (typeof network !== 'string')
@@ -135,9 +137,13 @@ function parseTxOperationsMeta({network, tx, result, meta}) {
135
137
  const sorobanMeta = metaValue.sorobanMeta()
136
138
  params.events = sorobanMeta.events()
137
139
  params.diagnosticEvents = sorobanMeta.diagnosticEvents()
140
+ params.mapSac = mapSac
138
141
  }
139
142
  const analyzer = new EffectsAnalyzer(params)
140
143
  operation.effects = analyzer.analyze()
144
+ if (analyzer.sacMap && !isEmptyObject(analyzer.sacMap)) {
145
+ operation.sacMap = analyzer.sacMap
146
+ }
141
147
  }
142
148
  }
143
149
  return res
@@ -159,13 +165,20 @@ function ensureXdrInputType(value, xdrType) {
159
165
  return xdrType.fromXDR(value, typeof value === 'string' ? 'base64' : 'raw')
160
166
  }
161
167
 
168
+ function isEmptyObject(obj) {
169
+ for (const key in obj)
170
+ return false
171
+ return true
172
+ }
173
+
162
174
  /**
163
175
  * @typedef {{}} ParsedTxOperationsMetadata
164
- * @property {Transaction|FeeBumpTransaction} tx
165
- * @property {BaseOperation[]} operations
166
- * @property {Boolean} isEphemeral
167
- * @property {Boolean} [failed]
168
- * @property {{}[]} [effects]
176
+ * @property {Transaction|FeeBumpTransaction} tx - Parsed transaction object
177
+ * @property {BaseOperation[]} operations - Transaction operations
178
+ * @property {Boolean} isEphemeral - True for transactions without result metadata
179
+ * @property {Boolean} [failed] - True for transactions failed during on-chain execution
180
+ * @property {{}[]} [effects] - Top-level transaction effects (fee charges and )
181
+ * @property {Object<String,String>} [sacMap] - Optional map of SAC->Asset
169
182
  */
170
183
 
171
184
  module.exports = {
@@ -176,5 +189,6 @@ module.exports = {
176
189
  parseTxMetaChanges,
177
190
  effectTypes,
178
191
  xdrParserUtils,
179
- contractPreimageEncoder
192
+ contractPreimageEncoder,
193
+ disposeSacCache
180
194
  }
@@ -1,13 +1,6 @@
1
1
  const {StrKey} = require('@stellar/stellar-base')
2
- const {
3
- xdrParseAsset,
4
- xdrParseAccountAddress,
5
- xdrParseClaimant,
6
- xdrParsePrice,
7
- xdrParseSignerKey,
8
- xdrParseScVal
9
- } = require('./tx-xdr-parser-utils')
10
- const {TxMetaEffectParserError} = require('./errors')
2
+ const {TxMetaEffectParserError} = require('../errors')
3
+ const {xdrParseAsset, xdrParseAccountAddress, xdrParseClaimant, xdrParsePrice, xdrParseSignerKey} = require('./tx-xdr-parser-utils')
11
4
 
12
5
  /**
13
6
  * @typedef {{}} ParsedLedgerEntryMeta
@@ -1,5 +1,5 @@
1
1
  const {parseLedgerEntryChanges} = require('./ledger-entry-changes-parser')
2
- const {TxMetaEffectParserError} = require('./errors')
2
+ const {TxMetaEffectParserError} = require('../errors')
3
3
 
4
4
  /**
5
5
  * Parse top-level transaction metadata changes
@@ -1,6 +1,6 @@
1
1
  const {xdr} = require('@stellar/stellar-base')
2
2
  const {xdrParseAccountAddress, xdrParseTradeAtom, xdrParseClaimedOffer, xdrParseAsset} = require('./tx-xdr-parser-utils')
3
- const {TxMetaEffectParserError} = require('./errors')
3
+ const {TxMetaEffectParserError} = require('../errors')
4
4
 
5
5
  /**
6
6
  * Parse extra data from operation result
@@ -1,5 +1,5 @@
1
1
  const {StrKey, LiquidityPoolId, scValToBigInt, xdr, Asset} = require('@stellar/stellar-base')
2
- const {TxMetaEffectParserError} = require('./errors')
2
+ const {TxMetaEffectParserError} = require('../errors')
3
3
 
4
4
  /**
5
5
  * @param {String} address
@@ -1,150 +0,0 @@
1
- const {StrKey} = require('@stellar/stellar-base')
2
- const effectTypes = require('./effect-types')
3
- const {UnexpectedTxMetaChangeError} = require('./errors')
4
-
5
- /**
6
- * Returns true for AlphaNum4/12 assets adn false otherwise
7
- * @param {String} asset
8
- */
9
- function isAsset(asset) {
10
- return asset.includes('-') //lazy check for {code}-{issuer}-{type} format
11
- }
12
-
13
-
14
- /**
15
- * Convert value in stroops (Int64 amount) to the normal string representation
16
- * @param {String|Number|BigInt} valueInStroops
17
- * @return {String}
18
- */
19
- function fromStroops(valueInStroops) {
20
- try {
21
- let parsed = typeof valueInStroops === 'bigint' ?
22
- valueInStroops :
23
- BigInt(valueInStroops.toString())
24
- let negative = false
25
- if (parsed < 0n) {
26
- negative = true
27
- parsed *= -1n
28
- }
29
- const int = parsed / 10000000n
30
- const fract = parsed % 10000000n
31
- let res = int.toString()
32
- if (fract) {
33
- res += '.' + fract.toString().padStart(7, '0')
34
- }
35
- if (negative) {
36
- res = '-' + res
37
- }
38
- return trimZeros(res)
39
- } catch (e) {
40
- return '0'
41
- }
42
- }
43
-
44
-
45
- /**
46
- * Convert arbitrary stringified amount to int64 representation
47
- * @param {String|Number} value
48
- * @return {BigInt}
49
- */
50
- function toStroops(value) {
51
- if (!value)
52
- return 0n
53
- if (typeof value === 'number') {
54
- value = value.toFixed(7)
55
- }
56
- if (typeof value !== 'string' || !/^-?[\d.,]+$/.test(value))
57
- return 0n //invalid format
58
- try {
59
- let [int, decimal = '0'] = value.split('.', 2)
60
- let negative = false
61
- if (int.startsWith('-')) {
62
- negative = true
63
- int = int.slice(1)
64
- }
65
- let res = BigInt(int) * 10000000n + BigInt(decimal.slice(0, 7).padEnd(7, '0'))
66
- if (negative) {
67
- res *= -1n
68
- if (res < -0x8000000000000000n) //overflow
69
- return 0n
70
- } else if (res > 0xFFFFFFFFFFFFFFFFn) //overflow
71
- return 0n
72
- return res
73
- } catch (e) {
74
- return 0n
75
- }
76
- }
77
-
78
- /**
79
- * Trim trailing fractional zeros from a string amount representation
80
- * @param {String} value
81
- * @return {String}
82
- * @internal
83
- */
84
- function trimZeros(value) {
85
- const [int, fract] = value.split('.')
86
- if (!fract)
87
- return int
88
- const trimmed = fract.replace(/0+$/, '')
89
- if (!trimmed.length)
90
- return int
91
- return int + '.' + trimmed
92
- }
93
-
94
- /**
95
- * Replace multiplexed addresses with base G addresses
96
- * @param {String} address
97
- * @return {String}
98
- * @internal
99
- */
100
- function normalizeAddress(address) {
101
- const prefix = address[0]
102
- if (prefix === 'G')
103
- return address //lazy check for ed25519 G address
104
- if (prefix !== 'M')
105
- throw new TypeError('Expected ED25519 or Muxed address')
106
- const rawBytes = StrKey.decodeMed25519PublicKey(address)
107
- return StrKey.encodeEd25519PublicKey(rawBytes.subarray(0, 32))
108
- }
109
-
110
- /**
111
- * @param {String} action
112
- * @param {String} type
113
- * @return {String}
114
- */
115
- function encodeSponsorshipEffectName(action, type) {
116
- let actionKey
117
- switch (action) {
118
- case 'created':
119
- actionKey = 'Created'
120
- break
121
- case 'updated':
122
- actionKey = 'Updated'
123
- break
124
- case 'removed':
125
- actionKey = 'Removed'
126
- break
127
- default:
128
- throw new UnexpectedTxMetaChangeError({action, type})
129
- }
130
- return effectTypes[`${type}Sponsorship${actionKey}`]
131
- }
132
-
133
- /**
134
- * Check if asset issuer is a source account
135
- * @param {String} account
136
- * @param {String} asset
137
- * @return {Boolean}
138
- */
139
- function isIssuer(account, asset) {
140
- return asset.includes(account)
141
- }
142
-
143
- module.exports = {
144
- fromStroops,
145
- toStroops,
146
- normalizeAddress,
147
- encodeSponsorshipEffectName,
148
- isIssuer,
149
- isAsset
150
- }
package/src/effect.js DELETED
@@ -1,24 +0,0 @@
1
- class Effect {
2
- constructor(type, source) {
3
- }
4
-
5
- /**
6
- * @type {String}
7
- * @readonly
8
- */
9
- type
10
-
11
- /**
12
- * @type {String}
13
- * @readonly
14
- */
15
- source
16
-
17
- toString() {
18
- return this.toJSON()
19
- }
20
-
21
- toJSON() {
22
-
23
- }
24
- }