@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,153 @@
|
|
|
1
|
+
import { type Address, type Hex, isHex, size } from 'viem'
|
|
2
|
+
import type { ArrayArgs } from '../decoder/types'
|
|
3
|
+
import { getArgByName } from '../decoder/utils'
|
|
4
|
+
import type { DataKey } from '../types'
|
|
5
|
+
|
|
6
|
+
export interface CollectedAddress {
|
|
7
|
+
key: DataKey
|
|
8
|
+
path: Array<string | number>
|
|
9
|
+
parent?: Record<string, unknown>
|
|
10
|
+
root: unknown
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Collect all DataKeys from a data structure
|
|
15
|
+
* This is a simplified version that just returns the keys
|
|
16
|
+
* @param data - The data to analyze
|
|
17
|
+
* @param skipGqlTypes - Whether to skip GraphQL type fields
|
|
18
|
+
* @param existingKeys - Optional existing keys to merge with
|
|
19
|
+
*/
|
|
20
|
+
export function collectDataKeys(
|
|
21
|
+
data: unknown,
|
|
22
|
+
skipGqlTypes = true,
|
|
23
|
+
existingKeys?: DataKey[]
|
|
24
|
+
): DataKey[] {
|
|
25
|
+
const addresses = new Set<string>()
|
|
26
|
+
|
|
27
|
+
// Add existing keys to the set first
|
|
28
|
+
if (existingKeys) {
|
|
29
|
+
for (const key of existingKeys) {
|
|
30
|
+
if (typeof key === 'string') {
|
|
31
|
+
addresses.add(key)
|
|
32
|
+
} else {
|
|
33
|
+
addresses.add(JSON.stringify(key))
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Check if this is an LSP8 decoded transaction and collect tokens
|
|
39
|
+
function collectLSP8Tokens(obj: Record<string, unknown>): void {
|
|
40
|
+
if (
|
|
41
|
+
obj.standard !== 'LSP8IdentifiableDigitalAsset' ||
|
|
42
|
+
!obj.to ||
|
|
43
|
+
!obj.args
|
|
44
|
+
) {
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const contractAddress = obj.to as string
|
|
49
|
+
const args = obj.args as ArrayArgs
|
|
50
|
+
|
|
51
|
+
// Check for single tokenId
|
|
52
|
+
const tokenId = getArgByName(args, 'tokenId') as string | undefined
|
|
53
|
+
if (tokenId && tokenId !== null && tokenId !== '0x') {
|
|
54
|
+
addresses.add(`${contractAddress}:${tokenId}`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check for multiple tokenIds
|
|
58
|
+
const tokenIds = getArgByName(args, 'tokenIds') as string[] | undefined
|
|
59
|
+
if (Array.isArray(tokenIds)) {
|
|
60
|
+
for (const tokenId of tokenIds) {
|
|
61
|
+
// Skip null/undefined/empty tokens
|
|
62
|
+
if (tokenId && tokenId !== null && tokenId !== '0x') {
|
|
63
|
+
addresses.add(`${contractAddress}:${tokenId}`)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function collect(
|
|
70
|
+
obj: unknown,
|
|
71
|
+
parent?: Record<string, unknown>,
|
|
72
|
+
visited = new WeakSet(),
|
|
73
|
+
path: string[] = []
|
|
74
|
+
): void {
|
|
75
|
+
// Circular reference detection
|
|
76
|
+
if (obj && typeof obj === 'object' && visited.has(obj)) {
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (obj && typeof obj === 'object') {
|
|
81
|
+
visited.add(obj)
|
|
82
|
+
}
|
|
83
|
+
// Handle string case first
|
|
84
|
+
if (typeof obj === 'string' && obj.startsWith('0x')) {
|
|
85
|
+
// Check if this is a composite address:tokenId format
|
|
86
|
+
if (obj.includes(':')) {
|
|
87
|
+
const [addressPart, tokenIdPart] = obj.split(':')
|
|
88
|
+
// Validate both parts are hex strings
|
|
89
|
+
if (
|
|
90
|
+
addressPart.startsWith('0x') &&
|
|
91
|
+
tokenIdPart.startsWith('0x') &&
|
|
92
|
+
isHex(addressPart) &&
|
|
93
|
+
size(addressPart as Hex) === 20
|
|
94
|
+
) {
|
|
95
|
+
addresses.add(obj) // Just add the composite string directly
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Use regex to detect zero-padded addresses
|
|
101
|
+
const isPaddedAddress = /^0x0*([a-fA-F0-9]{40})$/.test(obj)
|
|
102
|
+
|
|
103
|
+
if (isPaddedAddress || (isHex(obj) && size(obj as Hex) === 20)) {
|
|
104
|
+
// Skip if parent has __gqltype
|
|
105
|
+
if (parent && skipGqlTypes && '__gqltype' in parent) {
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Extract the actual address (remove padding if needed)
|
|
110
|
+
const address = isPaddedAddress
|
|
111
|
+
? (obj.replace(/^0x0*([a-fA-F0-9]{40})$/, '0x$1') as Address)
|
|
112
|
+
: obj
|
|
113
|
+
|
|
114
|
+
// Skip if too many zeros (likely not an address)
|
|
115
|
+
if (
|
|
116
|
+
address
|
|
117
|
+
.slice(2)
|
|
118
|
+
.split('')
|
|
119
|
+
.filter((c: string) => c === '0').length > 10
|
|
120
|
+
) {
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
addresses.add(address)
|
|
125
|
+
}
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!obj || typeof obj !== 'object') return
|
|
130
|
+
|
|
131
|
+
if (Array.isArray(obj)) {
|
|
132
|
+
for (let i = 0; i < obj.length; i++) {
|
|
133
|
+
collect(obj[i], parent, visited, [...path, `[${i}]`])
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
const record = obj as Record<string, unknown>
|
|
137
|
+
|
|
138
|
+
// Check if this is an LSP8 transaction at the root level
|
|
139
|
+
if ('standard' in record && 'to' in record && 'args' in record) {
|
|
140
|
+
collectLSP8Tokens(record)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for (const [key, value] of Object.entries(record)) {
|
|
144
|
+
collect(value, record, visited, [...path, key])
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
collect(data)
|
|
150
|
+
|
|
151
|
+
// Convert to DataKey array (all as strings)
|
|
152
|
+
return Array.from(addresses) as DataKey[]
|
|
153
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { Chain } from 'viem'
|
|
2
|
+
import { fetchMultipleAddresses } from '../shared/addressResolver'
|
|
3
|
+
import type { AddressIdentityCache } from '../shared/cache'
|
|
4
|
+
import type { DataKey, EnhancedInfo, IDataModel } from '../types'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for address resolver
|
|
8
|
+
*/
|
|
9
|
+
export interface AddressResolverConfig {
|
|
10
|
+
dataModel: IDataModel
|
|
11
|
+
graphqlEndpoint: string
|
|
12
|
+
chain: Chain
|
|
13
|
+
batchSize?: number
|
|
14
|
+
batchDelay?: number
|
|
15
|
+
cache?: AddressIdentityCache
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Resolve addresses using GraphQL
|
|
20
|
+
* Replaces the old patchValue mechanism
|
|
21
|
+
*/
|
|
22
|
+
export class AddressResolver {
|
|
23
|
+
private dataModel: IDataModel
|
|
24
|
+
private graphqlEndpoint: string
|
|
25
|
+
private batchSize: number
|
|
26
|
+
private batchDelay: number
|
|
27
|
+
private pendingAddresses = new Set<string>()
|
|
28
|
+
private batchTimer?: NodeJS.Timeout
|
|
29
|
+
private cache?: AddressIdentityCache
|
|
30
|
+
|
|
31
|
+
constructor(config: AddressResolverConfig) {
|
|
32
|
+
this.dataModel = config.dataModel
|
|
33
|
+
this.graphqlEndpoint = config.graphqlEndpoint
|
|
34
|
+
this.batchSize = config.batchSize || 50
|
|
35
|
+
this.batchDelay = config.batchDelay || 100
|
|
36
|
+
this.cache = config.cache
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Start watching for missing addresses
|
|
41
|
+
*/
|
|
42
|
+
startWatching() {
|
|
43
|
+
// Check for missing addresses periodically
|
|
44
|
+
setInterval(() => {
|
|
45
|
+
const missing = this.dataModel.getMissingKeys()
|
|
46
|
+
if (missing.length > 0) {
|
|
47
|
+
this.queueAddresses(missing)
|
|
48
|
+
}
|
|
49
|
+
}, 1000)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Queue addresses for batch resolution
|
|
54
|
+
*/
|
|
55
|
+
queueAddresses(addresses: DataKey[]) {
|
|
56
|
+
// Add to pending set
|
|
57
|
+
for (const addr of addresses) {
|
|
58
|
+
this.pendingAddresses.add(addr)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Clear existing timer
|
|
62
|
+
if (this.batchTimer) {
|
|
63
|
+
clearTimeout(this.batchTimer)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Set new timer
|
|
67
|
+
this.batchTimer = setTimeout(() => {
|
|
68
|
+
this.processBatch()
|
|
69
|
+
}, this.batchDelay)
|
|
70
|
+
|
|
71
|
+
// Process immediately if batch is full
|
|
72
|
+
if (this.pendingAddresses.size >= this.batchSize) {
|
|
73
|
+
clearTimeout(this.batchTimer)
|
|
74
|
+
this.processBatch()
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Process a batch of addresses
|
|
80
|
+
*/
|
|
81
|
+
private async processBatch() {
|
|
82
|
+
const batch = Array.from(this.pendingAddresses).slice(0, this.batchSize)
|
|
83
|
+
if (batch.length === 0) return
|
|
84
|
+
|
|
85
|
+
// Remove from pending
|
|
86
|
+
for (const key of batch) {
|
|
87
|
+
this.pendingAddresses.delete(key)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Convert batch keys back to DataKey format
|
|
91
|
+
const dataKeys: DataKey[] = batch.map((key) => {
|
|
92
|
+
return key as DataKey
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
// Fetch data using shared implementation with cache
|
|
97
|
+
const results = await fetchMultipleAddresses(
|
|
98
|
+
dataKeys,
|
|
99
|
+
this.graphqlEndpoint,
|
|
100
|
+
this.cache
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
// Convert to array for injection
|
|
104
|
+
const enhancedData: EnhancedInfo[] = Array.from(results.values())
|
|
105
|
+
|
|
106
|
+
// Inject data into model
|
|
107
|
+
if (enhancedData.length > 0) {
|
|
108
|
+
this.dataModel.injectData(enhancedData)
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error('Failed to resolve addresses:', error)
|
|
112
|
+
|
|
113
|
+
// Mark addresses as having errors
|
|
114
|
+
for (const key of dataKeys) {
|
|
115
|
+
this.dataModel.setError(key, 'Failed to fetch address data')
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Continue processing if more pending
|
|
120
|
+
if (this.pendingAddresses.size > 0) {
|
|
121
|
+
this.batchTimer = setTimeout(() => {
|
|
122
|
+
this.processBatch()
|
|
123
|
+
}, this.batchDelay)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Stop watching for addresses
|
|
129
|
+
*/
|
|
130
|
+
stopWatching() {
|
|
131
|
+
if (this.batchTimer) {
|
|
132
|
+
clearTimeout(this.batchTimer)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|