@stellar-expert/tx-meta-effects-parser 9.0.0 → 9.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,13 +1,18 @@
1
1
  {
2
2
  "name": "@stellar-expert/tx-meta-effects-parser",
3
- "version": "9.0.0",
3
+ "version": "9.2.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)
@@ -828,9 +793,6 @@ class EffectsAnalyzer {
828
793
  }
829
794
  switch (action) {
830
795
  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
796
  break
835
797
  case 'updated':
836
798
  effect.type = effectTypes.contractUpdated
@@ -846,7 +808,7 @@ class EffectsAnalyzer {
846
808
  throw new UnexpectedTxMetaChangeError({type: 'contract', action})
847
809
  }
848
810
  if (effect) {
849
- this.addEffect(effect, effect.type === effectTypes.contractCreated ? 0 : undefined)
811
+ this.addEffect(effect, effect.type === effectTypes.contractCreated ? this.effects.findIndex(e => e.contract === effect.contract || e.owner === effect.contract) : undefined)
850
812
  }
851
813
  if (before?.storage?.length || after?.storage?.length) {
852
814
  this.processInstanceDataChanges(before, after, action === 'restored')
@@ -896,9 +858,19 @@ class EffectsAnalyzer {
896
858
  }
897
859
 
898
860
  processContractCodeChanges({type, action, before, after}) {
899
- const {hash, keyHash} = after || before
861
+ const {hash, keyHash, wasm} = after || before
900
862
  switch (action) {
901
863
  case 'created':
864
+ //ensure that the effect was not processed by the top-level createContract operation call
865
+ if (!this.effects.some(e => e.type === effectTypes.contractCodeUploaded && e.keyHash === keyHash)) {
866
+ const effect = {
867
+ type: effectTypes.contractCodeUploaded,
868
+ wasm,
869
+ wasmHash: hash,
870
+ keyHash
871
+ }
872
+ this.addEffect(effect)
873
+ }
902
874
  break //processed separately
903
875
  case 'updated':
904
876
  break //it doesn't change the state
@@ -971,28 +943,27 @@ class EffectsAnalyzer {
971
943
  }
972
944
  }
973
945
 
974
- processTtlChanges({action, before, after}) {
975
- /*if (action === 'removed')
976
- throw new UnexpectedTxMetaChangeError({type: 'ttl', action})*/
946
+ processTtlChanges({before, after}) {
977
947
  const {keyHash, ttl} = after || before
978
- const stateEffect = this.effects.find(e => e.keyHash === keyHash && e.type !== effectTypes.setTtl)
979
948
  const effect = {
980
949
  type: effectTypes.setTtl,
981
950
  keyHash,
982
951
  ttl
983
952
  }
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
953
+ for (const emitted of this.effects) {
954
+ if (emitted.keyHash === keyHash && emitted.type !== effectTypes.setTtl) {
955
+ if (emitted.type.startsWith('contractCode')) {
956
+ effect.kind = 'contractCode'
957
+ } else if (emitted.type.startsWith('contractData')) {
958
+ effect.kind = 'contractData'
959
+ effect.owner = emitted.owner
960
+ } else if (emitted.type.startsWith('contract')) {
961
+ effect.kind = 'contractData'
962
+ effect.owner = emitted.contract
963
+ } else
964
+ throw new UnexpectedTxMetaChangeError({type: 'ttl', action: emitted.type})
965
+ emitted.ttl = ttl
966
+ }
996
967
  }
997
968
  this.addEffect(effect)
998
969
  }
@@ -1032,6 +1003,20 @@ class EffectsAnalyzer {
1032
1003
  default:
1033
1004
  throw new UnexpectedTxMetaChangeError(change)
1034
1005
  }
1006
+ //ensure that the wasm upload effect always precedes contract creation effect
1007
+ for (let i = 0; i < this.effects.length; i++) {
1008
+ const effect = this.effects[i]
1009
+ if (effect.type === effectTypes.contractCodeUploaded) {
1010
+ //find the first reference in the already emitted effects
1011
+ const createdFromCodeIdx = this.effects.findIndex(e => e.type === effectTypes.contractCreated && e.wasmHash === effect.wasmHash)
1012
+ //reorder effects if needed
1013
+ if (createdFromCodeIdx >= 0) {
1014
+ this.effects.splice(i, 1)
1015
+ this.effects.splice(createdFromCodeIdx, 0, effect)
1016
+ i--
1017
+ }
1018
+ }
1019
+ }
1035
1020
  }
1036
1021
 
1037
1022
  processStateChanges() {
@@ -1048,8 +1033,17 @@ class EffectsAnalyzer {
1048
1033
  retrieveOpContractId() {
1049
1034
  const funcValue = this.operation.func._value._attributes
1050
1035
  if (funcValue) {
1051
- if (funcValue.contractAddress)
1052
- return StrKey.encodeContract(funcValue.contractAddress._value)
1036
+ if (funcValue.contractAddress) {
1037
+ let raw = funcValue.contractAddress._value
1038
+ switch (raw._arm) {
1039
+ case 'ed25519':
1040
+ return StrKey.encodeContract(raw._value)
1041
+ case undefined:
1042
+ return StrKey.encodeContract(raw)
1043
+ default:
1044
+ throw new Error(`Unsupported contract address type: ${raw._arm}`)
1045
+ }
1046
+ }
1053
1047
  const preimage = funcValue.contractIdPreimage
1054
1048
  if (preimage)
1055
1049
  return contractIdFromPreimage(preimage, this.network)
@@ -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:
@@ -322,14 +318,18 @@ function parseStateOwnerDataAddress(contract) {
322
318
  return xdrParseAccountAddress(contract.accountId())
323
319
  }
324
320
 
325
- function parseContractCode(value) {
321
+ function parseContractCode(value, actionType) {
326
322
  const contract = value.value()
327
323
  const hash = contract.hash()
328
- return {
324
+ const res = {
329
325
  entry: 'contractCode',
330
326
  hash: hash.toString('hex'),
331
327
  keyHash: generateContractCodeEntryHash(hash)
332
328
  }
329
+ if (actionType === 'created') {
330
+ res.wasm = contract.code().toString('base64')
331
+ }
332
+ return res
333
333
  }
334
334
 
335
335
  module.exports = {parseLedgerEntryChanges}