@stellar-expert/tx-meta-effects-parser 5.0.0-beta10

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.
@@ -0,0 +1,35 @@
1
+ const {parseLedgerEntryChanges} = require('./ledger-entry-changes-parser')
2
+ const {TxMetaEffectParserError} = require('./errors')
3
+
4
+ /**
5
+ * Parse top-level transaction metadata changes
6
+ * @param {TransactionMeta} meta
7
+ * @return {ParsedLedgerEntryMeta[]}
8
+ */
9
+ function parseTxMetaChanges(meta) {
10
+ const transactionMeta = meta.value()
11
+ const txMetaChanges = []
12
+
13
+ switch (meta.arm()) {
14
+ case 'v1':
15
+ retrieveTopLevelChanges(transactionMeta.txChanges(), txMetaChanges)
16
+ break
17
+ case 'v2':
18
+ case 'v3':
19
+ retrieveTopLevelChanges(transactionMeta.txChangesBefore(), txMetaChanges)
20
+ retrieveTopLevelChanges(transactionMeta.txChangesAfter(), txMetaChanges)
21
+ break
22
+ default:
23
+ throw new TxMetaEffectParserError(`Transaction meta version ${meta.arm()} is not supported.`)
24
+ }
25
+
26
+ return txMetaChanges
27
+ }
28
+
29
+ function retrieveTopLevelChanges(changes, res) {
30
+ for (const entry of parseLedgerEntryChanges(changes)) {
31
+ res.push(entry)
32
+ }
33
+ }
34
+
35
+ module.exports = {parseTxMetaChanges}
@@ -0,0 +1,127 @@
1
+ const {xdr} = require('@stellar/stellar-base')
2
+ const {xdrParseAccountAddress, xdrParseTradeAtom, xdrParseClaimedOffer, xdrParseAsset} = require('./tx-xdr-parser-utils')
3
+ const {TxMetaEffectParserError} = require('./errors')
4
+
5
+ /**
6
+ * Parse extra data from operation result
7
+ * @param {Object} rawOpResult - Operation result XDR
8
+ * @return {Object}
9
+ */
10
+ function parseRawOpResult(rawOpResult) {
11
+ const inner = rawOpResult.tr()
12
+ if (inner === undefined)
13
+ return null //"opNoAccount" Case
14
+ const opResult = inner.value()
15
+ const successOpResultType = opResult.switch()
16
+
17
+ //no need to parse failed operations
18
+ if (successOpResultType.value < 0)
19
+ return null
20
+
21
+ const res = {
22
+ resultType: successOpResultType.name
23
+ }
24
+
25
+ switch (successOpResultType.name) {
26
+ case 'pathPaymentStrictReceiveSuccess':
27
+ case 'pathPaymentStrictSendSuccess': {
28
+ res.claimedOffers = opResult.value().offers().map(claimedOffer => xdrParseClaimedOffer(claimedOffer))
29
+ const paymentValue = opResult.value().last()
30
+ res.payment = {
31
+ account: xdrParseAccountAddress(paymentValue.destination()),
32
+ amount: paymentValue.amount().toString(),
33
+ asset: xdrParseAsset(paymentValue.asset())
34
+ }
35
+ }
36
+ break
37
+ case 'manageSellOfferSuccess':
38
+ case 'manageBuyOfferSuccess': {
39
+ const makerOfferXdr = opResult.value().offer().value()
40
+ res.makerOffer = makerOfferXdr && xdrParseTradeAtom(makerOfferXdr)
41
+ res.claimedOffers = opResult.value().offersClaimed().map(claimedOffer => xdrParseClaimedOffer(claimedOffer))
42
+ }
43
+ break
44
+ case 'accountMergeSuccess':
45
+ //retrieve the actual amount of transferred XLM
46
+ res.actualMergedAmount = opResult.sourceAccountBalance().toString()
47
+ break
48
+ case 'inflationSuccess':
49
+ res.inflationPayouts = (opResult.payouts() || []).map(payout => ({
50
+ account: xdrParseAccountAddress(payout.destination()),
51
+ amount: payout.amount().toString()
52
+ }))
53
+ break
54
+ case 'createClaimableBalanceSuccess':
55
+ res.balanceId = Buffer.from(opResult.balanceId().value()).toString('hex')
56
+ break
57
+ case 'invokeHostFunctionSuccess':
58
+ res.result = opResult.value()
59
+ break
60
+ case 'setOptionsSuccess':
61
+ case 'manageDataSuccess':
62
+ case 'createAccountSuccess':
63
+ case 'paymentSuccess':
64
+ case 'changeTrustSuccess':
65
+ case 'allowTrustSuccess':
66
+ case 'bumpSequenceSuccess':
67
+ case 'claimClaimableBalanceSuccess':
68
+ case 'beginSponsoringFutureReservesSuccess':
69
+ case 'endSponsoringFutureReservesSuccess':
70
+ case 'revokeSponsorshipSuccess':
71
+ case 'clawbackSuccess':
72
+ case 'clawbackClaimableBalanceSuccess':
73
+ case 'setTrustLineFlagsSuccess':
74
+ case 'liquidityPoolDepositSuccess':
75
+ case 'liquidityPoolWithdrawSuccess':
76
+ case 'restoreFootprintSuccess':
77
+ case 'extendFootprintTtlSuccess':
78
+ break //no extra info available
79
+ default:
80
+ throw new TxMetaEffectParserError(`Unknown op result: ${successOpResultType.name}`)
81
+ }
82
+ return res
83
+ }
84
+
85
+ /**
86
+ * @typedef {Object} ParsedTxResult
87
+ * @property {Object} feeCharged
88
+ * @property {Boolean} success
89
+ * @property {Array<Object>} opResults
90
+ */
91
+
92
+ /**
93
+ * Parse single transaction result.
94
+ * @param {Object|String} result - Raw transaction result XDR.
95
+ * @return {ParsedTxResult}
96
+ */
97
+ function parseTxResult(result) {
98
+ if (typeof result === 'string') {
99
+ result = xdr.TransactionResult.fromXDR(result, 'base64')
100
+ }
101
+ const innerResult = result.result()
102
+ const txResultState = innerResult.switch()
103
+ const feeCharged = result.feeCharged().toString()
104
+ const success = txResultState.value >= 0
105
+
106
+ let opResults = []
107
+ if (success) {
108
+ switch (txResultState.name) {
109
+ case 'txFeeBumpInnerSuccess':
110
+ //const childTxHash = innerResult.innerResultPair().transactionHash()
111
+ opResults = (innerResult.value().result().result().results() || []).map(parseRawOpResult)
112
+ break
113
+ case 'txSuccess':
114
+ opResults = (innerResult.results() || []).map(parseRawOpResult)
115
+ break
116
+ default:
117
+ throw new TxMetaEffectParserError(`Invalid tx result state switch: ${txResultState.name}`)
118
+ }
119
+ }
120
+ return {
121
+ success,
122
+ opResults,
123
+ feeCharged
124
+ }
125
+ }
126
+
127
+ module.exports = {parseTxResult}
@@ -0,0 +1,276 @@
1
+ const {StrKey, LiquidityPoolId, scValToBigInt, xdr} = require('@stellar/stellar-base')
2
+ const {TxMetaEffectParserError} = require('./errors')
3
+
4
+ /**
5
+ * Parse account address from XDR representation
6
+ * @param accountId
7
+ * @param muxedAccountsSupported
8
+ * @return {String|{muxedId: String, primary: String}}
9
+ */
10
+ function xdrParseAccountAddress(accountId, muxedAccountsSupported = false) {
11
+ if (!accountId)
12
+ return undefined
13
+ if (accountId.arm) {
14
+ switch (accountId.arm()) {
15
+ case 'ed25519':
16
+ return StrKey.encodeEd25519PublicKey(accountId.ed25519())
17
+ case 'med25519':
18
+ if (!muxedAccountsSupported)
19
+ throw new TxMetaEffectParserError(`Muxed accounts not supported here`)
20
+ return {
21
+ primary: StrKey.encodeEd25519PublicKey(accountId.value().ed25519()),
22
+ muxedId: accountId.value().id().toString()
23
+ }
24
+ default:
25
+ throw new TxMetaEffectParserError(`Unsupported account type: ${accountId.arm()}`)
26
+ }
27
+ }
28
+ if (accountId instanceof Buffer) {
29
+ return StrKey.encodeEd25519PublicKey(accountId)
30
+ }
31
+ throw new TypeError(`Failed to identify and parse account address: ${accountId}`)
32
+ }
33
+
34
+ /**
35
+ * Parse Contract ID from raw bytes
36
+ * @param {Buffer} rawContractId
37
+ * @return {String}
38
+ */
39
+ function xdrParseContractAddress(rawContractId) {
40
+ return StrKey.encodeContract(rawContractId)
41
+ }
42
+
43
+ /**
44
+ * Parse XDR price representation
45
+ * @param {{n: Function, d: Function}} price
46
+ * @return {Number}
47
+ */
48
+ function xdrParsePrice(price) {
49
+ return price.n() / price.d()
50
+ }
51
+
52
+ /**
53
+ * Parse account signer key XDR
54
+ * @param {xdr.SignerKey} signer
55
+ * @return {String}
56
+ */
57
+ function xdrParseSignerKey(signer) {
58
+ const type = signer.arm()
59
+ switch (type) {
60
+ case 'ed25519':
61
+ return StrKey.encodeEd25519PublicKey(signer.ed25519())
62
+ case 'preAuthTx':
63
+ return StrKey.encodePreAuthTx(signer.preAuthTx())
64
+ case 'hashX':
65
+ return StrKey.encodeSha256Hash(signer.hashX())
66
+ case 'ed25519SignedPayload':
67
+ return StrKey.encodeSignedPayload(signer.ed25519SignedPayload()) //TODO: check
68
+ }
69
+ throw new TxMetaEffectParserError(`Unsupported signer type: "${type}"`)
70
+ }
71
+
72
+
73
+ /**
74
+ * @typedef {Object} ParsedOffer
75
+ * @property {String} account
76
+ * @property {Array<String>} asset
77
+ * @property {Array<String>} amount
78
+ * @property {String} offerId?
79
+ * @property {Buffer} poolId?
80
+ */
81
+
82
+ /**
83
+ * Parse maker offer descriptor from raw XDR.
84
+ * @param {Object} offerXdr
85
+ * @return {ParsedOffer}
86
+ */
87
+ function xdrParseTradeAtom(offerXdr) {
88
+ return {
89
+ offerId: offerXdr.offerId().toString(),
90
+ account: xdrParseAccountAddress(offerXdr.sellerId()),
91
+ asset: [xdrParseAsset(offerXdr.selling()).toString(), xdrParseAsset(offerXdr.buying()).toString()],
92
+ //offer amount is always stored in terms of a selling asset, even for buy offers
93
+ amount: (offerXdr.amount() || offerXdr.buyAmount()).toString(),
94
+ //flags: offerXdr.flags()
95
+ price: xdrParsePrice(offerXdr.price())
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Parse claimed offer atom from raw XDR.
101
+ * @param {xdr.ClaimAtom} claimedAtom
102
+ * @return {ParsedOffer}
103
+ */
104
+ function xdrParseClaimedOffer(claimedAtom) {
105
+ const atomType = claimedAtom.arm()
106
+ let res
107
+ switch (atomType) {
108
+ case 'v0':
109
+ claimedAtom = claimedAtom.v0()
110
+ res = {
111
+ account: xdrParseAccountAddress(claimedAtom.sellerEd25519()),
112
+ offerId: claimedAtom.offerId().toString()
113
+ }
114
+ break
115
+ case 'orderBook':
116
+ claimedAtom = claimedAtom.orderBook()
117
+ res = {
118
+ account: xdrParseAccountAddress(claimedAtom.sellerId()),
119
+ offerId: claimedAtom.offerId().toString()
120
+ }
121
+ break
122
+ case 'liquidityPool':
123
+ claimedAtom = claimedAtom.liquidityPool()
124
+ res = {
125
+ poolId: claimedAtom.liquidityPoolId()
126
+ }
127
+ break
128
+ default:
129
+ throw new TxMetaEffectParserError(`Unsupported claimed atom type: ` + atomType)
130
+ }
131
+ return {
132
+ asset: [
133
+ xdrParseAsset(claimedAtom.assetSold()),
134
+ xdrParseAsset(claimedAtom.assetBought())
135
+ ],
136
+ amount: [
137
+ claimedAtom.amountSold().toString(),
138
+ claimedAtom.amountBought().toString()
139
+ ],
140
+ ...res
141
+ }
142
+ }
143
+
144
+ function xdrParseClaimantPredicate(predicate) {
145
+ if (!predicate) return {}
146
+ const type = predicate.switch().name
147
+ const value = predicate.value()
148
+ switch (type) {
149
+ case 'claimPredicateUnconditional':
150
+ return {}
151
+ case 'claimPredicateAnd':
152
+ return {and: value.map(p => xdrParseClaimantPredicate(p))}
153
+ case 'claimPredicateOr':
154
+ return {or: value.map(p => xdrParseClaimantPredicate(p))}
155
+ case 'claimPredicateNot':
156
+ return {not: xdrParseClaimantPredicate(value)}
157
+ case 'claimPredicateBeforeAbsoluteTime':
158
+ return {absBefore: value.toString()}
159
+ case 'claimPredicateBeforeRelativeTime':
160
+ return {relBefore: value.toString()}
161
+ default:
162
+ throw new TxMetaEffectParserError(`Unknown claim condition predicate: ${type}`)
163
+ }
164
+ }
165
+
166
+ function xdrParseClaimant(claimant) {
167
+ const value = claimant.value()
168
+ return {
169
+ destination: xdrParseAccountAddress(value.destination()),
170
+ predicate: xdrParseClaimantPredicate(value.predicate())
171
+ }
172
+ }
173
+
174
+ function xdrParseAsset(src) {
175
+ if (!src)
176
+ return undefined
177
+
178
+ if (src.arm) { //XDR
179
+ switch (src.switch().name) {
180
+ case 'assetTypeNative':
181
+ return 'XLM'
182
+ case 'assetTypePoolShare': {
183
+ const poolId = src.value()
184
+ if (poolId.length)
185
+ return poolId.toString('hex')
186
+ if (poolId.constantProduct)
187
+ return LiquidityPoolId.fromOperation(poolId).getLiquidityPoolId()
188
+ throw new TxMetaEffectParserError('Unsupported liquidity pool asset id format')
189
+ }
190
+ default: {
191
+ const value = src.value()
192
+ return `${value.assetCode().toString().replace(/\0+$/, '')}-${StrKey.encodeEd25519PublicKey(value.issuer().ed25519())}-${src.arm() === 'alphaNum4' ? 1 : 2}`
193
+ }
194
+ }
195
+ }
196
+
197
+ if (typeof src === 'string') {
198
+ if (src === 'XLM' || src === 'native' || src.includes('-'))
199
+ return 'XLM'//already parsed value
200
+ if (src.includes(':')) {
201
+ const [code, issuer] = src.split(':')
202
+ return `${code.replace(/\0+$/, '')}-${issuer}-${code.length > 4 ? 2 : 1}`
203
+ }
204
+ if (src.length === 64)
205
+ return src //pool id
206
+ }
207
+ if (src.type === 0 && !src.code || src.code === 'XLM' && !src.issuer)
208
+ return 'XLM'
209
+ if (src.code && src.issuer)
210
+ return `${src.code}-${src.issuer}-${src.type || (src.code.length > 4 ? 2 : 1)}`
211
+ }
212
+
213
+
214
+ function xdrParseScVal(value, treatBytesAsContractId = false) {
215
+ if (typeof value === 'string') {
216
+ value = xdr.ScVal.fromXDR(value, 'base64')
217
+ }
218
+ switch (value._arm) {
219
+ case 'vec':
220
+ return value._value.map(xdrParseScVal)
221
+ case 'map':
222
+ const res = {}
223
+ for (const entry of value._value) {
224
+ res[xdrParseScVal(entry.key())] = xdrParseScVal(entry.val())
225
+ }
226
+ return res
227
+ case 'i256':
228
+ case 'u256':
229
+ case 'i128':
230
+ case 'u128':
231
+ case 'i64':
232
+ case 'u64':
233
+ case 'timepoint':
234
+ case 'duration':
235
+ return scValToBigInt(value).toString()
236
+ case 'address':
237
+ if (value._value._arm === 'accountId')
238
+ return xdrParseAccountAddress(value._value.value())
239
+ if (value._value._arm === 'contractId')
240
+ return xdrParseContractAddress(value._value.value())
241
+ throw new TxMetaEffectParserError('Not supported XDR primitive type: ' + value.toString())
242
+ case 'bytes':
243
+ return treatBytesAsContractId ? xdrParseContractAddress(value.value()) : value._value.toString('base64')
244
+ case 'i32':
245
+ case 'u32':
246
+ case 'b':
247
+ return value._value
248
+ case 'str':
249
+ case 'sym':
250
+ return value._value.toString()
251
+ case 'nonceKey':
252
+ return value._value.nonce()._value.toString()
253
+ case 'instance':
254
+ return value._value.executable.wasmHash().toString('base64')
255
+ case 'error':
256
+ return value.toXDR('base64')
257
+ case 'contractId':
258
+ return xdrParseContractAddress(value._value)
259
+ default:
260
+ if (value.switch().name === 'scvVoid') //scVoid
261
+ return undefined
262
+ throw new TxMetaEffectParserError('Not supported XDR primitive type: ' + value.toXDR ? value.toXDR() : value.toString())
263
+ }
264
+ }
265
+
266
+ module.exports = {
267
+ xdrParseAsset,
268
+ xdrParseAccountAddress,
269
+ xdrParseContractAddress,
270
+ xdrParseClaimant,
271
+ xdrParseClaimedOffer,
272
+ xdrParseTradeAtom,
273
+ xdrParseSignerKey,
274
+ xdrParsePrice,
275
+ xdrParseScVal
276
+ }