@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/README.MD +700 -494
- package/package.json +8 -3
- package/src/effects-analyzer.js +51 -64
- package/src/parser/ledger-entry-changes-parser.js +13 -11
package/package.json
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stellar-expert/tx-meta-effects-parser",
|
|
3
|
-
"version": "9.
|
|
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
|
},
|
package/src/effects-analyzer.js
CHANGED
|
@@ -144,7 +144,7 @@ class EffectsAnalyzer {
|
|
|
144
144
|
if (!effect.source) {
|
|
145
145
|
effect.source = this.source
|
|
146
146
|
}
|
|
147
|
-
if (atPosition
|
|
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':
|
|
543
|
-
|
|
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 ?
|
|
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({
|
|
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
|
-
|
|
985
|
-
if (
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
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
|
|
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
|
-
|
|
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}
|