@lukso/transaction-decoder 1.3.6-dev.ea75859 → 1.3.7-dev.2df3eae

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 (46) hide show
  1. package/dist/browser.cjs +214 -24
  2. package/dist/browser.cjs.map +1 -1
  3. package/dist/browser.d.cts +2 -2
  4. package/dist/browser.d.ts +2 -2
  5. package/dist/browser.js +4 -4
  6. package/dist/cdn/transaction-decoder.global.js +75 -75
  7. package/dist/cdn/transaction-decoder.global.js.map +1 -1
  8. package/dist/{chunk-NDBDNXBI.js → chunk-C3O7HMFS.js} +2 -2
  9. package/dist/{chunk-FKBKAWB3.js → chunk-JP3VO7OF.js} +2 -2
  10. package/dist/{chunk-SSY7TTU2.js → chunk-LZFM5SNN.js} +4 -4
  11. package/dist/{chunk-T4H2HHIB.js → chunk-XE5YIF5G.js} +209 -19
  12. package/dist/chunk-XE5YIF5G.js.map +1 -0
  13. package/dist/data.cjs +208 -18
  14. package/dist/data.cjs.map +1 -1
  15. package/dist/data.d.cts +3 -3
  16. package/dist/data.d.ts +3 -3
  17. package/dist/data.js +2 -2
  18. package/dist/{index-BzXh7poJ.d.ts → index-BWD9DaG6.d.cts} +6 -1
  19. package/dist/{index-BzXh7poJ.d.cts → index-BWD9DaG6.d.ts} +6 -1
  20. package/dist/index.cjs +214 -24
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +3 -3
  23. package/dist/index.d.ts +3 -3
  24. package/dist/index.js +4 -4
  25. package/dist/server.cjs +1499 -1258
  26. package/dist/server.cjs.map +1 -1
  27. package/dist/server.d.cts +17 -2
  28. package/dist/server.d.ts +17 -2
  29. package/dist/server.js +56 -2
  30. package/dist/server.js.map +1 -1
  31. package/dist/{utils-De_c6fUK.d.ts → utils-4Qdavg8r.d.ts} +2 -2
  32. package/dist/{utils-BEpSreRR.d.cts → utils-OR-XzeYG.d.cts} +2 -2
  33. package/package.json +8 -8
  34. package/src/decoder/lsp7Mint.test.ts +18 -11
  35. package/src/decoder/plugins/enhanceLSP0ERC725Account.ts +39 -2
  36. package/src/decoder/plugins/universalReceiverTypeIds.ts +277 -0
  37. package/src/decoder/types.ts +2 -0
  38. package/src/server/decodeEventLogs.ts +71 -0
  39. package/src/server/index.ts +10 -0
  40. package/src/server/types.ts +14 -1
  41. package/src/server.ts +1 -0
  42. package/src/types/index.ts +5 -1
  43. package/dist/chunk-T4H2HHIB.js.map +0 -1
  44. /package/dist/{chunk-NDBDNXBI.js.map → chunk-C3O7HMFS.js.map} +0 -0
  45. /package/dist/{chunk-FKBKAWB3.js.map → chunk-JP3VO7OF.js.map} +0 -0
  46. /package/dist/{chunk-SSY7TTU2.js.map → chunk-LZFM5SNN.js.map} +0 -0
