@lukso/transaction-decoder 1.0.1-dev.0f1bea5

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.
Files changed (110) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +486 -0
  3. package/dist/browser.cjs +6912 -0
  4. package/dist/browser.cjs.map +1 -0
  5. package/dist/browser.d.cts +6 -0
  6. package/dist/browser.d.ts +6 -0
  7. package/dist/browser.js +131 -0
  8. package/dist/browser.js.map +1 -0
  9. package/dist/cdn/transaction-decoder.global.js +296 -0
  10. package/dist/cdn/transaction-decoder.global.js.map +1 -0
  11. package/dist/chunk-GGBHTWJL.js +437 -0
  12. package/dist/chunk-GGBHTWJL.js.map +1 -0
  13. package/dist/chunk-GXZOF3QY.js +839 -0
  14. package/dist/chunk-GXZOF3QY.js.map +1 -0
  15. package/dist/chunk-LJ6ES5XF.js +776 -0
  16. package/dist/chunk-LJ6ES5XF.js.map +1 -0
  17. package/dist/chunk-XVHJWV5U.js +4925 -0
  18. package/dist/chunk-XVHJWV5U.js.map +1 -0
  19. package/dist/data.cjs +5518 -0
  20. package/dist/data.cjs.map +1 -0
  21. package/dist/data.d.cts +43 -0
  22. package/dist/data.d.ts +43 -0
  23. package/dist/data.js +55 -0
  24. package/dist/data.js.map +1 -0
  25. package/dist/index-BzXh7poJ.d.cts +524 -0
  26. package/dist/index-BzXh7poJ.d.ts +524 -0
  27. package/dist/index.cjs +6912 -0
  28. package/dist/index.cjs.map +1 -0
  29. package/dist/index.d.cts +756 -0
  30. package/dist/index.d.ts +756 -0
  31. package/dist/index.js +131 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/server.cjs +5644 -0
  34. package/dist/server.cjs.map +1 -0
  35. package/dist/server.d.cts +217 -0
  36. package/dist/server.d.ts +217 -0
  37. package/dist/server.js +644 -0
  38. package/dist/server.js.map +1 -0
  39. package/dist/utils-CBAkjQh3.d.cts +108 -0
  40. package/dist/utils-xT9-km0r.d.ts +108 -0
  41. package/package.json +101 -0
  42. package/src/browser.ts +13 -0
  43. package/src/client/resolveAddresses.ts +157 -0
  44. package/src/core/addressCollector.ts +153 -0
  45. package/src/core/addressResolver.ts +135 -0
  46. package/src/core/dataModel.ts +888 -0
  47. package/src/core/instance.ts +33 -0
  48. package/src/core/integrateDecoder.ts +325 -0
  49. package/src/data.ts +70 -0
  50. package/src/decoder/GENERATOR_PROPOSAL.md +182 -0
  51. package/src/decoder/THREE_PHASE_EXAMPLE.md +108 -0
  52. package/src/decoder/aggregation.ts +218 -0
  53. package/src/decoder/browserCache.ts +237 -0
  54. package/src/decoder/cache/README.md +126 -0
  55. package/src/decoder/cache/index.ts +44 -0
  56. package/src/decoder/cache.ts +139 -0
  57. package/src/decoder/constants.ts +125 -0
  58. package/src/decoder/decodeTransaction.ts +292 -0
  59. package/src/decoder/errors.ts +95 -0
  60. package/src/decoder/events.ts +192 -0
  61. package/src/decoder/functionSignature.ts +344 -0
  62. package/src/decoder/getDataFromExternalSources.ts +248 -0
  63. package/src/decoder/graphqlWS.ts +22 -0
  64. package/src/decoder/interfaces.ts +185 -0
  65. package/src/decoder/keyValue.ts +5 -0
  66. package/src/decoder/kvCache.ts +241 -0
  67. package/src/decoder/lruCache.ts +184 -0
  68. package/src/decoder/lsp7Mint.test.ts +179 -0
  69. package/src/decoder/lsp7TransferBatch.test.ts +105 -0
  70. package/src/decoder/plugins/RegistryAbi.ts +562 -0
  71. package/src/decoder/plugins/enhanceBurntPix.ts +132 -0
  72. package/src/decoder/plugins/enhanceGraffiti.ts +70 -0
  73. package/src/decoder/plugins/enhanceLSP0ERC725Account.ts +179 -0
  74. package/src/decoder/plugins/enhanceLSP26FollowerSystem.ts +88 -0
  75. package/src/decoder/plugins/enhanceLSP6KeyManager.ts +231 -0
  76. package/src/decoder/plugins/enhanceLSP7DigitalAsset.ts +165 -0
  77. package/src/decoder/plugins/enhanceLSP8IdentifiableDigitalAsset.ts +170 -0
  78. package/src/decoder/plugins/enhanceLSP9Vault.ts +57 -0
  79. package/src/decoder/plugins/enhanceRetrieveAbi.ts +85 -0
  80. package/src/decoder/plugins/enhanceSetData.ts +135 -0
  81. package/src/decoder/plugins/index.ts +99 -0
  82. package/src/decoder/plugins/schemaDefault.ts +318 -0
  83. package/src/decoder/plugins/standardPlugin.ts +202 -0
  84. package/src/decoder/registry.ts +322 -0
  85. package/src/decoder/singleGQL.ts +293 -0
  86. package/src/decoder/transaction.ts +198 -0
  87. package/src/decoder/types.ts +465 -0
  88. package/src/decoder/utils.ts +212 -0
  89. package/src/example/usage.ts +172 -0
  90. package/src/index.ts +174 -0
  91. package/src/server/addressResolver.ts +68 -0
  92. package/src/server/caches.ts +209 -0
  93. package/src/server/decodeTransactionSync.ts +156 -0
  94. package/src/server/decodeTransactionsBatch.ts +207 -0
  95. package/src/server/finishDecoding.ts +116 -0
  96. package/src/server/index.ts +81 -0
  97. package/src/server/lsp23Resolver.test.ts +46 -0
  98. package/src/server/lsp23Resolver.ts +419 -0
  99. package/src/server/types.ts +168 -0
  100. package/src/server.ts +22 -0
  101. package/src/shared/addressResolver.ts +651 -0
  102. package/src/shared/cache.ts +144 -0
  103. package/src/shared/constants.ts +21 -0
  104. package/src/stubs/tty.ts +13 -0
  105. package/src/stubs/util.ts +42 -0
  106. package/src/types/index.ts +154 -0
  107. package/src/types/provider.ts +46 -0
  108. package/src/umd.ts +13 -0
  109. package/src/utils/debug.ts +49 -0
  110. package/src/utils/json-bigint.ts +47 -0
