@lukso/transaction-decoder 1.3.6-dev.4a582bd → 1.3.6-dev.6843bdd

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 (40) hide show
  1. package/dist/browser.cjs +203 -13
  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 +70 -70
  7. package/dist/cdn/transaction-decoder.global.js.map +1 -1
  8. package/dist/{chunk-FKBKAWB3.js → chunk-HFBAO6YR.js} +2 -2
  9. package/dist/{chunk-T4H2HHIB.js → chunk-MGPLCACC.js} +198 -8
  10. package/dist/chunk-MGPLCACC.js.map +1 -0
  11. package/dist/{chunk-SSY7TTU2.js → chunk-MSTGEOTX.js} +4 -4
  12. package/dist/{chunk-NDBDNXBI.js → chunk-RYQVLCCC.js} +2 -2
  13. package/dist/data.cjs +197 -7
  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 +203 -13
  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 +197 -7
  26. package/dist/server.cjs.map +1 -1
  27. package/dist/server.d.cts +1 -1
  28. package/dist/server.d.ts +1 -1
  29. package/dist/server.js +2 -2
  30. package/dist/{utils-De_c6fUK.d.ts → utils-4Qdavg8r.d.ts} +2 -2
  31. package/dist/{utils-BEpSreRR.d.cts → utils-OR-XzeYG.d.cts} +2 -2
  32. package/package.json +8 -8
  33. package/src/decoder/lsp7Mint.test.ts +18 -11
  34. package/src/decoder/plugins/enhanceLSP0ERC725Account.ts +39 -2
  35. package/src/decoder/plugins/universalReceiverTypeIds.ts +277 -0
  36. package/src/decoder/types.ts +2 -0
  37. package/dist/chunk-T4H2HHIB.js.map +0 -1
  38. /package/dist/{chunk-FKBKAWB3.js.map → chunk-HFBAO6YR.js.map} +0 -0
  39. /package/dist/{chunk-SSY7TTU2.js.map → chunk-MSTGEOTX.js.map} +0 -0
  40. /package/dist/{chunk-NDBDNXBI.js.map → chunk-RYQVLCCC.js.map} +0 -0
package/dist/server.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Chain, Abi } from 'viem';
2
- import { b as DecoderResult, c as DataKey, E as EnhancedInfo, e as Info } from './index-BzXh7poJ.cjs';
2
+ import { c as DecoderResult, b as DataKey, E as EnhancedInfo, e as Info } from './index-BWD9DaG6.cjs';
3
3
  import '@preact/signals-core';
4
4
  import '@erc725/erc725.js';
5
5
 
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Chain, Abi } from 'viem';
2
- import { b as DecoderResult, c as DataKey, E as EnhancedInfo, e as Info } from './index-BzXh7poJ.js';
2
+ import { c as DecoderResult, b as DataKey, E as EnhancedInfo, e as Info } from './index-BWD9DaG6.js';
3
3
  import '@preact/signals-core';
4
4
  import '@erc725/erc725.js';
5
5
 
package/dist/server.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  fetchMultipleAddresses
3
- } from "./chunk-FKBKAWB3.js";
3
+ } from "./chunk-HFBAO6YR.js";
4
4
  import {
5
5
  __name,
6
6
  collectDataKeys,
@@ -8,7 +8,7 @@ import {
8
8
  decodeTransaction,
9
9
  needsEnhancement,
10
10
  pluginRegistry
11
- } from "./chunk-T4H2HHIB.js";
11
+ } from "./chunk-MGPLCACC.js";
12
12
 
13
13
  // src/server/addressResolver.ts
