@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/README.MD +700 -494
- package/package.json +8 -3
- package/src/effects-analyzer.js +60 -66
- package/src/parser/ledger-entry-changes-parser.js +10 -10
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.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
|
},
|
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)
|
|
@@ -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 ?
|
|
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({
|
|
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
|
-
|
|
985
|
-
if (
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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}
|