@stellar-expert/tx-meta-effects-parser 9.1.0 → 9.3.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,13 +1,18 @@
1
1
  {
2
2
  "name": "@stellar-expert/tx-meta-effects-parser",
3
- "version": "9.1.0",
3
+ "version": "9.3.0",
4
4
  "description": "Low-level effects parser for Stellar transaction results and meta XDR",
5
5
  "main": "src/index.js",
6
+ "author": "team@stellar.expert",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/stellar-expert/tx-meta-effects-parser.git"
11
+ },
12
+ "bugs": "https://github.com/stellar-expert/tx-meta-effects-parser/issues",
6
13
  "scripts": {
7
14
  "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
8
15
  },
9
- "author": "team@stellar.expert",
10
- "license": "MIT",
11
16
  "peerDependencies": {
12
17
  "@stellar/stellar-sdk": ">=16"
13
18
  },
@@ -144,7 +144,7 @@ class EffectsAnalyzer {
144
144
  if (!effect.source) {
145
145
  effect.source = this.source
146
146
  }
147
- if (atPosition !== undefined) {
147
+ if (atPosition >= 0) {
148
148
  this.effects.splice(atPosition < 0 ? 0 : atPosition, 0, effect)
149
149
  } else {
150
150
  this.effects.push(effect)
@@ -413,46 +413,7 @@ class EffectsAnalyzer {
413
413
  break
414
414
  }
415
415
  case 'createContract':
416
- case 'createContractV2':
417
- const preimage = value.contractIdPreimage()
418
- const executable = value.executable()
419
- const executableType = executable.switch().name
420
-
421
- const effect = {
422
- type: effectTypes.contractCreated,
423
- contract: contractIdFromPreimage(preimage, this.network)
424
- }
425
- switch (executableType) {
426
- case 'contractExecutableWasm':
427
- effect.kind = 'wasm'
428
- effect.wasmHash = executable.wasmHash().toString('hex')
429
- break
430
- case 'contractExecutableStellarAsset':
431
- const preimageParams = preimage.value()
432
- switch (preimage.switch().name) {
433
- case 'contractIdPreimageFromAddress':
434
- effect.kind = 'fromAddress'
435
- effect.issuer = xdrParseAccountAddress(preimageParams.address().value())
436
- effect.salt = preimageParams.salt().toString('base64')
437
- break
438
- case 'contractIdPreimageFromAsset':
439
- effect.kind = 'fromAsset'
440
- effect.asset = xdrParseAsset(preimageParams)
441
- break
442
- default:
443
- throw new TxMetaEffectParserError('Unknown preimage type: ' + preimage.switch().name)
444
- }
445
- break
446
- default:
447
- throw new TxMetaEffectParserError('Unknown contract type: ' + executableType)
448
- }
449
- if (func.arm() === 'createContractV2') {
450
- const args = value.constructorArgs() //array
451
- if (args.length > 0) {
452
- effect.constructorArgs = args.map(arg => arg.toXDR('base64'))
453
- }
454
- }
455
- this.addEffect(effect, 0)
416
+ case 'createContractV2': //handled in entry changes
456
417
  break
457
418
  default:
458
419
  throw new TxMetaEffectParserError('Unknown host function call type: ' + func.arm())
@@ -539,8 +500,12 @@ class EffectsAnalyzer {
539
500
  effect.balance = before?.balanceId || after?.balanceId
540
501
  //TODO: add claimable balance asset to the effect
541
502
  break
542
- case 'liquidityPool': //ignore??
543
- continue
503
+ case 'liquidityPool':
504
+ //sponsored liquidity pool entry is never removed, so only created/updated effects are emitted
505
+ if (action !== 'created' && action !== 'updated')
506
+ continue
507
+ effect.pool = before?.pool || after?.pool
508
+ break
544
509
  }
545
510
  effect.type = encodeSponsorshipEffectName(action, type)
546
511
  this.addEffect(effect)
@@ -823,14 +788,13 @@ class EffectsAnalyzer {
823
788
  case 'wasm':
824
789
  effect.wasmHash = after.wasmHash
825
790
  break
791
+ case 'fromAddress':
792
+ break
826
793
  default:
827
794
  throw new TxMetaEffectParserError('Unexpected contract type: ' + kind)
828
795
  }
829
796
  switch (action) {
830
797
  case 'created':
831
- if (this.effects.some(e => e.type === effectTypes.contractCreated && e.contract === contract)) {
832
- effect = undefined //skip contract creation effects processed by top-level createContract operation call
833
- }
834
798
  break
835
799
  case 'updated':
836
800
  effect.type = effectTypes.contractUpdated
@@ -846,7 +810,7 @@ class EffectsAnalyzer {
846
810
  throw new UnexpectedTxMetaChangeError({type: 'contract', action})
847
811
  }
848
812
  if (effect) {
849
- this.addEffect(effect, effect.type === effectTypes.contractCreated ? 0 : undefined)
813
+ this.addEffect(effect, effect.type === effectTypes.contractCreated ? this.effects.findIndex(e => e.contract === effect.contract || e.owner === effect.contract) : undefined)
850
814
  }
851
815
  if (before?.storage?.length || after?.storage?.length) {
852
816
  this.processInstanceDataChanges(before, after, action === 'restored')
@@ -896,9 +860,19 @@ class EffectsAnalyzer {
896
860
  }
897
861
 
898
862
  processContractCodeChanges({type, action, before, after}) {
899
- const {hash, keyHash} = after || before
863
+ const {hash, keyHash, wasm} = after || before
900
864
  switch (action) {
901
865
  case 'created':
866
+ //ensure that the effect was not processed by the top-level createContract operation call
867
+ if (!this.effects.some(e => e.type === effectTypes.contractCodeUploaded && e.keyHash === keyHash)) {
868
+ const effect = {
869
+ type: effectTypes.contractCodeUploaded,
870
+ wasm,
871
+ wasmHash: hash,
872
+ keyHash
873
+ }
874
+ this.addEffect(effect)
875
+ }
902
876
  break //processed separately
903
877
  case 'updated':
904
878
  break //it doesn't change the state
@@ -971,28 +945,27 @@ class EffectsAnalyzer {
971
945
  }
972
946
  }
973
947
 
974
- processTtlChanges({action, before, after}) {
975
- /*if (action === 'removed')
976
- throw new UnexpectedTxMetaChangeError({type: 'ttl', action})*/
948
+ processTtlChanges({before, after}) {
977
949
  const {keyHash, ttl} = after || before
978
- const stateEffect = this.effects.find(e => e.keyHash === keyHash && e.type !== effectTypes.setTtl)
979
950
  const effect = {
980
951
  type: effectTypes.setTtl,
981
952
  keyHash,
982
953
  ttl
983
954
  }
984
- if (stateEffect) {
985
- if (stateEffect.type.startsWith('contractCode')) {
986
- effect.kind = 'contractCode'
987
- } else if (stateEffect.type.startsWith('contractData')) {
988
- effect.kind = 'contractData'
989
- effect.owner = stateEffect.owner
990
- } else if (stateEffect.type.startsWith('contract')) {
991
- effect.kind = 'contractData'
992
- effect.owner = stateEffect.contract
993
- } else
994
- throw new UnexpectedTxMetaChangeError({type: 'ttl', action: stateEffect.type})
995
- stateEffect.ttl = ttl
955
+ for (const emitted of this.effects) {
956
+ if (emitted.keyHash === keyHash && emitted.type !== effectTypes.setTtl) {
957
+ if (emitted.type.startsWith('contractCode')) {
958
+ effect.kind = 'contractCode'
959
+ } else if (emitted.type.startsWith('contractData')) {
960
+ effect.kind = 'contractData'
961
+ effect.owner = emitted.owner
962
+ } else if (emitted.type.startsWith('contract')) {
963
+ effect.kind = 'contractData'
964
+ effect.owner = emitted.contract
965
+ } else
966
+ throw new UnexpectedTxMetaChangeError({type: 'ttl', action: emitted.type})
967
+ emitted.ttl = ttl
968
+ }
996
969
  }
997
970
  this.addEffect(effect)
998
971
  }
@@ -1032,6 +1005,20 @@ class EffectsAnalyzer {
1032
1005
  default:
1033
1006
  throw new UnexpectedTxMetaChangeError(change)
1034
1007
  }
1008
+ //ensure that the wasm upload effect always precedes contract creation effect
1009
+ for (let i = 0; i < this.effects.length; i++) {
1010
+ const effect = this.effects[i]
1011
+ if (effect.type === effectTypes.contractCodeUploaded) {
1012
+ //find the first reference in the already emitted effects
1013
+ const createdFromCodeIdx = this.effects.findIndex(e => e.type === effectTypes.contractCreated && e.wasmHash === effect.wasmHash)
1014
+ //reorder effects if needed
1015
+ if (createdFromCodeIdx >= 0) {
1016
+ this.effects.splice(i, 1)
1017
+ this.effects.splice(createdFromCodeIdx, 0, effect)
1018
+ i--
1019
+ }
1020
+ }
1021
+ }
1035
1022
  }
1036
1023
 
1037
1024
  processStateChanges() {
@@ -1,5 +1,5 @@
1
1
  const {StrKey} = require('@stellar/stellar-sdk')
2
- const {TxMetaEffectParserError, UnexpectedTxMetaChangeError} = require('../errors')
2
+ const {TxMetaEffectParserError} = require('../errors')
3
3
  const {
4
4
  xdrParseAsset,
5
5
  xdrParseAccountAddress,
@@ -41,8 +41,6 @@ function parseLedgerEntryChanges(ledgerEntryChanges, filter = undefined) {
41
41
  state = stateData
42
42
  continue
43
43
  case 'created':
44
- if (type === 'contractCode')
45
- continue //processed in operation handler
46
44
  change.before = null
47
45
  change.after = stateData
48
46
  change.type = stateData.entry
@@ -59,8 +57,6 @@ function parseLedgerEntryChanges(ledgerEntryChanges, filter = undefined) {
59
57
  state = change.after
60
58
  break
61
59
  case 'removed':
62
- if (!state && type === 'ttl')
63
- continue //skip expiration processing for now
64
60
  change.before = state
65
61
  change.after = null
66
62
  change.type = state.entry
@@ -85,14 +81,14 @@ function parseEntry(entry, actionType) {
85
81
  if (actionType === 'removed')
86
82
  return null
87
83
  const value = entry.value()
88
- const parsed = parseEntryData(value.data())
84
+ const parsed = parseEntryData(value.data(), actionType)
89
85
  if (parsed === null)
90
86
  return null
91
87
  //parsed.modified = entry.lastModifiedLedgerSeq()
92
88
  return parseLedgerEntryExt(parsed, value)
93
89
  }
94
90
 
95
- function parseEntryData(data) {
91
+ function parseEntryData(data, actionType) {
96
92
  const updatedEntryType = data.arm()
97
93
  switch (updatedEntryType) {
98
94
  case 'account':
@@ -112,7 +108,7 @@ function parseEntryData(data) {
112
108
  case 'contractData':
113
109
  return parseContractData(data)
114
110
  case 'contractCode':
115
- return parseContractCode(data)
111
+ return parseContractCode(data, actionType)
116
112
  case 'ttl':
117
113
  return parseTtl(data)
118
114
  default:
@@ -282,12 +278,14 @@ function parseContractData(value) {
282
278
  const type = instance.executable._switch.name
283
279
  switch (type) {
284
280
  case 'contractExecutableStellarAsset':
285
- entry.kind = 'fromAsset'
286
281
  if (instance.storage?.length) { //if not -- the asset has been created "fromAddress" - no metadata in this case
282
+ entry.kind = 'fromAsset'
287
283
  const metaArgs = instance.storage[0]._attributes
288
284
  if (metaArgs.key._value.toString() !== 'METADATA')
289
285
  throw new TxMetaEffectParserError('Unexpected asset initialization metadata')
290
286
  entry.asset = xdrParseAsset(metaArgs.val._value[1]._attributes.val._value.toString())
287
+ } else {
288
+ entry.kind = 'fromAddress'
291
289
  }
292
290
  break
293
291
  case 'contractExecutableWasm':
@@ -322,14 +320,18 @@ function parseStateOwnerDataAddress(contract) {
322
320
  return xdrParseAccountAddress(contract.accountId())
323
321
  }
324
322
 
325
- function parseContractCode(value) {
323
+ function parseContractCode(value, actionType) {
326
324
  const contract = value.value()
327
325
  const hash = contract.hash()
328
- return {
326
+ const res = {
329
327
  entry: 'contractCode',
330
328
  hash: hash.toString('hex'),
331
329
  keyHash: generateContractCodeEntryHash(hash)
332
330
  }
331
+ if (actionType === 'created') {
332
+ res.wasm = contract.code().toString('base64')
333
+ }
334
+ return res
333
335
  }
334
336
 
335
337
  module.exports = {parseLedgerEntryChanges}