@@ -0,0 +1,277 @@
1
+ import type { AbiParameter, Address, Hex } from 'viem'
2
+ import {
3
+ decodeAbiParameters,
4
+ encodeAbiParameters,
5
+ getAddress,
6
+ keccak256,
7
+ slice,
8
+ toHex,
9
+ } from 'viem'
10
+ import type { ArrayArgs } from '../types'
11
+ import { createNamedArgs } from '../utils'
12
+
13
+ /**
14
+ * Result of unwrapping a UniversalReceiver event based on its typeId.
15
+ * Contains the inner event that was wrapped by the UniversalReceiver notification,
16
+ * including rewritten topics and data fields matching the inner event's ABI.
17
+ */
18
+ export type UnwrapResult = {
19
+ eventName: string
20
+ args: ArrayArgs
21
+ standard?: string
22
+ topics: [Hex, ...Hex[]]
23
+ data: Hex
24
+ }
25
+
26
+ type UnwrapFn = (
27
+ receivedData: Hex,
28
+ from: Address,
29
+ profileAddress: Address,
30
+ eventName: string
31
+ ) => UnwrapResult | undefined
32
+
33
+ /**
34
+ * Compute topic0 (event signature hash) and ABI-encode the data field
35
+ * for an event with all non-indexed parameters.
36
+ */
37
+ function encodeEventLog(
38
+ eventName: string,
39
+ params: AbiParameter[],
40
+ values: unknown[]
41
+ ): { topics: [Hex, ...Hex[]]; data: Hex } {
42
+ const sig = `${eventName}(${params.map((p) => p.type).join(',')})`
43
+ const topic0 = keccak256(toHex(sig))
44
+ const data = encodeAbiParameters(params, values)
45
+ return { topics: [topic0], data }
46
+ }
47
+
48
+ // --- LSP26 FollowerSystem ---
49
+
50
+ // keccak256('LSP26FollowerSystem_FollowNotification')
51
+ const TYPEID_LSP26_FOLLOW: Hex =
52
+ '0x71e02f9f05bcd5816ec4f3134aa2e5a916669537ec6c77fe66ea595fabc2d51a'
53
+
54
+ // keccak256('LSP26FollowerSystem_UnfollowNotification')
55
+ const TYPEID_LSP26_UNFOLLOW: Hex =
56
+ '0x9d3c0b4012b69658977b099bdaa51eff0f0460f421fba96d15669506c00d1c4f'
57
+
58
+ // --- LSP7 DigitalAsset ---
59
+
60
+ // keccak256('LSP7Tokens_SenderNotification')
61
+ const TYPEID_LSP7_TOKENSSENDER: Hex =
62
+ '0x429ac7a06903dbc9c13dfcb3c9d11df8194581fa047c96d7a4171fc7402958ea'
63
+
64
+ // keccak256('LSP7Tokens_RecipientNotification')
65
+ const TYPEID_LSP7_TOKENSRECIPIENT: Hex =
66
+ '0x20804611b3e2ea21c480dc465142210acf4a2485947541770ec1fb87dee4a55c'
67
+
68
+ // --- LSP8 IdentifiableDigitalAsset ---
69
+
70
+ // keccak256('LSP8Tokens_SenderNotification')
71
+ const TYPEID_LSP8_TOKENSSENDER: Hex =
72
+ '0xb23eae7e6d1564b295b4c3e3be402d9a2f0776c57bdf365903496f6fa481ab00'
73
+
74
+ // keccak256('LSP8Tokens_RecipientNotification')
75
+ const TYPEID_LSP8_TOKENSRECIPIENT: Hex =
76
+ '0x0b084a55ebf70fd3c06fd755269dac2212c4d3f0f4d09079780bfa50c1b2984d'
77
+
78
+ // --- Event parameter definitions ---
79
+
80
+ const followParams: AbiParameter[] = [
81
+ { name: 'follower', type: 'address' },
82
+ { name: 'addr', type: 'address' },
83
+ ]
84
+
85
+ const unfollowParams: AbiParameter[] = [
86
+ { name: 'unfollower', type: 'address' },
87
+ { name: 'addr', type: 'address' },
88
+ ]
89
+
90
+ const lsp7TransferParams: AbiParameter[] = [
91
+ { name: 'operator', type: 'address' },
92
+ { name: 'from', type: 'address' },
93
+ { name: 'to', type: 'address' },
94
+ { name: 'amount', type: 'uint256' },
95
+ { name: 'force', type: 'bool' },
96
+ { name: 'data', type: 'bytes' },
97
+ ]
98
+
99
+ const lsp8TransferParams: AbiParameter[] = [
100
+ { name: 'operator', type: 'address' },
101
+ { name: 'from', type: 'address' },
102
+ { name: 'to', type: 'address' },
103
+ { name: 'tokenId', type: 'bytes32' },
104
+ { name: 'force', type: 'bool' },
105
+ { name: 'data', type: 'bytes' },
106
+ ]
107
+
108
+ const notificationParams: AbiParameter[] = [
109
+ { name: 'from', type: 'address' },
110
+ { name: 'typeId', type: 'bytes32' },
111
+ { name: 'data', type: 'bytes' },
112
+ ]
113
+
114
+ /**
115
+ * Decode LSP26 Follow notification.
116
+ * receivedData is abi.encodePacked(followerAddress) — raw 20 bytes.
117
+ */
118
+ function unwrapLSP26Follow(
119
+ receivedData: Hex,
120
+ _from: Address,
121
+ profileAddress: Address,
122
+ _eventName: string
123
+ ): UnwrapResult | undefined {
124
+ if (!receivedData || receivedData.length < 42) {
125
+ return undefined
126
+ }
127
+ const followerAddress = getAddress(slice(receivedData, 0, 20))
128
+ const values = [followerAddress, profileAddress]
129
+ return {
130
+ eventName: 'URDFollow',
131
+ ...createNamedArgs(values, followParams),
132
+ ...encodeEventLog('URDFollow', followParams, values),
133
+ standard: 'LSP26FollowerSystem',
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Decode LSP26 Unfollow notification.
139
+ * receivedData is abi.encodePacked(unfollowerAddress) — raw 20 bytes.
140
+ */
141
+ function unwrapLSP26Unfollow(
142
+ receivedData: Hex,
143
+ _from: Address,
144
+ profileAddress: Address,
145
+ _eventName: string
146
+ ): UnwrapResult | undefined {
147
+ if (!receivedData || receivedData.length < 42) {
148
+ return undefined
149
+ }
150
+ const unfollowerAddress = getAddress(slice(receivedData, 0, 20))
151
+ const values = [unfollowerAddress, profileAddress]
152
+ return {
153
+ eventName: 'URDUnfollow',
154
+ ...createNamedArgs(values, unfollowParams),
155
+ ...encodeEventLog('URDUnfollow', unfollowParams, values),
156
+ standard: 'LSP26FollowerSystem',
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Decode LSP7 token transfer notification.
162
+ * receivedData is abi.encode(operator, from, to, amount, data).
163
+ */
164
+ function unwrapLSP7Transfer(
165
+ receivedData: Hex,
166
+ _from: Address,
167
+ _profileAddress: Address,
168
+ eventName: string
169
+ ): UnwrapResult | undefined {
170
+ try {
171
+ const [operator, sender, recipient, amount, data] = decodeAbiParameters(
172
+ [
173
+ { name: 'operator', type: 'address' },
174
+ { name: 'from', type: 'address' },
175
+ { name: 'to', type: 'address' },
176
+ { name: 'amount', type: 'uint256' },
177
+ { name: 'data', type: 'bytes' },
178
+ ],
179
+ receivedData
180
+ )
181
+ const values = [operator, sender, recipient, amount, true, data]
182
+ return {
183
+ eventName,
184
+ ...createNamedArgs(values, lsp7TransferParams),
185
+ ...encodeEventLog(eventName, lsp7TransferParams, values),
186
+ standard: 'LSP7DigitalAsset',
187
+ }
188
+ } catch {
189
+ return undefined
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Decode LSP8 token transfer notification.
195
+ * receivedData is abi.encode(operator, from, to, tokenId, data).
196
+ */
197
+ function unwrapLSP8Transfer(
198
+ receivedData: Hex,
199
+ _from: Address,
200
+ _profileAddress: Address,
201
+ eventName: string
202
+ ): UnwrapResult | undefined {
203
+ try {
204
+ const [operator, sender, recipient, tokenId, data] = decodeAbiParameters(
205
+ [
206
+ { name: 'operator', type: 'address' },
207
+ { name: 'from', type: 'address' },
208
+ { name: 'to', type: 'address' },
209
+ { name: 'tokenId', type: 'bytes32' },
210
+ { name: 'data', type: 'bytes' },
211
+ ],
212
+ receivedData
213
+ )
214
+ const values = [operator, sender, recipient, tokenId, true, data]
215
+ return {
216
+ eventName,
217
+ ...createNamedArgs(values, lsp8TransferParams),
218
+ ...encodeEventLog(eventName, lsp8TransferParams, values),
219
+ standard: 'LSP8IdentifiableDigitalAsset',
220
+ }
221
+ } catch {
222
+ return undefined
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Registry mapping typeId → unwrap function.
228
+ */
229
+ const typeIdUnwrappers: Record<Hex, { fn: UnwrapFn; eventName: string }> = {
230
+ [TYPEID_LSP26_FOLLOW]: { fn: unwrapLSP26Follow, eventName: 'Follow' },
231
+ [TYPEID_LSP26_UNFOLLOW]: { fn: unwrapLSP26Unfollow, eventName: 'Unfollow' },
232
+ [TYPEID_LSP7_TOKENSSENDER]: {
233
+ fn: unwrapLSP7Transfer,
234
+ eventName: 'URDTokensSent',
235
+ },
236
+ [TYPEID_LSP7_TOKENSRECIPIENT]: {
237
+ fn: unwrapLSP7Transfer,
238
+ eventName: 'URDTokensReceived',
239
+ },
240
+ [TYPEID_LSP8_TOKENSSENDER]: {
241
+ fn: unwrapLSP8Transfer,
242
+ eventName: 'URDTokensSent',
243
+ },
244
+ [TYPEID_LSP8_TOKENSRECIPIENT]: {
245
+ fn: unwrapLSP8Transfer,
246
+ eventName: 'URDTokensReceived',
247
+ },
248
+ }
249
+
250
+ /**
251
+ * Unwrap a UniversalReceiver event based on its typeId.
252
+ * For known typeIds, decodes receivedData into specific event args.
253
+ * For unknown typeIds, returns a generic Notification with raw receivedData.
254
+ * Always rewrites topics and data to match the inner event's ABI.
255
+ */
256
+ export function unwrapUniversalReceiver(
257
+ typeId: Hex,
258
+ receivedData: Hex,
259
+ from: Address,
260
+ profileAddress: Address
261
+ ): UnwrapResult {
262
+ const entry = typeIdUnwrappers[typeId]
263
+ if (entry) {
264
+ const result = entry.fn(receivedData, from, profileAddress, entry.eventName)
265
+ if (result) {
266
+ return result
267
+ }
268
+ }
269
+
270
+ // Fallback: generic notification with raw data
271
+ const values = [from, typeId, receivedData]
272
+ return {
273
+ eventName: 'URDNotification',
274
+ ...createNamedArgs(values, notificationParams),
275
+ ...encodeEventLog('Notification', notificationParams, values),
276
+ }
277
+ }
@@ -461,5 +461,7 @@ export type DecodeEventResult =
461
461
  | ({
462
462
  eventName: string
463
463
  args: ArrayArgs
464
+ wrappers?: Array<{ eventName: string; args: ArrayArgs } & Log>
465
+ standard?: string
464
466
  } & Log)
465
467
  | undefined
@@ -0,0 +1,71 @@
1
+ import type { Hex } from 'viem'
2
+ import { decodeKeyValueRaw } from '../decoder/plugins'
3
+ import { pluginRegistry } from '../decoder/registry'
4
+ import type { DecodeEventResult, DecoderOptions } from '../decoder/types'
5
+ import type { ServerDecoderCaches } from './caches'
6
+ import type { ServerDecoderOptions } from './types'
7
+
8
+ /**
9
+ * Decode an array of raw log events using the registered decoder plugins.
10
+ *
11
+ * Uses the same plugin iteration pattern as transaction.ts:
12
+ * try each plugin's `decodeEvent`, first match wins, raw log kept as fallback.
13
+ *
14
+ * For DataChanged events, also decodes the ERC725Y key-value pair using
15
+ * the schema plugin system (same as setData transaction decoding).
16
+ */
17
+ export async function decodeEventLogs(
18
+ logs: DecodeEventResult[],
19
+ options: ServerDecoderOptions,
20
+ _caches: ServerDecoderCaches
21
+ ): Promise<DecodeEventResult[]> {
22
+ const { chain } = options
23
+ const plugins = await pluginRegistry.getAll({ syncOnly: true })
24
+ const schemaPlugins = pluginRegistry.getAllSchema()
25
+
26
+ const decoderOptions: DecoderOptions = {
27
+ chain,
28
+ plugins,
29
+ schemaPlugins,
30
+ wrappers: [],
31
+ async: false,
32
+ }
33
+
34
+ const results: DecodeEventResult[] = []
35
+
36
+ for (const log of logs) {
37
+ let decoded: DecodeEventResult
38
+ for (const plugin of plugins) {
39
+ decoded = await plugin.decodeEvent(log, decoderOptions)
40
+ if (decoded) break
41
+ }
42
+ const result = decoded ?? log
43
+
44
+ // For DataChanged events, decode the ERC725Y key-value pair
45
+ if (result?.eventName === 'DataChanged' && result.args) {
46
+ try {
47
+ const dataKey = result.args.find((a) => a.name === 'dataKey')?.value as
48
+ | Hex
49
+ | undefined
50
+ const dataValue = result.args.find((a) => a.name === 'dataValue')
51
+ ?.value as Hex | undefined
52
+ if (dataKey) {
53
+ const info = await decodeKeyValueRaw(
54
+ dataKey,
55
+ dataValue ?? '0x',
56
+ decoderOptions
57
+ )
58
+ if (info) {
59
+ ;(result as Record<string, unknown>).info = info
60
+ }
61
+ }
62
+ } catch {
63
+ // Schema decoding failed — keep event without info
64
+ }
65
+ }
66
+
67
+ results.push(result)
68
+ }
69
+
70
+ return results
71
+ }
@@ -1,5 +1,6 @@
1
1
  import { ServerAddressResolver } from './addressResolver'
2
2
  import { ServerDecoderCaches } from './caches'
3
+ import { decodeEventLogs } from './decodeEventLogs'
3
4
  import { decodeTransactionSync } from './decodeTransactionSync'
4
5
  import { decodeTransactionsBatch } from './decodeTransactionsBatch'
5
6
  import { finishDecoding } from './finishDecoding'
@@ -77,5 +78,14 @@ export function createServerDecoder(
77
78
  async resolveAddresses(addresses, timeoutMs = 3000) {
78
79
  return addressResolver.resolveAddressesWithTimeout(addresses, timeoutMs)
79
80
  },
81
+
82
+ async decodeEventLogs(logs, options) {
83
+ const opts: ServerDecoderOptions = {
84
+ chain: config.defaultChain,
85
+ timeoutMs: config.defaultTimeout,
86
+ ...options,
87
+ }
88
+ return decodeEventLogs(logs, opts, caches)
89
+ },
80
90
  }
81
91
  }
@@ -1,5 +1,10 @@
1
1
  import type { Chain } from 'viem'
2
- import type { DataKey, DecoderResult, EnhancedInfo } from '../types'
2
+ import type {
3
+ DataKey,
4
+ DecodeEventResult,
5
+ DecoderResult,
6
+ EnhancedInfo,
7
+ } from '../types'
3
8
 
4
9
  /**
5
10
  * Options for server-side decoding
@@ -165,4 +170,12 @@ export interface ServerDecoder {
165
170
  addresses: DataKey[],
166
171
  timeoutMs?: number
167
172
  ) => Promise<Map<DataKey, EnhancedInfo>>
173
+
174
+ /**
175
+ * Decode an array of raw log events using registered decoder plugins
176
+ */
177
+ decodeEventLogs: (
178
+ logs: DecodeEventResult[],
179
+ options?: Partial<ServerDecoderOptions>
180
+ ) => Promise<DecodeEventResult[]>
168
181
  }
package/src/server.ts CHANGED
@@ -10,6 +10,7 @@
10
10
 
11
11
  export { ServerAddressResolver } from './server/addressResolver'
12
12
  export { ServerDecoderCaches } from './server/caches'
13
+ export { decodeEventLogs } from './server/decodeEventLogs'
13
14
  export { decodeTransactionSync } from './server/decodeTransactionSync'
14
15
  export { decodeTransactionsBatch } from './server/decodeTransactionsBatch'
15
16
  export { createServerDecoder } from './server/index'
@@ -7,7 +7,11 @@ import type { Hex } from 'viem'
7
7
  import type { DecoderResult, EnhancedInfo } from '../decoder/types'
8
8
 
9
9
  // Re-export decoder types
10
- export type { DecoderResult, EnhancedInfo } from '../decoder/types'
10
+ export type {
11
+ DecodeEventResult,
12
+ DecoderResult,
13
+ EnhancedInfo,
14
+ } from '../decoder/types'
11
15
  export { AsyncOperations } from '../decoder/types'
12
16
 
13
17
  // Key is always a hex string - for tokens it's in the format "address:tokenId"