@stellar-expert/tx-meta-effects-parser 7.0.0-rc.2 → 7.0.0-rc.20

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