@@ -0,0 +1,318 @@
1
+ import {
2
+ type DecodeDataOutput,
3
+ decodeData,
4
+ decodeMappingKey,
5
+ decodePermissions,
6
+ decodeValueContent,
7
+ type ERC725JSONSchema,
8
+ isDynamicKeyName,
9
+ type Permissions,
10
+ } from '@erc725/erc725.js'
11
+ import { type Hex, isHex } from 'viem'
12
+ import { ARRAY_MAX, defaultSchema, IPFS_GATEWAY } from '../constants'
13
+ import { getDataFromExternalSources } from '../getDataFromExternalSources'
14
+ import type { Info } from '../types'
15
+
16
+ export const schemaDefaultPlugin = decodeKeyValuePlugin(defaultSchema)
17
+ export default schemaDefaultPlugin
18
+
19
+ /**
20
+ * Decode key/value pair as per erc725 schema
21
+ * @param key
22
+ * @param value
23
+ * @returns
24
+ */
25
+ export function decodeKeyValuePlugin(
26
+ defaultSchema: ERC725JSONSchema[],
27
+ process?: (output: Info) => Promise<Info>
28
+ ) {
29
+ const preProcess = async (key: Hex, value: Hex): Promise<Info> => {
30
+ // Search for the schema entry.
31
+ const schema =
32
+ defaultSchema.find((item) => {
33
+ if (key === item.key) {
34
+ return true
35
+ }
36
+ const index = item.key.indexOf('<')
37
+ if (index !== -1) {
38
+ return key.slice(0, index) === item.key.slice(0, index)
39
+ }
40
+ return false
41
+ }) ||
42
+ defaultSchema.find((item) => {
43
+ if (key.slice(0, 34) === item.key.slice(0, 34)) {
44
+ return true
45
+ }
46
+ return false
47
+ })
48
+ if (schema) {
49
+ try {
50
+ // Get the raw name.
51
+ const name = schema.name.replace(/[:\[\]]|<.*?>/g, '')
52
+ if (schema.keyType === 'Array') {
53
+ if (schema.key === key) {
54
+ // Convert hex string to Uint8Array without using Buffer
55
+ const hexString = value.slice(2)
56
+ const all = new Uint8Array(hexString.length / 2)
57
+ for (let i = 0; i < hexString.length; i += 2) {
58
+ all[i / 2] = Number.parseInt(hexString.substring(i, i + 2), 16)
59
+ }
60
+ // Figure out the first and last byte of the value (this is to allow
61
+ // the older and newer ways to store the array length i.e. uint256, uint128 and so on.)
62
+ const [{ start, end } = { start: 0, end: 0 }] = all.reduce(
63
+ (
64
+ acc: Array<{ isZero: boolean; start: number; end: number }>,
65
+ byte,
66
+ index_
67
+ ) => {
68
+ let isZero = byte === 0 || index_ === 0
69
+ const last = acc.at(-1)
70
+ let index = index_
71
+ if (
72
+ !last ||
73
+ last.isZero !== isZero ||
74
+ index === all.length - 1
75
+ ) {
76
+ if (index === all.length - 1) {
77
+ index = all.length
78
+ } else if (index === 0) {
79
+ isZero = true
80
+ }
81
+ if (last) {
82
+ last.end = index
83
+ last.isZero = isZero
84
+ }
85
+ if (isZero) {
86
+ acc.push({
87
+ isZero,
88
+ start: index,
89
+ end: index === 0 ? index + 1 : index,
90
+ })
91
+ }
92
+ }
93
+ return acc
94
+ },
95
+ []
96
+ )
97
+ const picked =
98
+ Math.abs(end - all.length + 1) <= 4
99
+ ? all.length + 1
100
+ : Math.abs(end - all.length / 2 + 1) <= 4
101
+ ? all.length / 2
102
+ : end
103
+ const segment = value.slice(0, picked * 2 + 2)
104
+ const length = BigInt(segment)
105
+ if (length > ARRAY_MAX) {
106
+ // We don't support arrays larger than 1000. Inside of the indexer it's a problem because of a single array column
107
+ // and we had to pick some kind of maximum size to prevent the SQL from crashing.
108
+ return {
109
+ ...schema,
110
+ type: 'Error',
111
+ key,
112
+ value: 'Array length too large',
113
+ }
114
+ }
115
+ return {
116
+ ...schema,
117
+ type: 'ArrayLength',
118
+ pretty: name,
119
+ value: Number(length),
120
+ }
121
+ }
122
+ // Extract array index.
123
+ const index = BigInt(`0x${key.slice(34)}`)
124
+ if (index > ARRAY_MAX) {
125
+ return {
126
+ ...schema,
127
+ type: 'Error',
128
+ key,
129
+ value: 'Array index too large',
130
+ }
131
+ }
132
+ let data =
133
+ value !== '0x'
134
+ ? decodeValueContent(schema.valueContent, value)
135
+ : undefined
136
+ if (schema.valueContent.startsWith('0x')) {
137
+ data = data === schema.valueContent
138
+ }
139
+ return {
140
+ ...schema,
141
+ type: 'ArrayItem',
142
+ pretty: name,
143
+ dynamic: Number(index),
144
+ value: data,
145
+ }
146
+ }
147
+ // Decode dynamic parts.
148
+ let dynamicKeyParts: string[] | string | undefined = undefined
149
+ if (isDynamicKeyName(schema.name)) {
150
+ dynamicKeyParts = decodeMappingKey(key, schema).map(({ value }) =>
151
+ isHex(value) ? (value as string).toLowerCase() : `${value}`
152
+ )
153
+ }
154
+ // Decode value
155
+ let rawData =
156
+ value !== '0x'
157
+ ? (decodeData(
158
+ [
159
+ dynamicKeyParts
160
+ ? {
161
+ keyName: schema.name,
162
+ dynamicKeyParts,
163
+ value,
164
+ }
165
+ : { keyName: schema.name, value },
166
+ ],
167
+ defaultSchema
168
+ ) as unknown as Record<string, unknown>[])
169
+ : [{ value: undefined }]
170
+ type Params = Parameters<typeof getDataFromExternalSources>
171
+ let data = rawData?.[0]?.value
172
+ // Some special cases.
173
+ if (schema.name === 'LSP8TokenMetadataBaseURI') {
174
+ const { url: _url } = (data as { url: string | undefined }) || {}
175
+ const url = _url?.replace(/\0*$/g, '')
176
+ if (!url && url?.includes('\0')) {
177
+ return {
178
+ ...schema,
179
+ pretty: name,
180
+ type: 'Error',
181
+ key,
182
+ value: 'Invalid URL (contains \\0)',
183
+ }
184
+ }
185
+ return { ...schema, type: 'Value', name, value: url }
186
+ }
187
+ if (data && typeof data === 'object' && !Array.isArray(data)) {
188
+ const { url: _url } = (data as { url: string | undefined }) || {}
189
+ const url = _url?.replace(/\0*$/g, '')
190
+ if (!url || url?.includes('\0')) {
191
+ const newURL = url?.replace(/\0/g, '\\0')
192
+ const { error, data, method } = rawData?.[0] as {
193
+ error: string
194
+ data?: string
195
+ method?: string
196
+ url?: string
197
+ }
198
+ return {
199
+ ...schema,
200
+ pretty: name,
201
+ type: 'Error',
202
+ key,
203
+ value: `${error ? `${error} ` : ''}Invalid URL (contains \\0 ${newURL})`,
204
+ data,
205
+ method,
206
+ url: newURL,
207
+ }
208
+ }
209
+ const newURL = (data as { url: string }).url.replace(
210
+ 'https://2eff.lukso.dev/ipfs/',
211
+ 'ipfs://'
212
+ )
213
+ if (newURL !== (data as { url: string }).url) {
214
+ ;(data as { url: string }).url = newURL
215
+ }
216
+ rawData = (await getDataFromExternalSources(
217
+ defaultSchema,
218
+ rawData as unknown as DecodeDataOutput[],
219
+ IPFS_GATEWAY,
220
+ false
221
+ )) as unknown as Record<string, unknown>[]
222
+ if (rawData?.[0]?.error) {
223
+ const { error, data, method, url } = rawData?.[0] as {
224
+ error: string
225
+ data?: string
226
+ method?: string
227
+ url?: string
228
+ }
229
+ return {
230
+ ...schema,
231
+ type: 'Error',
232
+ pretty: name,
233
+ value: error,
234
+ data,
235
+ method,
236
+ url,
237
+ }
238
+ }
239
+ data =
240
+ (rawData?.[0]?.value as Record<string, unknown>)?.[schema.name] ||
241
+ (rawData?.[0]?.value as Record<string, unknown>)
242
+ } else if (schema.valueContent.startsWith('0x')) {
243
+ data = data === schema.valueContent
244
+ }
245
+ const {
246
+ data: _data,
247
+ method,
248
+ src,
249
+ url,
250
+ } = rawData?.[0] as {
251
+ data?: string
252
+ method?: string
253
+ url?: string
254
+ src?: string
255
+ }
256
+ if (schema.name === 'AddressPermissions:Permissions:<address>') {
257
+ let _value: string | object = data as string | object
258
+ try {
259
+ const {
260
+ ERC4337_PERMISSION: _ignore1,
261
+ ALL_PERMISSIONS: _ignore2,
262
+ ...rest
263
+ } = decodePermissions(data as Hex) as unknown as Permissions & {
264
+ ISRECOVERY?: boolean
265
+ }
266
+ if (Object.keys('rest').length === 1 && rest.ADDCONTROLLER) {
267
+ rest.ISRECOVERY = true
268
+ }
269
+ _value = rest
270
+ } catch {
271
+ // Ignore
272
+ }
273
+ return {
274
+ ...schema,
275
+ type: 'Value',
276
+ pretty: name,
277
+ value: _value,
278
+ dynamic:
279
+ dynamicKeyParts?.length === 1
280
+ ? dynamicKeyParts[0]
281
+ : dynamicKeyParts,
282
+ data: _data,
283
+ method,
284
+ src,
285
+ url,
286
+ }
287
+ }
288
+ return {
289
+ ...schema,
290
+ type: 'Value',
291
+ pretty: name,
292
+ value: data,
293
+ dynamic:
294
+ dynamicKeyParts?.length === 1
295
+ ? dynamicKeyParts[0]
296
+ : dynamicKeyParts,
297
+ data: _data,
298
+ method,
299
+ src,
300
+ url,
301
+ }
302
+ } catch (error) {
303
+ return { ...schema, type: 'Error', key, value: error as Error }
304
+ }
305
+ }
306
+ return { type: 'Undecoded', name: undefined, key, value }
307
+ }
308
+ return async (key: Hex, value: Hex) => {
309
+ try {
310
+ const result = await preProcess(key, value)
311
+ if (process) {
312
+ return await process(result)
313
+ }
314
+ return result
315
+ } catch {}
316
+ return undefined
317
+ }
318
+ }
@@ -0,0 +1,202 @@
1
+ import type { ERC725JSONSchema } from '@erc725/erc725.js'
2
+ import type { Abi, AbiParameter, Hex, Log, Transaction } from 'viem'
3
+ import { pluginRegistry } from '../registry'
4
+ import type {
5
+ Aggregation,
6
+ DecodeEventCallback,
7
+ DecodeEventResult,
8
+ DecoderOptions,
9
+ DecoderPlugin,
10
+ DecoderResult,
11
+ EnhancerAggregationKeyCallback,
12
+ EnhancerCallback,
13
+ EnhancerSummarizeCallback,
14
+ Info,
15
+ PluginOptions,
16
+ ResultType,
17
+ SchemaPlugin,
18
+ } from '../types'
19
+ import { customDecodeFunctionData } from '../utils'
20
+ import { decodeKeyValuePlugin } from './schemaDefault'
21
+
22
+ /**
23
+ * Helper to create aggregation keys with plugin prefix
24
+ */
25
+ export function createAggregationKey(
26
+ pluginOptions: PluginOptions,
27
+ key: string
28
+ ): string {
29
+ return `${pluginOptions.name || pluginOptions.decoderName || 'unknown'}:${key}`
30
+ }
31
+
32
+ /**
33
+ *
34
+ * @param abi abi to use for decoding
35
+ * @param pluginOptions options to name abi, decoder or module (optional since the enhancer can provide some or all)
36
+ * @param enhance callback to do the actual enhancement once the abi function was decoded.
37
+ * @returns enhanced transaction
38
+ */
39
+ export function standardPlugin<T extends Abi | readonly unknown[]>(
40
+ abi: T,
41
+ pluginOptions: PluginOptions,
42
+ callbacks: {
43
+ enhance: EnhancerCallback
44
+ decodeEvent: DecodeEventCallback
45
+ overrideEnhance?: EnhancerCallback
46
+ getAggregationKey?: EnhancerAggregationKeyCallback
47
+ summarize?: EnhancerSummarizeCallback
48
+ aggregations?: Aggregation[]
49
+ }
50
+ ): DecoderPlugin {
51
+ const {
52
+ abiName,
53
+ decoderName = 'unknown',
54
+ moduleHint,
55
+ required,
56
+ priority,
57
+ name,
58
+ usesAsync,
59
+ } = pluginOptions
60
+ const plugin: DecoderPlugin = {
61
+ enhance: async (transaction: DecoderResult, options: DecoderOptions) => {
62
+ if (callbacks.overrideEnhance) {
63
+ return await callbacks.overrideEnhance(
64
+ transaction,
65
+ pluginOptions,
66
+ options
67
+ )
68
+ }
69
+ const { preferError } = options
70
+ const {
71
+ to: _to,
72
+ from: _from,
73
+ value: _value,
74
+ input,
75
+ blockNumber: _blockNumber,
76
+ // logs,
77
+ } = transaction as Partial<Transaction> & {
78
+ data?: `0x${string}`
79
+ logs?: Log[]
80
+ }
81
+ const to = _to || undefined
82
+ const from = _from || undefined
83
+ const value = _value ? BigInt(_value) : 0n
84
+ const decoded = customDecodeFunctionData(
85
+ {
86
+ abi,
87
+ data: input || '0x',
88
+ },
89
+ preferError
90
+ )
91
+ if (!decoded) {
92
+ // Most of the time decoded will through an error,
93
+ // but if the result is undefined, then assume it also
94
+ // didn't work.
95
+ return
96
+ }
97
+ const { functionName, sig, args } = decoded as ResultType & {
98
+ sig?: `0x${string}`
99
+ functionName?: string
100
+ args: Array<AbiParameter & { value?: unknown }>
101
+ }
102
+ const output: DecoderResult = {
103
+ isDecoded: true,
104
+ ...transaction,
105
+ resultType: 'execute',
106
+ functionName,
107
+ sig,
108
+ input,
109
+ __decoder: decoderName,
110
+ standard: abiName || 'unknown',
111
+ moduleHint,
112
+ args,
113
+ to,
114
+ from,
115
+ value,
116
+ } as DecoderResult
117
+ const enhanced = await callbacks.enhance(output, pluginOptions, options)
118
+ if (enhanced) {
119
+ return enhanced
120
+ }
121
+ return // This enhancement was denied by the enhancer so we need to re-try.
122
+ },
123
+ decodeEvent: async (log: DecodeEventResult, options: DecoderOptions) => {
124
+ return callbacks.decodeEvent(log, options)
125
+ },
126
+ getAggregationKey: async (result, pluginOptions, options) => {
127
+ if (!callbacks.getAggregationKey) {
128
+ return
129
+ }
130
+ return callbacks.getAggregationKey(result, pluginOptions, options)
131
+ },
132
+ summarize: async (aggregationKey, result, pluginOptions, options) => {
133
+ if (!callbacks.summarize) {
134
+ return
135
+ }
136
+ return callbacks.summarize(aggregationKey, result, pluginOptions, options)
137
+ },
138
+ }
139
+
140
+ // Add optional properties to the plugin
141
+ if (name !== undefined) {
142
+ plugin.name = name
143
+ }
144
+ if (required !== undefined) {
145
+ plugin.required = required
146
+ }
147
+ if (priority !== undefined) {
148
+ plugin.priority = priority
149
+ }
150
+
151
+ if (usesAsync !== undefined) {
152
+ plugin.usesAsync = usesAsync
153
+ }
154
+
155
+ if (callbacks.aggregations !== undefined) {
156
+ plugin.aggregations = callbacks.aggregations
157
+ }
158
+
159
+ // Freeze the plugin to prevent modifications
160
+ const frozenPlugin = Object.freeze(plugin)
161
+
162
+ // Auto-register the plugin if it has a name
163
+ // Use direct import of registry to avoid circular dependency
164
+ if (name) {
165
+ pluginRegistry.register(name, frozenPlugin, {
166
+ priority,
167
+ required,
168
+ usesAsync, // Include async flag for registration
169
+ })
170
+ }
171
+
172
+ return frozenPlugin
173
+ }
174
+
175
+ /**
176
+ * Creates a standard schema plugin from ERC725 schemas
177
+ * @param schemas Array of ERC725JSONSchema objects
178
+ * @param pluginOptions Options including name, required, priority
179
+ * @param process Optional post-processing function
180
+ * @returns SchemaPlugin
181
+ */
182
+ export function standardSchemaPlugin(
183
+ schemas: ERC725JSONSchema[],
184
+ pluginOptions?: {
185
+ name?: string
186
+ required?: boolean
187
+ priority?: number
188
+ },
189
+ process?: (output: Info) => Promise<Info>
190
+ ): SchemaPlugin {
191
+ const basePlugin = decodeKeyValuePlugin(schemas, process)
192
+
193
+ // Create a proper SchemaPlugin by assigning properties to the function
194
+ const plugin = Object.assign(basePlugin, {
195
+ name: pluginOptions?.name,
196
+ required: pluginOptions?.required,
197
+ priority: pluginOptions?.priority ?? 50,
198
+ })
199
+
200
+ // Freeze the plugin to prevent modifications
201
+ return Object.freeze(plugin)
202
+ }