@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.
- package/LICENSE +201 -0
- package/README.md +486 -0
- package/dist/browser.cjs +6912 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.cts +6 -0
- package/dist/browser.d.ts +6 -0
- package/dist/browser.js +131 -0
- package/dist/browser.js.map +1 -0
- package/dist/cdn/transaction-decoder.global.js +296 -0
- package/dist/cdn/transaction-decoder.global.js.map +1 -0
- package/dist/chunk-GGBHTWJL.js +437 -0
- package/dist/chunk-GGBHTWJL.js.map +1 -0
- package/dist/chunk-GXZOF3QY.js +839 -0
- package/dist/chunk-GXZOF3QY.js.map +1 -0
- package/dist/chunk-LJ6ES5XF.js +776 -0
- package/dist/chunk-LJ6ES5XF.js.map +1 -0
- package/dist/chunk-XVHJWV5U.js +4925 -0
- package/dist/chunk-XVHJWV5U.js.map +1 -0
- package/dist/data.cjs +5518 -0
- package/dist/data.cjs.map +1 -0
- package/dist/data.d.cts +43 -0
- package/dist/data.d.ts +43 -0
- package/dist/data.js +55 -0
- package/dist/data.js.map +1 -0
- package/dist/index-BzXh7poJ.d.cts +524 -0
- package/dist/index-BzXh7poJ.d.ts +524 -0
- package/dist/index.cjs +6912 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +756 -0
- package/dist/index.d.ts +756 -0
- package/dist/index.js +131 -0
- package/dist/index.js.map +1 -0
- package/dist/server.cjs +5644 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +217 -0
- package/dist/server.d.ts +217 -0
- package/dist/server.js +644 -0
- package/dist/server.js.map +1 -0
- package/dist/utils-CBAkjQh3.d.cts +108 -0
- package/dist/utils-xT9-km0r.d.ts +108 -0
- package/package.json +101 -0
- package/src/browser.ts +13 -0
- package/src/client/resolveAddresses.ts +157 -0
- package/src/core/addressCollector.ts +153 -0
- package/src/core/addressResolver.ts +135 -0
- package/src/core/dataModel.ts +888 -0
- package/src/core/instance.ts +33 -0
- package/src/core/integrateDecoder.ts +325 -0
- package/src/data.ts +70 -0
- package/src/decoder/GENERATOR_PROPOSAL.md +182 -0
- package/src/decoder/THREE_PHASE_EXAMPLE.md +108 -0
- package/src/decoder/aggregation.ts +218 -0
- package/src/decoder/browserCache.ts +237 -0
- package/src/decoder/cache/README.md +126 -0
- package/src/decoder/cache/index.ts +44 -0
- package/src/decoder/cache.ts +139 -0
- package/src/decoder/constants.ts +125 -0
- package/src/decoder/decodeTransaction.ts +292 -0
- package/src/decoder/errors.ts +95 -0
- package/src/decoder/events.ts +192 -0
- package/src/decoder/functionSignature.ts +344 -0
- package/src/decoder/getDataFromExternalSources.ts +248 -0
- package/src/decoder/graphqlWS.ts +22 -0
- package/src/decoder/interfaces.ts +185 -0
- package/src/decoder/keyValue.ts +5 -0
- package/src/decoder/kvCache.ts +241 -0
- package/src/decoder/lruCache.ts +184 -0
- package/src/decoder/lsp7Mint.test.ts +179 -0
- package/src/decoder/lsp7TransferBatch.test.ts +105 -0
- package/src/decoder/plugins/RegistryAbi.ts +562 -0
- package/src/decoder/plugins/enhanceBurntPix.ts +132 -0
- package/src/decoder/plugins/enhanceGraffiti.ts +70 -0
- package/src/decoder/plugins/enhanceLSP0ERC725Account.ts +179 -0
- package/src/decoder/plugins/enhanceLSP26FollowerSystem.ts +88 -0
- package/src/decoder/plugins/enhanceLSP6KeyManager.ts +231 -0
- package/src/decoder/plugins/enhanceLSP7DigitalAsset.ts +165 -0
- package/src/decoder/plugins/enhanceLSP8IdentifiableDigitalAsset.ts +170 -0
- package/src/decoder/plugins/enhanceLSP9Vault.ts +57 -0
- package/src/decoder/plugins/enhanceRetrieveAbi.ts +85 -0
- package/src/decoder/plugins/enhanceSetData.ts +135 -0
- package/src/decoder/plugins/index.ts +99 -0
- package/src/decoder/plugins/schemaDefault.ts +318 -0
- package/src/decoder/plugins/standardPlugin.ts +202 -0
- package/src/decoder/registry.ts +322 -0
- package/src/decoder/singleGQL.ts +293 -0
- package/src/decoder/transaction.ts +198 -0
- package/src/decoder/types.ts +465 -0
- package/src/decoder/utils.ts +212 -0
- package/src/example/usage.ts +172 -0
- package/src/index.ts +174 -0
- package/src/server/addressResolver.ts +68 -0
- package/src/server/caches.ts +209 -0
- package/src/server/decodeTransactionSync.ts +156 -0
- package/src/server/decodeTransactionsBatch.ts +207 -0
- package/src/server/finishDecoding.ts +116 -0
- package/src/server/index.ts +81 -0
- package/src/server/lsp23Resolver.test.ts +46 -0
- package/src/server/lsp23Resolver.ts +419 -0
- package/src/server/types.ts +168 -0
- package/src/server.ts +22 -0
- package/src/shared/addressResolver.ts +651 -0
- package/src/shared/cache.ts +144 -0
- package/src/shared/constants.ts +21 -0
- package/src/stubs/tty.ts +13 -0
- package/src/stubs/util.ts +42 -0
- package/src/types/index.ts +154 -0
- package/src/types/provider.ts +46 -0
- package/src/umd.ts +13 -0
- package/src/utils/debug.ts +49 -0
- package/src/utils/json-bigint.ts +47 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { ServerAddressResolver } from './addressResolver'
|
|
2
|
+
import { ServerDecoderCaches } from './caches'
|
|
3
|
+
import { decodeTransactionSync } from './decodeTransactionSync'
|
|
4
|
+
import { decodeTransactionsBatch } from './decodeTransactionsBatch'
|
|
5
|
+
import { finishDecoding } from './finishDecoding'
|
|
6
|
+
import type {
|
|
7
|
+
ServerDecoder,
|
|
8
|
+
ServerDecoderConfig,
|
|
9
|
+
ServerDecoderOptions,
|
|
10
|
+
} from './types'
|
|
11
|
+
|
|
12
|
+
// Import plugins to ensure they are registered
|
|
13
|
+
import '../decoder/plugins'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create a server-optimized decoder instance
|
|
17
|
+
*/
|
|
18
|
+
export function createServerDecoder(
|
|
19
|
+
config: ServerDecoderConfig
|
|
20
|
+
): ServerDecoder {
|
|
21
|
+
// Create caches
|
|
22
|
+
const caches = new ServerDecoderCaches({
|
|
23
|
+
transactionCacheSize: config.transactionCacheSize,
|
|
24
|
+
addressCacheSize: config.addressCacheSize,
|
|
25
|
+
abiCacheSize: config.abiCacheSize,
|
|
26
|
+
schemaCacheSize: config.schemaCacheSize,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
// Create address resolver
|
|
30
|
+
const addressResolver = new ServerAddressResolver(
|
|
31
|
+
config.graphqlEndpoint,
|
|
32
|
+
config.defaultChain,
|
|
33
|
+
caches
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
// Return server decoder interface
|
|
37
|
+
return {
|
|
38
|
+
async decodeTransaction(transaction, options) {
|
|
39
|
+
const opts: ServerDecoderOptions = {
|
|
40
|
+
chain: config.defaultChain,
|
|
41
|
+
timeoutMs: config.defaultTimeout,
|
|
42
|
+
...options,
|
|
43
|
+
}
|
|
44
|
+
return decodeTransactionSync(transaction, opts, caches)
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
async decodeTransactionsBatch(transactions, options) {
|
|
48
|
+
const opts: ServerDecoderOptions = {
|
|
49
|
+
chain: config.defaultChain,
|
|
50
|
+
timeoutMs: config.defaultTimeout,
|
|
51
|
+
...options,
|
|
52
|
+
}
|
|
53
|
+
return decodeTransactionsBatch(transactions, opts, caches)
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
async finishDecoding(transaction, options) {
|
|
57
|
+
const opts: ServerDecoderOptions = {
|
|
58
|
+
chain: config.defaultChain,
|
|
59
|
+
timeoutMs: config.defaultTimeout,
|
|
60
|
+
...options,
|
|
61
|
+
}
|
|
62
|
+
return finishDecoding(transaction, opts, caches)
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
clearCaches() {
|
|
66
|
+
caches.clear()
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
getCacheStats() {
|
|
70
|
+
return caches.getStats()
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
async warmAddressCache(addresses) {
|
|
74
|
+
await addressResolver.warmCache(addresses)
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
async resolveAddresses(addresses, timeoutMs = 3000) {
|
|
78
|
+
return addressResolver.resolveAddressesWithTimeout(addresses, timeoutMs)
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
decodeDeploymentCalldata,
|
|
4
|
+
decodeDeploymentOutput,
|
|
5
|
+
} from './lsp23Resolver'
|
|
6
|
+
|
|
7
|
+
describe('LSP23 Resolver', () => {
|
|
8
|
+
it('should decode deployERC1167Proxies calldata', () => {
|
|
9
|
+
// Sample transaction from user's trace
|
|
10
|
+
const input =
|
|
11
|
+
'0x6a66a75300000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000066093407b6704b89793beffd0d8f0000000000000000000000000000000000000000000000000000000000000002602835628cabf5153dc5b88df586e3f52fd940693d9634d5d50a353cb1b8077234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000052c90985af970d4e0dc26cb5d052505278af32a900000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000024c4d66de8000000000000000000000000000000000066093407b6704b89793beffd0d8f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a75684d7d048704a2db851d05ba0c3cbe226264c00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000004c4d66de800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000075ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc50cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b474b80742de2bf82acb3630000a5467dfe7019bf2c7c5f7a707711b9d4cad118c84b80742de2bf82acb36300004c773efdab62bfccf329acda9018b7dac888e463df30dba06db6a30e65354d9a64c609861f089545ca58c6b4dbe31a5f338cb0e3df30dba06db6a30e65354d9a64c6098600000000000000000000000000000000df30dba06db6a30e65354d9a64c6098600000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000000005d00006f357c6a0020f8a1d63f95b24fbfcacf39976c7cbc8213cfd14d0ec50b1c3901f1c2cfe8f633697066733a2f2f516d627047734e516b56744e7375514a636e5437724458366d3676317a52675261516637364e48357a674e4e506e0000000000000000000000000000000000000000000000000000000000000000000014a5467dfe7019bf2c7c5f7a707711b9d4cad118c800000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000060080000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000007f3f06000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014a5467dfe7019bf2c7c5f7a707711b9d4cad118c800000000000000000000000000000000000000000000000000000000000000000000000000000000000000144c773efdab62bfccf329acda9018b7dac888e463000000000000000000000000'
|
|
12
|
+
|
|
13
|
+
const decoded = decodeDeploymentCalldata(input)
|
|
14
|
+
|
|
15
|
+
expect(decoded).toBeDefined()
|
|
16
|
+
expect(decoded?.salt).toBe(
|
|
17
|
+
'0x2835628cabf5153dc5b88df586e3f52fd940693d9634d5d50a353cb1b8077234'
|
|
18
|
+
)
|
|
19
|
+
expect(decoded?.primaryImplementation.toLowerCase()).toBe(
|
|
20
|
+
'0x52c90985af970d4e0dc26cb5d052505278af32a9'
|
|
21
|
+
)
|
|
22
|
+
expect(decoded?.secondaryImplementation.toLowerCase()).toBe(
|
|
23
|
+
'0xa75684d7d048704a2db851d05ba0c3cbe226264c'
|
|
24
|
+
)
|
|
25
|
+
expect(decoded?.postDeploymentModule.toLowerCase()).toBe(
|
|
26
|
+
'0x000000000066093407b6704b89793beffd0d8f00'
|
|
27
|
+
)
|
|
28
|
+
expect(decoded?.initializationCalldata).toBeDefined()
|
|
29
|
+
expect(decoded?.initializationCalldata.length).toBeGreaterThan(0)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('should decode deployERC1167Proxies output', () => {
|
|
33
|
+
const output =
|
|
34
|
+
'0x00000000000000000000000002e655f92f01bc7880807ec409f134b91bb28381000000000000000000000000497fdac9e42193f6f79651d2d1caf6ba5789e055'
|
|
35
|
+
|
|
36
|
+
const decoded = decodeDeploymentOutput(output)
|
|
37
|
+
|
|
38
|
+
expect(decoded).toBeDefined()
|
|
39
|
+
expect(decoded?.profileAddress.toLowerCase()).toBe(
|
|
40
|
+
'0x02e655f92f01bc7880807ec409f134b91bb28381'
|
|
41
|
+
)
|
|
42
|
+
expect(decoded?.urdAddress.toLowerCase()).toBe(
|
|
43
|
+
'0x497fdac9e42193f6f79651d2d1caf6ba5789e055'
|
|
44
|
+
)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import type { Address, Chain } from 'viem'
|
|
2
|
+
import {
|
|
3
|
+
bytesToHex,
|
|
4
|
+
decodeAbiParameters,
|
|
5
|
+
isHex,
|
|
6
|
+
parseAbiParameters,
|
|
7
|
+
} from 'viem'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* LSP23 Factory address (same on all networks)
|
|
11
|
+
*/
|
|
12
|
+
export const LSP23_FACTORY_ADDRESS =
|
|
13
|
+
'0x2300000a84d25df63081feaa37ba6b62c4c89a30' as const
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* deployERC1167Proxies function selector
|
|
17
|
+
*/
|
|
18
|
+
export const DEPLOY_ERC1167_PROXIES_SELECTOR = '0x6a66a753' as const
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* LSP23 deployment data structure
|
|
22
|
+
*/
|
|
23
|
+
export interface LSP23DeploymentData {
|
|
24
|
+
profileAddress: Address
|
|
25
|
+
urdAddress: Address
|
|
26
|
+
deployerAddress: Address // The controller that deployed this profile
|
|
27
|
+
salt: string
|
|
28
|
+
primaryImplementation: Address
|
|
29
|
+
secondaryImplementation: Address
|
|
30
|
+
postDeploymentModule: Address
|
|
31
|
+
initializationCalldata: string
|
|
32
|
+
deploymentCalldata: string // Full calldata to deployERC1167Proxies
|
|
33
|
+
transactionHash: string
|
|
34
|
+
blockNumber: bigint
|
|
35
|
+
blockTimestamp: number // Unix timestamp of the block
|
|
36
|
+
chainId: number
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Contract deployment tuple structures
|
|
41
|
+
* Primary: (bytes32 salt, uint256 fundingAmount, address implementationContract, bytes initializeCalldata)
|
|
42
|
+
* Secondary: (uint256 fundingAmount, address implementationContract, bytes addInitializeCalldata, bool addConstructor, bytes constructorParams)
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Decode deployERC1167Proxies calldata
|
|
47
|
+
*
|
|
48
|
+
* Function signature:
|
|
49
|
+
* deployERC1167Proxies(
|
|
50
|
+
* (bytes32 salt, uint256 fundingAmount, address implementationContract, bytes initializeCalldata) primaryContractDeployment,
|
|
51
|
+
* (uint256 fundingAmount, address implementationContract, bytes addInitializeCalldata, bool addConstructor, bytes constructorParams) secondaryContractDeployment,
|
|
52
|
+
* address postDeploymentModule,
|
|
53
|
+
* bytes postDeploymentModuleCalldata
|
|
54
|
+
* )
|
|
55
|
+
*/
|
|
56
|
+
export function decodeDeploymentCalldata(input: string): {
|
|
57
|
+
salt: string
|
|
58
|
+
primaryImplementation: Address
|
|
59
|
+
secondaryImplementation: Address
|
|
60
|
+
postDeploymentModule: Address
|
|
61
|
+
initializationCalldata: string
|
|
62
|
+
} | null {
|
|
63
|
+
try {
|
|
64
|
+
// Remove function selector (first 4 bytes / 8 hex chars + 0x)
|
|
65
|
+
const params = `0x${input.slice(10)}` as `0x${string}`
|
|
66
|
+
|
|
67
|
+
// Decode the parameters
|
|
68
|
+
const decoded = decodeAbiParameters(
|
|
69
|
+
parseAbiParameters(
|
|
70
|
+
'(bytes32,uint256,address,bytes), (uint256,address,bytes,bool,bytes), address, bytes'
|
|
71
|
+
),
|
|
72
|
+
params
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
const [
|
|
76
|
+
primaryDeployment,
|
|
77
|
+
secondaryDeployment,
|
|
78
|
+
postDeploymentModule,
|
|
79
|
+
postDeploymentModuleCalldata,
|
|
80
|
+
] = decoded
|
|
81
|
+
|
|
82
|
+
// Primary deployment tuple: (bytes32 salt, uint256 fundingAmount, address implementationContract, bytes initializeCalldata)
|
|
83
|
+
const [salt, , primaryImplementation] = primaryDeployment as [
|
|
84
|
+
string,
|
|
85
|
+
bigint,
|
|
86
|
+
Address,
|
|
87
|
+
string,
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
// Secondary deployment tuple: (uint256 fundingAmount, address implementationContract, bytes addInitializeCalldata, bool addConstructor, bytes constructorParams)
|
|
91
|
+
const [, secondaryImplementation] = secondaryDeployment as [
|
|
92
|
+
bigint,
|
|
93
|
+
Address,
|
|
94
|
+
string,
|
|
95
|
+
boolean,
|
|
96
|
+
string,
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
if (!primaryImplementation || !secondaryImplementation) {
|
|
100
|
+
return null
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
salt,
|
|
105
|
+
primaryImplementation,
|
|
106
|
+
secondaryImplementation,
|
|
107
|
+
postDeploymentModule: postDeploymentModule as Address,
|
|
108
|
+
initializationCalldata: postDeploymentModuleCalldata as string,
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error('Failed to decode LSP23 deployment calldata:', error)
|
|
112
|
+
return null
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Decode deployERC1167Proxies output to get deployed addresses
|
|
118
|
+
*
|
|
119
|
+
* Returns: (address primaryContractAddress, address secondaryContractAddress)
|
|
120
|
+
*/
|
|
121
|
+
export function decodeDeploymentOutput(output: string): {
|
|
122
|
+
profileAddress: Address
|
|
123
|
+
urdAddress: Address
|
|
124
|
+
} | null {
|
|
125
|
+
try {
|
|
126
|
+
const decoded = decodeAbiParameters(
|
|
127
|
+
parseAbiParameters('address, address'),
|
|
128
|
+
output as `0x${string}`
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
profileAddress: decoded[0],
|
|
133
|
+
urdAddress: decoded[1],
|
|
134
|
+
}
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.error('Failed to decode LSP23 deployment output:', error)
|
|
137
|
+
return null
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Extract controller addresses from initialization calldata
|
|
143
|
+
*
|
|
144
|
+
* The initializationCalldata contains a setDataBatch call that sets up controller permissions.
|
|
145
|
+
* Function: setDataBatch(bytes32[] dataKeys, bytes[] dataValues)
|
|
146
|
+
*
|
|
147
|
+
* The dataKeys contain entries like:
|
|
148
|
+
* - AddressPermissions:Permissions:<controller-address>
|
|
149
|
+
*
|
|
150
|
+
* We extract controller addresses from these keys.
|
|
151
|
+
*/
|
|
152
|
+
export function extractControllersFromInitCalldata(
|
|
153
|
+
initializationCalldata: string
|
|
154
|
+
): Address[] {
|
|
155
|
+
try {
|
|
156
|
+
if (!initializationCalldata || initializationCalldata === '0x') {
|
|
157
|
+
return []
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// The postDeploymentModuleCalldata is raw ABI-encoded (bytes32[], bytes[])
|
|
161
|
+
// No function selector - decode directly
|
|
162
|
+
const decoded = decodeAbiParameters(
|
|
163
|
+
parseAbiParameters('bytes32[], bytes[]'),
|
|
164
|
+
initializationCalldata as `0x${string}`
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
const [dataKeys, dataValues] = decoded
|
|
168
|
+
const controllers: Address[] = []
|
|
169
|
+
|
|
170
|
+
// Look for AddressPermissions[] keys
|
|
171
|
+
// The key is 0xdf30dba06db6a30e65354d9a64c60986 followed by zeros
|
|
172
|
+
// The controller address is in the corresponding value at the same index (encoded as bytes)
|
|
173
|
+
const ADDRESS_PERMISSIONS_ARRAY_PREFIX =
|
|
174
|
+
'0xdf30dba06db6a30e65354d9a64c60986'
|
|
175
|
+
|
|
176
|
+
for (let i = 0; i < dataKeys.length; i++) {
|
|
177
|
+
const keyStr = dataKeys[i].toLowerCase()
|
|
178
|
+
if (keyStr.startsWith(ADDRESS_PERMISSIONS_ARRAY_PREFIX)) {
|
|
179
|
+
// The value contains the controller address
|
|
180
|
+
const value = dataValues[i]
|
|
181
|
+
|
|
182
|
+
// Convert to hex if it's not already
|
|
183
|
+
const valueHex = isHex(value) ? value : bytesToHex(value as Uint8Array)
|
|
184
|
+
|
|
185
|
+
// The value should be a 20-byte address (40 hex chars + 0x = 42 total)
|
|
186
|
+
// or possibly 32 bytes with padding
|
|
187
|
+
if (valueHex.length === 42) {
|
|
188
|
+
// Exactly 20 bytes - this is the address
|
|
189
|
+
const addr = valueHex as Address
|
|
190
|
+
if (
|
|
191
|
+
addr !== '0x0000000000000000000000000000000000000000' &&
|
|
192
|
+
!controllers.some((c) => c.toLowerCase() === addr.toLowerCase())
|
|
193
|
+
) {
|
|
194
|
+
controllers.push(addr)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return controllers
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error('Failed to extract controllers from init calldata:', error)
|
|
203
|
+
return []
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Hypersync query configuration for LSP23 deployments
|
|
209
|
+
*/
|
|
210
|
+
export interface HypersyncQueryConfig {
|
|
211
|
+
/**
|
|
212
|
+
* Controller addresses (derived keys) that deployed profiles
|
|
213
|
+
* We only want LSP23 data for profiles WE created, not imported
|
|
214
|
+
*/
|
|
215
|
+
controllerAddresses?: Address[]
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Profile addresses to query deployment data for
|
|
219
|
+
* More efficient than querying by controllers when we already know the profiles
|
|
220
|
+
*/
|
|
221
|
+
profileAddresses?: Address[]
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Starting block (0 for genesis)
|
|
225
|
+
*/
|
|
226
|
+
fromBlock?: number
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Ending block (undefined for latest)
|
|
230
|
+
*/
|
|
231
|
+
toBlock?: number
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Chain ID to query
|
|
235
|
+
*/
|
|
236
|
+
chainId: number
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Build Hypersync query to find LSP23 deployments
|
|
241
|
+
*
|
|
242
|
+
* Strategy 1 (by profileAddresses - RECOMMENDED):
|
|
243
|
+
* Query CREATE2 traces where the created address matches our profile addresses.
|
|
244
|
+
* Then filter transactions to LSP23 factory and verify deployer is one of our controllers.
|
|
245
|
+
* This is more efficient when we already know the profile addresses from GraphQL.
|
|
246
|
+
*
|
|
247
|
+
* Strategy 2 (by controllerAddresses - LEGACY):
|
|
248
|
+
* Query transactions TO the LSP23 factory FROM our controller addresses.
|
|
249
|
+
* This scans the entire chain history for all deployments by our controllers.
|
|
250
|
+
*/
|
|
251
|
+
export function buildLSP23DeploymentQuery(config: HypersyncQueryConfig) {
|
|
252
|
+
const {
|
|
253
|
+
controllerAddresses,
|
|
254
|
+
profileAddresses,
|
|
255
|
+
fromBlock = 0,
|
|
256
|
+
toBlock,
|
|
257
|
+
chainId,
|
|
258
|
+
} = config
|
|
259
|
+
|
|
260
|
+
// Prefer querying by profile addresses (more efficient)
|
|
261
|
+
if (profileAddresses && profileAddresses.length > 0) {
|
|
262
|
+
return {
|
|
263
|
+
from_block: fromBlock,
|
|
264
|
+
to_block: toBlock,
|
|
265
|
+
// Query CREATE2 traces where the created address is one of our profiles
|
|
266
|
+
traces: [
|
|
267
|
+
{
|
|
268
|
+
type: ['create2'],
|
|
269
|
+
to: profileAddresses.map((addr) => addr.toLowerCase()),
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
// Also get the transactions to verify they're to the LSP23 factory
|
|
273
|
+
transactions: [
|
|
274
|
+
{
|
|
275
|
+
to: [LSP23_FACTORY_ADDRESS.toLowerCase()],
|
|
276
|
+
},
|
|
277
|
+
],
|
|
278
|
+
field_selection: {
|
|
279
|
+
transaction: ['hash', 'from', 'to', 'input', 'output', 'block_number'],
|
|
280
|
+
trace: ['transaction_hash', 'type', 'from', 'to'],
|
|
281
|
+
block: ['number', 'timestamp'],
|
|
282
|
+
},
|
|
283
|
+
include_all_blocks: false,
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Fall back to querying by controller addresses (less efficient)
|
|
288
|
+
if (controllerAddresses && controllerAddresses.length > 0) {
|
|
289
|
+
return {
|
|
290
|
+
from_block: fromBlock,
|
|
291
|
+
to_block: toBlock,
|
|
292
|
+
// Query transactions to the LSP23 factory from our controllers
|
|
293
|
+
transactions: [
|
|
294
|
+
{
|
|
295
|
+
from: controllerAddresses.map((addr) => addr.toLowerCase()),
|
|
296
|
+
to: [LSP23_FACTORY_ADDRESS.toLowerCase()],
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
// Also get traces to see the CREATE2 operations
|
|
300
|
+
traces: [
|
|
301
|
+
{
|
|
302
|
+
type: ['create2'],
|
|
303
|
+
// No filter on 'to' - we'll match by transaction hash
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
field_selection: {
|
|
307
|
+
transaction: ['hash', 'from', 'to', 'input', 'output', 'block_number'],
|
|
308
|
+
trace: ['transaction_hash', 'type', 'from', 'to'],
|
|
309
|
+
block: ['number', 'timestamp'],
|
|
310
|
+
},
|
|
311
|
+
include_all_blocks: false,
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
throw new Error(
|
|
316
|
+
'Either profileAddresses or controllerAddresses must be provided'
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Parse Hypersync response to extract LSP23 deployment data
|
|
322
|
+
* @param response - Hypersync API response
|
|
323
|
+
* @param chainId - Chain ID
|
|
324
|
+
* @param controllerAddresses - Optional list of controller addresses to filter by (only return deployments from these deployers)
|
|
325
|
+
*/
|
|
326
|
+
export function parseLSP23DeploymentsFromHypersync(
|
|
327
|
+
response: any,
|
|
328
|
+
chainId: number,
|
|
329
|
+
controllerAddresses?: Address[]
|
|
330
|
+
): LSP23DeploymentData[] {
|
|
331
|
+
const deployments: LSP23DeploymentData[] = []
|
|
332
|
+
|
|
333
|
+
if (!response?.data) {
|
|
334
|
+
return deployments
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Build a map of block number -> timestamp
|
|
338
|
+
const blockTimestamps = new Map<number, number>()
|
|
339
|
+
for (const block of response.data.blocks || []) {
|
|
340
|
+
blockTimestamps.set(block.number, block.timestamp)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Build a map of transaction hash -> transaction data
|
|
344
|
+
const txMap = new Map<string, any>()
|
|
345
|
+
for (const tx of response.data.transactions || []) {
|
|
346
|
+
if (
|
|
347
|
+
tx.to?.toLowerCase() === LSP23_FACTORY_ADDRESS.toLowerCase() &&
|
|
348
|
+
tx.input?.startsWith(DEPLOY_ERC1167_PROXIES_SELECTOR)
|
|
349
|
+
) {
|
|
350
|
+
txMap.set(tx.hash.toLowerCase(), tx)
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Process traces to find CREATE2 operations
|
|
355
|
+
const create2Traces = new Map<string, Address>() // txHash -> created profile address
|
|
356
|
+
|
|
357
|
+
for (const trace of response.data.traces || []) {
|
|
358
|
+
if (trace.type === 'create2' && trace.to) {
|
|
359
|
+
create2Traces.set(
|
|
360
|
+
trace.transaction_hash.toLowerCase(),
|
|
361
|
+
trace.to as Address
|
|
362
|
+
)
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Create a Set of lowercase controller addresses for efficient filtering
|
|
367
|
+
const controllerSet = controllerAddresses
|
|
368
|
+
? new Set(controllerAddresses.map((addr) => addr.toLowerCase()))
|
|
369
|
+
: null
|
|
370
|
+
|
|
371
|
+
// Match transactions with traces
|
|
372
|
+
for (const [txHash, profileAddress] of create2Traces) {
|
|
373
|
+
const tx = txMap.get(txHash)
|
|
374
|
+
if (!tx) continue
|
|
375
|
+
|
|
376
|
+
// If controller filtering is enabled, verify the deployer is one of our controllers
|
|
377
|
+
if (controllerSet && !controllerSet.has(tx.from.toLowerCase())) {
|
|
378
|
+
continue
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Decode the transaction
|
|
382
|
+
const decodedInput = decodeDeploymentCalldata(tx.input)
|
|
383
|
+
const decodedOutput = tx.output ? decodeDeploymentOutput(tx.output) : null
|
|
384
|
+
|
|
385
|
+
if (!decodedInput || !decodedOutput) continue
|
|
386
|
+
|
|
387
|
+
// Get block timestamp for this transaction
|
|
388
|
+
const blockTimestamp = blockTimestamps.get(tx.block_number) || 0
|
|
389
|
+
|
|
390
|
+
deployments.push({
|
|
391
|
+
profileAddress: decodedOutput.profileAddress,
|
|
392
|
+
urdAddress: decodedOutput.urdAddress,
|
|
393
|
+
deployerAddress: tx.from as Address, // The controller that deployed this profile
|
|
394
|
+
salt: decodedInput.salt,
|
|
395
|
+
primaryImplementation: decodedInput.primaryImplementation,
|
|
396
|
+
secondaryImplementation: decodedInput.secondaryImplementation,
|
|
397
|
+
postDeploymentModule: decodedInput.postDeploymentModule,
|
|
398
|
+
initializationCalldata: decodedInput.initializationCalldata,
|
|
399
|
+
deploymentCalldata: tx.input, // Full calldata to deployERC1167Proxies
|
|
400
|
+
transactionHash: tx.hash,
|
|
401
|
+
blockNumber: BigInt(tx.block_number),
|
|
402
|
+
blockTimestamp, // Unix timestamp of the block
|
|
403
|
+
chainId,
|
|
404
|
+
})
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return deployments
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Note: Hypersync querying should be done server-side to protect HYPERSYNC_TOKEN.
|
|
412
|
+
* Use the wallet-app API endpoint /api/lsp23Deployments instead of calling Hypersync directly.
|
|
413
|
+
*
|
|
414
|
+
* This module exports:
|
|
415
|
+
* - buildLSP23DeploymentQuery() - Build the Hypersync query structure
|
|
416
|
+
* - parseLSP23DeploymentsFromHypersync() - Parse Hypersync response
|
|
417
|
+
* - decodeDeploymentCalldata() - Decode LSP23 transaction input
|
|
418
|
+
* - decodeDeploymentOutput() - Decode LSP23 transaction output
|
|
419
|
+
*/
|