14
14
  var ServerAddressResolver = class {
@@ -1,4 +1,4 @@
1
- import { g as IDataModelConsumer, I as IDataModel, c as DataKey, b as DecoderResult, T as TransactionState, h as DecoderOptions, P as PluginOptions, i as EnhancerCallback, j as DecodeEventCallback, k as EnhancerAggregationKeyCallback, l as EnhancerSummarizeCallback, m as Aggregation, n as DecoderPlugin, e as Info, S as SchemaPlugin, o as ResultSetData, A as ArrayArgs, C as CustomDecodeFunctionDataReturn, E as EnhancedInfo } from './index-BzXh7poJ.js';
1
+ import { g as IDataModelConsumer, I as IDataModel, b as DataKey, c as DecoderResult, T as TransactionState, h as DecoderOptions, P as PluginOptions, i as EnhancerCallback, j as DecodeEventCallback, k as EnhancerAggregationKeyCallback, l as EnhancerSummarizeCallback, m as Aggregation, n as DecoderPlugin, e as Info, S as SchemaPlugin, o as ResultSetData, A as ArrayArgs, C as CustomDecodeFunctionDataReturn, E as EnhancedInfo } from './index-BWD9DaG6.js';
2
2
  import { Signal } from '@preact/signals-core';
3
3
  import { Transaction, Abi, Hex, AbiParameter, DecodeFunctionDataParameters, Address, Chain, PublicClient } from 'viem';
4
4
  import { ERC725JSONSchema } from '@erc725/erc725.js';
@@ -105,4 +105,4 @@ declare function getPublicClient(chain: Chain): PublicClient;
105
105
  */
106
106
  declare function needsEnhancement(result: DecoderResult): boolean;
107
107
 
108
- export { type AddressResolver as A, type TransactionDecoderResult as T, decodeTransaction as a, decodeKeyValue as b, decodeKeyValueRaw as c, dataModel as d, createNamedArgs as e, customDecodeFunctionData as f, extractAddress as g, getPublicClient as h, consumerModel as i, createGlobalInstance as j, decodeTransactionAsync as k, createAggregationKey as l, defaultPlugins as m, defaultSchemaPlugins as n, standardPlugin as o, getArgByName as p, needsEnhancement as q, standardSchemaPlugin as s };
108
+ export { type AddressResolver as A, type TransactionDecoderResult as T, customDecodeFunctionData as a, decodeKeyValue as b, createNamedArgs as c, dataModel as d, decodeKeyValueRaw as e, decodeTransaction as f, extractAddress as g, getPublicClient as h, consumerModel as i, createAggregationKey as j, createGlobalInstance as k, decodeTransactionAsync as l, defaultPlugins as m, defaultSchemaPlugins as n, getArgByName as o, needsEnhancement as p, standardPlugin as q, standardSchemaPlugin as s };
@@ -1,4 +1,4 @@
1
- import { g as IDataModelConsumer, I as IDataModel, c as DataKey, b as DecoderResult, T as TransactionState, h as DecoderOptions, P as PluginOptions, i as EnhancerCallback, j as DecodeEventCallback, k as EnhancerAggregationKeyCallback, l as EnhancerSummarizeCallback, m as Aggregation, n as DecoderPlugin, e as Info, S as SchemaPlugin, o as ResultSetData, A as ArrayArgs, C as CustomDecodeFunctionDataReturn, E as EnhancedInfo } from './index-BzXh7poJ.cjs';
1
+ import { g as IDataModelConsumer, I as IDataModel, b as DataKey, c as DecoderResult, T as TransactionState, h as DecoderOptions, P as PluginOptions, i as EnhancerCallback, j as DecodeEventCallback, k as EnhancerAggregationKeyCallback, l as EnhancerSummarizeCallback, m as Aggregation, n as DecoderPlugin, e as Info, S as SchemaPlugin, o as ResultSetData, A as ArrayArgs, C as CustomDecodeFunctionDataReturn, E as EnhancedInfo } from './index-BWD9DaG6.cjs';
2
2
  import { Signal } from '@preact/signals-core';
3
3
  import { Transaction, Abi, Hex, AbiParameter, DecodeFunctionDataParameters, Address, Chain, PublicClient } from 'viem';
4
4
  import { ERC725JSONSchema } from '@erc725/erc725.js';
@@ -105,4 +105,4 @@ declare function getPublicClient(chain: Chain): PublicClient;
105
105
  */
106
106
  declare function needsEnhancement(result: DecoderResult): boolean;
107
107
 
108
- export { type AddressResolver as A, type TransactionDecoderResult as T, decodeTransaction as a, decodeKeyValue as b, decodeKeyValueRaw as c, dataModel as d, createNamedArgs as e, customDecodeFunctionData as f, extractAddress as g, getPublicClient as h, consumerModel as i, createGlobalInstance as j, decodeTransactionAsync as k, createAggregationKey as l, defaultPlugins as m, defaultSchemaPlugins as n, standardPlugin as o, getArgByName as p, needsEnhancement as q, standardSchemaPlugin as s };
108
+ export { type AddressResolver as A, type TransactionDecoderResult as T, customDecodeFunctionData as a, decodeKeyValue as b, createNamedArgs as c, dataModel as d, decodeKeyValueRaw as e, decodeTransaction as f, extractAddress as g, getPublicClient as h, consumerModel as i, createAggregationKey as j, createGlobalInstance as k, decodeTransactionAsync as l, defaultPlugins as m, defaultSchemaPlugins as n, getArgByName as o, needsEnhancement as p, standardPlugin as q, standardSchemaPlugin as s };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukso/transaction-decoder",
3
- "version": "1.3.6-dev.4a582bd",
3
+ "version": "1.3.6-dev.6843bdd",
4
4
  "description": "Transaction decoder for LUKSO blockchain with reactive state management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -47,19 +47,19 @@
47
47
  "@lukso/lsp8-contracts": "0.16.7",
48
48
  "@lukso/lsp9-contracts": "0.15.5",
49
49
  "@lukso/universalprofile-contracts": "0.15.5",
50
- "@preact/signals-core": "^1.12.1",
50
+ "@preact/signals-core": "^1.13.0",
51
51
  "graphql-request": "^7.4.0",
52
- "graphql-ws": "^6.0.6",
53
- "lru-cache": "^11.2.4",
54
- "viem": "^2.43.5"
52
+ "graphql-ws": "^6.0.7",
53
+ "lru-cache": "^11.2.6",
54
+ "viem": "^2.45.2"
55
55
  },
