@stellar-expert/tx-meta-effects-parser 7.0.0-rc.9 → 8.0.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.
@@ -1,313 +1,337 @@
1
- const {StrKey} = require('@stellar/stellar-base')
2
- const {TxMetaEffectParserError, UnexpectedTxMetaChangeError} = require('../errors')
3
- const {xdrParseAsset, xdrParseAccountAddress, xdrParseClaimant, xdrParsePrice, xdrParseSignerKey} = require('./tx-xdr-parser-utils')
4
- const {generateContractStateEntryHash, generateContractCodeEntryHash} = require('./ledger-key')
5
-
6
- /**
7
- * @typedef {{}} ParsedLedgerEntryMeta
8
- * @property {'account'|'trustline'|'offer'|'data'|'liquidityPool'|'claimableBalance'|'contractData'|'contractCode'|'ttl'} type - Ledger entry type
9
- * @property {'created'|'updated'|'removed'} action - Ledger modification action
10
- * @property {{}} before - Ledger entry state before changes applied
11
- * @property {{}} after - Ledger entry state after changes application
12
- */
13
-
14
- /**
15
- * @param {LedgerEntryChange[]} ledgerEntryChanges
16
- * @return {ParsedLedgerEntryMeta[]}
17
- */
18
- function parseLedgerEntryChanges(ledgerEntryChanges) {
19
- const changes = []
20
- let state
21
- for (let i = 0; i < ledgerEntryChanges.length; i++) {
22
- const entry = ledgerEntryChanges[i]
23
- const action = entry._arm
24
- const stateData = parseEntry(entry, action)
25
- if (stateData === undefined)
26
- continue
27
- const change = {action}
28
- const type = entry._value._arm
29
- switch (action) {
30
- case 'state':
31
- state = stateData
32
- continue
33
- case 'created':
34
- if (type === 'contractCode')
35
- continue //processed in operation handler
36
- change.before = null
37
- change.after = stateData
38
- change.type = stateData.entry
39
- break
40
- case 'updated':
41
- if (type === 'contractCode')
42
- throw new UnexpectedTxMetaChangeError({type, action})
43
- change.before = state
44
- change.after = stateData
45
- change.type = stateData.entry
46
- break
47
- case 'removed':
48
- if (!state && type === 'ttl')
49
- continue //skip expiration processing for now
50
- change.before = state
51
- change.after = null
52
- change.type = state.entry
53
- break
54
- default:
55
- throw new TxMetaEffectParserError(`Unknown change entry type: ${action}`)
56
- }
57
- changes.push(change)
58
- state = null
59
- }
60
- return changes
61
- }
62
-
63
- function parseEntry(entry, actionType) {
64
- if (actionType === 'removed')
65
- return null
66
- const value = entry.value()
67
- const parsed = parseEntryData(value.data())
68
- if (parsed === null)
69
- return null
70
- //parsed.modified = entry.lastModifiedLedgerSeq()
71
- return parseLedgerEntryExt(parsed, value)
72
- }
73
-
74
- function parseEntryData(data) {
75
- const updatedEntryType = data.arm()
76
- switch (updatedEntryType) {
77
- case 'account':
78
- return parseAccountEntry(data)
79
- case 'trustline':
80
- case 'trustLine':
81
- return parseTrustlineEntry(data)
82
- case 'offer':
83
- return parseOfferEntry(data)
84
- case 'data':
85
- case 'datum':
86
- return parseDataEntry(data)
87
- case 'claimableBalance':
88
- return parseClaimableBalanceEntry(data)
89
- case 'liquidityPool':
90
- return parseLiquidityPoolEntry(data)
91
- case 'contractData':
92
- return parseContractData(data)
93
- case 'contractCode':
94
- return parseContractCode(data)
95
- case 'ttl':
96
- return parseTtl(data)
97
- default:
98
- throw new TxMetaEffectParserError(`Unknown meta entry type: ${updatedEntryType}`)
99
- }
100
- }
101
-
102
- function parseLedgerEntryExt(data, entry) {
103
- const v1 = entry.ext()?.v1()
104
- if (v1) {
105
- const sponsor = v1.sponsoringId()
106
- if (sponsor) {
107
- data.sponsor = xdrParseAccountAddress(sponsor)
108
- }
109
- }
110
- return data
111
- }
112
-
113
- function parseAccountEntry(value) {
114
- const accountEntryXdr = value.value()
115
- const data = {
116
- entry: 'account',
117
- address: xdrParseAccountAddress(accountEntryXdr.accountId()),
118
- sequence: accountEntryXdr.seqNum().toString(),
119
- balance: accountEntryXdr.balance().toString(),
120
- homeDomain: accountEntryXdr.homeDomain().toString('UTF8'),
121
- inflationDest: xdrParseAccountAddress(accountEntryXdr.inflationDest()),
122
- flags: accountEntryXdr.flags(),
123
- signers: accountEntryXdr.signers().map(signer => ({
124
- key: xdrParseSignerKey(signer.key()),
125
- weight: signer.weight()
126
- }))
127
- }
128
- const thresholds = accountEntryXdr.thresholds()
129
- data.thresholds = thresholds.slice(1).join()
130
- data.masterWeight = thresholds[0]
131
- const extV1 = accountEntryXdr.ext()?.v1()
132
- if (extV1) {
133
- const extV2 = extV1.ext()?.v2()
134
- if (extV2) {
135
- const sponsoringIDs = extV2.signerSponsoringIDs()
136
- if (sponsoringIDs.length > 0) {
137
- for (let i = 0; i < data.signers.length; i++) {
138
- const sponsor = sponsoringIDs[i]
139
- if (sponsor) { //attach sponsors directly to the signers
140
- data.signers[i].sponsor = xdrParseAccountAddress(sponsor)
141
- }
142
- }
143
- }
144
- }
145
- }
146
- //ignored fields: numSubEntries, extV1.liabilities, extV2.numSponsored, extV2.numSponsoring, extV3.seqLedger, extv3.seqTime
147
- return data
148
- }
149
-
150
- function parseTrustlineEntry(value) {
151
- const trustlineEntryXdr = value.value()
152
- const trustlineAsset = trustlineEntryXdr.asset()
153
- const trustlineType = trustlineAsset.switch()
154
- let asset
155
- switch (trustlineType.value) {
156
- case 0:
157
- case 1:
158
- case 2:
159
- asset = xdrParseAsset(trustlineAsset)
160
- break
161
- case 3:
162
- asset = trustlineEntryXdr.asset().liquidityPoolId().toString('hex')
163
- //data.liquidityPoolUseCount = trustlineEntryXdr.liquidityPoolUseCount()
164
- break
165
- default:
166
- throw new TxMetaEffectParserError(`Unsupported trustline type ` + trustlineType)
167
- }
168
- const data = {
169
- entry: 'trustline',
170
- account: xdrParseAccountAddress(trustlineEntryXdr.accountId()),
171
- asset,
172
- balance: trustlineEntryXdr.balance().toString(),
173
- limit: trustlineEntryXdr.limit().toString(),
174
- flags: trustlineEntryXdr.flags()
175
- }
176
-
177
- /*
178
- //ignored
179
- const extV1 = trustlineEntryXdr.ext()?.v1()
180
- if (extV1) {
181
- const liabilities = extV1.liabilities()
182
- data.buying_liabilities = liabilities.buying().toString()
183
- data.selling_liabilities = liabilities.selling().toString()
184
- }*/
185
-
186
- return data
187
- }
188
-
189
- function parseDataEntry(value) {
190
- const dataEntryXdr = value.value()
191
- return {
192
- entry: 'data',
193
- account: xdrParseAccountAddress(dataEntryXdr.accountId()),
194
- name: dataEntryXdr.dataName().toString(),
195
- value: dataEntryXdr.dataValue().toString('base64')
196
- }
197
- }
198
-
199
- function parseLiquidityPoolEntry(value) {
200
- const liquidityPoolEntryXdr = value.value()
201
- const body = liquidityPoolEntryXdr.body().value()
202
- const params = body.params()
203
- return {
204
- entry: 'liquidityPool',
205
- pool: liquidityPoolEntryXdr.liquidityPoolId().toString('hex'),
206
- asset: [xdrParseAsset(params.assetA()), xdrParseAsset(params.assetB())],
207
- fee: params.fee(),
208
- amount: [body.reserveA().toString(), body.reserveB().toString()],
209
- shares: body.totalPoolShares().toString(),
210
- accounts: body.poolSharesTrustLineCount().low
211
- }
212
- }
213
-
214
- function parseOfferEntry(value) {
215
- const offerEntryXdr = value.value()
216
- const data = {
217
- entry: 'offer',
218
- id: offerEntryXdr.offerId().toString(),
219
- account: xdrParseAccountAddress(offerEntryXdr.sellerId()),
220
- asset: [xdrParseAsset(offerEntryXdr.selling()), xdrParseAsset(offerEntryXdr.buying())],
221
- amount: offerEntryXdr.amount().toString(),
222
- price: xdrParsePrice(offerEntryXdr.price()),
223
- flags: offerEntryXdr.flags()
224
- }
225
- return data
226
- }
227
-
228
- function parseClaimableBalanceEntry(value) {
229
- const claimableBalanceXdr = value.value()
230
- const data = {
231
- balanceId: Buffer.from(claimableBalanceXdr.balanceId().value()).toString('hex'),
232
- entry: 'claimableBalance',
233
- asset: xdrParseAsset(claimableBalanceXdr.asset()),
234
- amount: claimableBalanceXdr.amount().toString(),
235
- claimants: claimableBalanceXdr.claimants().map(claimant => xdrParseClaimant(claimant))
236
- }
237
- const extV1 = claimableBalanceXdr.ext()?.v1()
238
- if (extV1) {
239
- data.flags = extV1.flags()
240
- }
241
- return data
242
- }
243
-
244
- function parseContractData(value) {
245
- const data = value.value()
246
- const owner = parseStateOwnerDataAddress(data.contract())
247
-
248
- const valueAttr = data.val()
249
- const entry = {
250
- entry: 'contractData',
251
- owner,
252
- key: data.key().toXDR('base64'),
253
- value: valueAttr.toXDR('base64'),
254
- durability: data.durability().name,
255
- keyHash: generateContractStateEntryHash(data)
256
- }
257
- if (data.key().switch()?.name === 'scvLedgerKeyContractInstance' && entry.durability === 'persistent') {
258
- entry.durability = 'instance'
259
- const instance = valueAttr.instance()._attributes
260
- const type = instance.executable._switch.name
261
- switch (type) {
262
- case 'contractExecutableStellarAsset':
263
- entry.kind = 'fromAsset'
264
- if (instance.storage?.length) { //if not -- the asset has been created "fromAddress" - no metadata in this case
265
- const metaArgs = instance.storage[0]._attributes
266
- if (metaArgs.key._value.toString() !== 'METADATA')
267
- throw new TxMetaEffectParserError('Unexpected asset initialization metadata')
268
- entry.asset = xdrParseAsset(metaArgs.val._value[1]._attributes.val._value.toString())
269
- }
270
- break
271
- case 'contractExecutableWasm':
272
- entry.kind = 'wasm'
273
- entry.wasmHash = instance.executable.wasmHash().toString('hex')
274
- break
275
- default:
276
- throw new TxMetaEffectParserError('Unsupported executable type: ' + type)
277
- }
278
- if (instance.storage?.length) {
279
- entry.storage = instance.storage.map(entry => ({
280
- key: entry.key().toXDR('base64'),
281
- val: entry.val().toXDR('base64')
282
- }))
283
- }
284
- }
285
- return entry
286
- }
287
-
288
- function parseTtl(data) {
289
- const attrs = data._value._attributes
290
- return {
291
- entry: 'ttl',
292
- keyHash: attrs.keyHash.toString('hex'),
293
- ttl: attrs.liveUntilLedgerSeq
294
- }
295
- }
296
-
297
- function parseStateOwnerDataAddress(contract) {
298
- if (contract.switch().name === 'scAddressTypeContract')
299
- return StrKey.encodeContract(contract.contractId())
300
- return xdrParseAccountAddress(contract.accountId())
301
- }
302
-
303
- function parseContractCode(value) {
304
- const contract = value.value()
305
- const hash = contract.hash()
306
- return {
307
- entry: 'contractCode',
308
- hash: hash.toString('hex'),
309
- keyHash: generateContractCodeEntryHash(hash)
310
- }
311
- }
312
-
1
+ const {StrKey} = require('@stellar/stellar-base')
2
+ const {TxMetaEffectParserError, UnexpectedTxMetaChangeError} = require('../errors')
3
+ const {
4
+ xdrParseAsset,
5
+ xdrParseAccountAddress,
6
+ xdrParseClaimant,
7
+ xdrParsePrice,
8
+ xdrParseSignerKey
9
+ } = require('./tx-xdr-parser-utils')
10
+ const {generateContractStateEntryHash, generateContractCodeEntryHash} = require('./ledger-key')
11
+
12
+ /**
13
+ * @typedef {{}} ParsedLedgerEntryMeta
14
+ * @property {'account'|'trustline'|'offer'|'data'|'liquidityPool'|'claimableBalance'|'contractData'|'contractCode'|'ttl'} type - Ledger entry type
15
+ * @property {'created'|'updated'|'removed'|'restored'} action - Ledger modification action
16
+ * @property {{}} before - Ledger entry state before changes applied
17
+ * @property {{}} after - Ledger entry state after changes application
18
+ */
19
+
20
+ /**
21
+ * @param {LedgerEntryChange[]} ledgerEntryChanges
22
+ * @param {Set<string>} [filter]
23
+ * @return {ParsedLedgerEntryMeta[]}
24
+ */
25
+ function parseLedgerEntryChanges(ledgerEntryChanges, filter = undefined) {
26
+ const changes = []
27
+ let state
28
+ let containsTtl = false
29
+ for (let i = 0; i < ledgerEntryChanges.length; i++) {
30
+ const entry = ledgerEntryChanges[i]
31
+ const type = entry._value._arm || entry._value._attributes.data._arm
32
+ if (filter && !filter.has(type)) //skip filtered ledger entry types
33
+ continue
34
+ const action = entry._arm
35
+ const stateData = parseEntry(entry, action)
36
+ if (stateData === undefined)
37
+ continue
38
+ const change = {action, type}
39
+ switch (action) {
40
+ case 'state':
41
+ state = stateData
42
+ continue
43
+ case 'created':
44
+ if (type === 'contractCode')
45
+ continue //processed in operation handler
46
+ change.before = null
47
+ change.after = stateData
48
+ change.type = stateData.entry
49
+ break
50
+ case 'updated':
51
+ if (type === 'contractCode')
52
+ console.warn(new UnexpectedTxMetaChangeError({type, action})) //happens only in protocol 20
53
+ change.before = state
54
+ change.after = stateData
55
+ change.type = stateData.entry
56
+ break
57
+ case 'restored':
58
+ change.before = stateData
59
+ change.after = stateData
60
+ change.type = stateData.entry
61
+ state = change.after
62
+ break
63
+ case 'removed':
64
+ if (!state && type === 'ttl')
65
+ continue //skip expiration processing for now
66
+ change.before = state
67
+ change.after = null
68
+ change.type = state.entry
69
+ break
70
+ default:
71
+ throw new TxMetaEffectParserError(`Unknown change entry type: ${action}`)
72
+ }
73
+ if (change.type === 'ttl') {
74
+ containsTtl = true
75
+ }
76
+ changes.push(change)
77
+ }
78
+ if (containsTtl) { //put ttl entries into the end of array
79
+ changes.sort((a, b) =>
80
+ a.type !== 'ttl' && b.type === 'ttl' ?
81
+ -1 : 0)
82
+ }
83
+ return changes
84
+ }
85
+
86
+ function parseEntry(entry, actionType) {
87
+ if (actionType === 'removed')
88
+ return null
89
+ const value = entry.value()
90
+ const parsed = parseEntryData(value.data())
91
+ if (parsed === null)
92
+ return null
93
+ //parsed.modified = entry.lastModifiedLedgerSeq()
94
+ return parseLedgerEntryExt(parsed, value)
95
+ }
96
+
97
+ function parseEntryData(data) {
98
+ const updatedEntryType = data.arm()
99
+ switch (updatedEntryType) {
100
+ case 'account':
101
+ return parseAccountEntry(data)
102
+ case 'trustline':
103
+ case 'trustLine':
104
+ return parseTrustlineEntry(data)
105
+ case 'offer':
106
+ return parseOfferEntry(data)
107
+ case 'data':
108
+ case 'datum':
109
+ return parseDataEntry(data)
110
+ case 'claimableBalance':
111
+ return parseClaimableBalanceEntry(data)
112
+ case 'liquidityPool':
113
+ return parseLiquidityPoolEntry(data)
114
+ case 'contractData':
115
+ return parseContractData(data)
116
+ case 'contractCode':
117
+ return parseContractCode(data)
118
+ case 'ttl':
119
+ return parseTtl(data)
120
+ default:
121
+ throw new TxMetaEffectParserError(`Unknown meta entry type: ${updatedEntryType}`)
122
+ }
123
+ }
124
+
125
+ function parseLedgerEntryExt(data, entry) {
126
+ const v1 = entry.ext()?.v1()
127
+ if (v1) {
128
+ const sponsor = v1.sponsoringId()
129
+ if (sponsor) {
130
+ data.sponsor = xdrParseAccountAddress(sponsor)
131
+ }
132
+ }
133
+ return data
134
+ }
135
+
136
+ function parseAccountEntry(value) {
137
+ const accountEntryXdr = value.value()
138
+ const data = {
139
+ entry: 'account',
140
+ address: xdrParseAccountAddress(accountEntryXdr.accountId()),
141
+ sequence: accountEntryXdr.seqNum().toString(),
142
+ balance: accountEntryXdr.balance().toString(),
143
+ homeDomain: accountEntryXdr.homeDomain().toString('UTF8'),
144
+ inflationDest: xdrParseAccountAddress(accountEntryXdr.inflationDest()),
145
+ flags: accountEntryXdr.flags(),
146
+ signers: accountEntryXdr.signers().map(signer => ({
147
+ key: xdrParseSignerKey(signer.key()),
148
+ weight: signer.weight()
149
+ }))
150
+ }
151
+ const thresholds = accountEntryXdr.thresholds()
152
+ data.thresholds = thresholds.slice(1).join()
153
+ data.masterWeight = thresholds[0]
154
+ const extV1 = accountEntryXdr.ext()?.v1()
155
+ if (extV1) {
156
+ const extV2 = extV1.ext()?.v2()
157
+ if (extV2) {
158
+ const sponsoringIDs = extV2.signerSponsoringIDs()
159
+ if (sponsoringIDs.length > 0) {
160
+ for (let i = 0; i < data.signers.length; i++) {
161
+ const sponsor = sponsoringIDs[i]
162
+ if (sponsor) { //attach sponsors directly to the signers
163
+ data.signers[i].sponsor = xdrParseAccountAddress(sponsor)
164
+ }
165
+ }
166
+ }
167
+ }
168
+ }
169
+ //ignored fields: numSubEntries, extV1.liabilities, extV2.numSponsored, extV2.numSponsoring, extV3.seqLedger, extv3.seqTime
170
+ return data
171
+ }
172
+
173
+ function parseTrustlineEntry(value) {
174
+ const trustlineEntryXdr = value.value()
175
+ const trustlineAsset = trustlineEntryXdr.asset()
176
+ const trustlineType = trustlineAsset.switch()
177
+ let asset
178
+ switch (trustlineType.value) {
179
+ case 0:
180
+ case 1:
181
+ case 2:
182
+ asset = xdrParseAsset(trustlineAsset)
183
+ break
184
+ case 3:
185
+ asset = StrKey.encodeLiquidityPool(trustlineEntryXdr.asset().liquidityPoolId())
186
+ break
187
+ default:
188
+ throw new TxMetaEffectParserError(`Unsupported trustline type ` + trustlineType)
189
+ }
190
+ const data = {
191
+ entry: 'trustline',
192
+ account: xdrParseAccountAddress(trustlineEntryXdr.accountId()),
193
+ asset,
194
+ balance: trustlineEntryXdr.balance().toString(),
195
+ limit: trustlineEntryXdr.limit().toString(),
196
+ flags: trustlineEntryXdr.flags()
197
+ }
198
+
199
+ /*
200
+ //ignored
201
+ const extV1 = trustlineEntryXdr.ext()?.v1()
202
+ if (extV1) {
203
+ const liabilities = extV1.liabilities()
204
+ data.buying_liabilities = liabilities.buying().toString()
205
+ data.selling_liabilities = liabilities.selling().toString()
206
+ }*/
207
+
208
+ return data
209
+ }
210
+
211
+ function parseDataEntry(value) {
212
+ const dataEntryXdr = value.value()
213
+ return {
214
+ entry: 'data',
215
+ account: xdrParseAccountAddress(dataEntryXdr.accountId()),
216
+ name: dataEntryXdr.dataName().toString(),
217
+ value: dataEntryXdr.dataValue().toString('base64')
218
+ }
219
+ }
220
+
221
+ function parseLiquidityPoolEntry(value) {
222
+ const liquidityPoolEntryXdr = value.value()
223
+ const body = liquidityPoolEntryXdr.body().value()
224
+ const params = body.params()
225
+ return {
226
+ entry: 'liquidityPool',
227
+ pool: StrKey.encodeLiquidityPool(liquidityPoolEntryXdr.liquidityPoolId()),
228
+ asset: [xdrParseAsset(params.assetA()), xdrParseAsset(params.assetB())],
229
+ fee: params.fee(),
230
+ amount: [body.reserveA().toString(), body.reserveB().toString()],
231
+ shares: body.totalPoolShares().toString(),
232
+ accounts: body.poolSharesTrustLineCount().low
233
+ }
234
+ }
235
+
236
+ function parseOfferEntry(value) {
237
+ const offerEntryXdr = value.value()
238
+ const rprice = offerEntryXdr.price()
239
+ const data = {
240
+ entry: 'offer',
241
+ id: offerEntryXdr.offerId().toString(),
242
+ account: xdrParseAccountAddress(offerEntryXdr.sellerId()),
243
+ asset: [xdrParseAsset(offerEntryXdr.selling()), xdrParseAsset(offerEntryXdr.buying())],
244
+ amount: offerEntryXdr.amount().toString(),
245
+ price: xdrParsePrice(rprice),
246
+ rprice: rprice._attributes,
247
+ flags: offerEntryXdr.flags()
248
+ }
249
+ return data
250
+ }
251
+
252
+ function parseClaimableBalanceEntry(value) {
253
+ const claimableBalanceXdr = value.value()
254
+ const data = {
255
+ balanceId: StrKey.encodeClaimableBalance(claimableBalanceXdr.balanceId().value()),
256
+ entry: 'claimableBalance',
257
+ asset: xdrParseAsset(claimableBalanceXdr.asset()),
258
+ amount: claimableBalanceXdr.amount().toString(),
259
+ claimants: claimableBalanceXdr.claimants().map(claimant => xdrParseClaimant(claimant))
260
+ }
261
+ const extV1 = claimableBalanceXdr.ext()?.v1()
262
+ if (extV1) {
263
+ data.flags = extV1.flags()
264
+ }
265
+ return data
266
+ }
267
+
268
+ function parseContractData(value) {
269
+ const data = value.value()
270
+ const owner = parseStateOwnerDataAddress(data.contract())
271
+
272
+ const valueAttr = data.val()
273
+ const entry = {
274
+ entry: 'contractData',
275
+ owner,
276
+ key: data.key().toXDR('base64'),
277
+ value: valueAttr.toXDR('base64'),
278
+ durability: data.durability().name,
279
+ keyHash: generateContractStateEntryHash(data)
280
+ }
281
+ if (data.key().switch()?.name === 'scvLedgerKeyContractInstance' && entry.durability === 'persistent') {
282
+ entry.durability = 'instance'
283
+ const instance = valueAttr.instance()._attributes
284
+ const type = instance.executable._switch.name
285
+ switch (type) {
286
+ case 'contractExecutableStellarAsset':
287
+ entry.kind = 'fromAsset'
288
+ if (instance.storage?.length) { //if not -- the asset has been created "fromAddress" - no metadata in this case
289
+ const metaArgs = instance.storage[0]._attributes
290
+ if (metaArgs.key._value.toString() !== 'METADATA')
291
+ throw new TxMetaEffectParserError('Unexpected asset initialization metadata')
292
+ entry.asset = xdrParseAsset(metaArgs.val._value[1]._attributes.val._value.toString())
293
+ }
294
+ break
295
+ case 'contractExecutableWasm':
296
+ entry.kind = 'wasm'
297
+ entry.wasmHash = instance.executable.wasmHash().toString('hex')
298
+ break
299
+ default:
300
+ throw new TxMetaEffectParserError('Unsupported executable type: ' + type)
301
+ }
302
+ if (instance.storage?.length) {
303
+ entry.storage = instance.storage.map(entry => ({
304
+ key: entry.key().toXDR('base64'),
305
+ val: entry.val().toXDR('base64')
306
+ }))
307
+ }
308
+ }
309
+ return entry
310
+ }
311
+
312
+ function parseTtl(data) {
313
+ const attrs = data._value._attributes
314
+ return {
315
+ entry: 'ttl',
316
+ keyHash: attrs.keyHash.toString('hex'),
317
+ ttl: attrs.liveUntilLedgerSeq
318
+ }
319
+ }
320
+
321
+ function parseStateOwnerDataAddress(contract) {
322
+ if (contract.switch().name === 'scAddressTypeContract')
323
+ return StrKey.encodeContract(contract.contractId())
324
+ return xdrParseAccountAddress(contract.accountId())
325
+ }
326
+
327
+ function parseContractCode(value) {
328
+ const contract = value.value()
329
+ const hash = contract.hash()
330
+ return {
331
+ entry: 'contractCode',
332
+ hash: hash.toString('hex'),
333
+ keyHash: generateContractCodeEntryHash(hash)
334
+ }
335
+ }
336
+
313
337
  module.exports = {parseLedgerEntryChanges}