56
56
  "devDependencies": {
57
- "@biomejs/biome": "^2.3.11",
57
+ "@biomejs/biome": "^2.3.14",
58
58
  "@types/debug": "^4.1.12",
59
- "@types/node": "^25.0.3",
59
+ "@types/node": "^25.2.3",
60
60
  "tsup": "^8.5.1",
61
61
  "typescript": "^5.9.3",
62
- "vitest": "^4.0.16"
62
+ "vitest": "^4.0.18"
63
63
  },
64
64
  "peerDependencies": {
65
65
  "viem": "^2.0.0"
@@ -145,24 +145,31 @@ describe('LSP7 Mint Transaction Decoding', () => {
145
145
  expect(dataChangedLogs.length).toBeGreaterThan(0)
146
146
  console.log(`\nFound ${dataChangedLogs.length} DataChanged events`)
147
147
 
148
- // Look for UniversalReceiver event (confirms the mint was received)
149
- // UniversalReceiver signature: 0x9c3ba68eb5742b8e3961aea0afc7371a71bf433c8a67a831803b64c064a178c2
150
- const universalReceiverLog = result?.logs?.find(
151
- (log: any) => log.eventName === 'UniversalReceiver'
148
+ // Look for unwrapped URDTokensReceived event from UniversalReceiver (confirms the mint was received)
149
+ // The UniversalReceiver event is now unwrapped into URDTokensReceived with standard: LSP7DigitalAsset
150
+ const unwrappedTransferLog = result?.logs?.find(
151
+ (log: any) =>
152
+ log.eventName === 'URDTokensReceived' &&
153
+ log.standard === 'LSP7DigitalAsset'
152
154
  )
153
- expect(universalReceiverLog).toBeDefined()
154
- expect(universalReceiverLog?.address).toBe(
155
+ expect(unwrappedTransferLog).toBeDefined()
156
+ expect(unwrappedTransferLog?.address).toBe(
155
157
  '0xda9b727f1718f128008ad025828e7fecfaa8a37b'
156
158
  ) // LSP7 token contract
157
159
 
158
- // Verify the UniversalReceiver typeId indicates this is a mint
159
- // typeId 0x20804611b3e2ea21c480dc465142210acf4a2485947541770ec1fb87dee4a55c = _TYPEID_LSP7_TOKENSSENDER
160
- expect(universalReceiverLog?.args?.[2]?.value).toBe(
160
+ // Verify the original UniversalReceiver is preserved in wrappers
161
+ const wrapper = (unwrappedTransferLog as any)?.wrappers?.[0]
162
+ expect(wrapper).toBeDefined()
163
+ expect(wrapper?.eventName).toBe('UniversalReceiver')
164
+
165
+ // Verify the UniversalReceiver typeId indicates this is a mint recipient notification
166
+ // typeId 0x20804611b3e2ea21c480dc465142210acf4a2485947541770ec1fb87dee4a55c = _TYPEID_LSP7_TOKENSRECIPIENT
167
+ expect(wrapper?.args?.[2]?.value).toBe(
161
168
  '0x20804611b3e2ea21c480dc465142210acf4a2485947541770ec1fb87dee4a55c'
162
169
  )
163
170
 
164
- // Verify the 'from' address in UniversalReceiver (the minter, which is the UP)
165
- expect(universalReceiverLog?.args?.[0]?.value).toBe(
171
+ // Verify the 'from' address in the wrapper (the minter, which is the UP)
172
+ expect(wrapper?.args?.[0]?.value).toBe(
166
173
  '0x81A904879F657142d9b5C7e875c786717e2125c0'
167
174
  )
168
175
 
@@ -1,5 +1,5 @@
1
1
  import { universalProfileAbi as UniversalProfile } from '@lukso/universalprofile-contracts/abi'
2
- import type { Address } from 'viem'
2
+ import type { Address, Hex, Log } from 'viem'
3
3
  import { decodeEvent } from '../events'
4
4
  import { decodeTransaction } from '../transaction'
5
5
  import type {
@@ -15,6 +15,7 @@ import type {
15
15
  import { createNamedArgs } from '../utils'
16
16
  import { standardPlugin } from '.'
17
17
  import { enhanceSetData } from './enhanceSetData'
18
+ import { unwrapUniversalReceiver } from './universalReceiverTypeIds'
18
19
 
19
20
  export const enhanceLSP0ERC725AccountPlugin = standardPlugin(
20
21
  UniversalProfile,
@@ -26,7 +27,43 @@ export const enhanceLSP0ERC725AccountPlugin = standardPlugin(
26
27
  {
27
28
  enhance: enhanceLSP0ERC725Account,
28
29
  decodeEvent: async (log: DecodeEventResult, options: DecoderOptions) => {
29
- return decodeEvent(options.chain, UniversalProfile, log)
30
+ const decoded = decodeEvent(options.chain, UniversalProfile, log)
31
+ if (!decoded || decoded.eventName !== 'UniversalReceiver') {
32
+ return decoded
33
+ }
34
+
35
+ // Extract UniversalReceiver args
36
+ const typeId = decoded.args.find((a) => a.name === 'typeId')?.value as Hex
37
+ const receivedData = decoded.args.find((a) => a.name === 'receivedData')
38
+ ?.value as Hex
39
+ const from = decoded.args.find((a) => a.name === 'from')?.value as Address
40
+ const profileAddress = (decoded as unknown as Log).address as Address
41
+
42
+ if (!typeId || !receivedData || !from) {
43
+ return decoded
44
+ }
45
+
46
+ // Unwrap based on typeId (always succeeds — falls back to generic Notification)
47
+ const unwrapped = unwrapUniversalReceiver(
48
+ typeId,
49
+ receivedData,
50
+ from,
51
+ profileAddress
52
+ )
53
+
54
+ // Rewrite the event: inner event becomes the main event,
55
+ // original UniversalReceiver goes into wrappers.
56
+ // Topics and data are rewritten to match the inner event's ABI.
57
+ const { wrappers: _w, standard: _s, ...logBase } = decoded
58
+ return {
59
+ ...logBase,
60
+ eventName: unwrapped.eventName,
61
+ args: unwrapped.args,
62
+ standard: unwrapped.standard,
63
+ topics: unwrapped.topics,
64
+ data: unwrapped.data,
65
+ wrappers: [logBase],
66
+ } as DecodeEventResult
30
67
  },
31
68
  }
32
69
  )
@@ -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