@lukso/transaction-view-headless 0.2.2-dev.a8c9315
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/.turbo/turbo-build.log +51 -0
- package/CHANGELOG.md +88 -0
- package/COMPONENT_ARCHITECTURE.md +190 -0
- package/COMPONENT_CONVERSION_ANALYSIS.md +371 -0
- package/CONTEXT_VARIANT_GUIDE.md +425 -0
- package/LAZY_LOADING_GUIDE.md +534 -0
- package/LICENSE +201 -0
- package/PLAYBACK_EXAMPLES.md +658 -0
- package/PLAYBACK_GUIDE.md +402 -0
- package/README.md +43 -0
- package/REGISTRY_SUMMARY.md +638 -0
- package/SUFFERING_ECONOMICS.md +346 -0
- package/SUFFERING_ECONOMICS.zip +0 -0
- package/VIEW_REGISTRATION_GUIDE.md +795 -0
- package/dist/composables/index.cjs +1321 -0
- package/dist/composables/index.cjs.map +1 -0
- package/dist/composables/index.d.cts +6 -0
- package/dist/composables/index.d.ts +6 -0
- package/dist/composables/index.js +1276 -0
- package/dist/composables/index.js.map +1 -0
- package/dist/index-CMLU1hKL.d.ts +374 -0
- package/dist/index-CSDjhiKV.d.cts +374 -0
- package/dist/index.cjs +1534 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +93 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.js +1463 -0
- package/dist/index.js.map +1 -0
- package/dist/registry/index.cjs +522 -0
- package/dist/registry/index.cjs.map +1 -0
- package/dist/registry/index.d.cts +46 -0
- package/dist/registry/index.d.ts +46 -0
- package/dist/registry/index.js +487 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/types/index.cjs +19 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.cts +203 -0
- package/dist/types/index.d.ts +203 -0
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/index.cjs +217 -0
- package/dist/utils/index.cjs.map +1 -0
- package/dist/utils/index.d.cts +47 -0
- package/dist/utils/index.d.ts +47 -0
- package/dist/utils/index.js +188 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/view-loader-dD86QAlQ.d.cts +121 -0
- package/dist/view-loader-ii8AxiQR.d.ts +121 -0
- package/package.json +64 -0
- package/publish.log +0 -0
- package/src/composables/index.ts +59 -0
- package/src/composables/use-image-loader.ts +315 -0
- package/src/composables/use-transaction-playback.ts +396 -0
- package/src/composables/use-transaction-view.ts +309 -0
- package/src/config/lukso-config.ts +171 -0
- package/src/examples/context-aware-views.ts +300 -0
- package/src/examples/example-views.ts +197 -0
- package/src/examples/lit-plus-rn-strategy.ts +448 -0
- package/src/examples/lsp7-custom-matcher-examples.ts +174 -0
- package/src/examples/opt-in-context-examples.ts +372 -0
- package/src/examples/optimized-view-definitions.ts +408 -0
- package/src/examples/vue-to-lit-compilation.ts +358 -0
- package/src/hooks/useAddressQuery.ts +135 -0
- package/src/hooks/useAddressResolution.ts +437 -0
- package/src/index.ts +36 -0
- package/src/registry/index.ts +13 -0
- package/src/registry/view-loader.ts +305 -0
- package/src/registry/view-registry.ts +135 -0
- package/src/types/index.ts +16 -0
- package/src/types/view-contexts.ts +72 -0
- package/src/types/view-matching.ts +173 -0
- package/src/utils/context-matcher.ts +165 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/view-matcher.ts +338 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +34 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AddressIdentityCache,
|
|
3
|
+
DataKey,
|
|
4
|
+
DecoderResult,
|
|
5
|
+
EnhancedInfo,
|
|
6
|
+
} from '@lukso/transaction-decoder'
|
|
7
|
+
import {
|
|
8
|
+
fetchMultipleAddresses,
|
|
9
|
+
getGraphQLEndpoint,
|
|
10
|
+
} from '@lukso/transaction-decoder'
|
|
11
|
+
import type { Chain } from 'viem'
|
|
12
|
+
import { getGlobalLoader, ViewLoadingError } from '../registry/view-loader'
|
|
13
|
+
import { getGlobalRegistry } from '../registry/view-registry'
|
|
14
|
+
import type {
|
|
15
|
+
ViewMatch,
|
|
16
|
+
ViewMatchOptions,
|
|
17
|
+
ViewRegistry,
|
|
18
|
+
} from '../types/view-matching'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Options for the transaction view composable
|
|
22
|
+
*/
|
|
23
|
+
export interface UseTransactionViewOptions extends Partial<ViewMatchOptions> {
|
|
24
|
+
/** Custom registry to use instead of global */
|
|
25
|
+
registry?: ViewRegistry
|
|
26
|
+
/** Whether to automatically load the best matching component */
|
|
27
|
+
autoLoad?: boolean
|
|
28
|
+
/** Fallback view ID to use if no matches found */
|
|
29
|
+
fallbackViewId?: string
|
|
30
|
+
/** Whether to resolve addresses for this view (uses transaction.addresses array) */
|
|
31
|
+
resolve?: boolean
|
|
32
|
+
/** Chain for address resolution (required if resolve: true) */
|
|
33
|
+
chain?: Chain
|
|
34
|
+
/** Optional cache adapter for address resolution */
|
|
35
|
+
addressCache?: AddressIdentityCache
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Result from the transaction view composable
|
|
40
|
+
*/
|
|
41
|
+
export interface UseTransactionViewResult {
|
|
42
|
+
/** All matching views sorted by score */
|
|
43
|
+
matches: ViewMatch[]
|
|
44
|
+
/** The best matching view (highest score) */
|
|
45
|
+
bestMatch: ViewMatch | null
|
|
46
|
+
/** The loaded component (if autoLoad is true) */
|
|
47
|
+
component: any
|
|
48
|
+
/** Loading state */
|
|
49
|
+
isLoading: boolean
|
|
50
|
+
/** Error state */
|
|
51
|
+
error: ViewLoadingError | null
|
|
52
|
+
/** Address resolution loading state */
|
|
53
|
+
isResolvingAddresses: boolean
|
|
54
|
+
/** Resolved address data map (if resolve: true) */
|
|
55
|
+
resolvedAddresses: Record<string, EnhancedInfo>
|
|
56
|
+
/** Function to manually load a specific match */
|
|
57
|
+
loadMatch: (match: ViewMatch) => Promise<any>
|
|
58
|
+
/** Function to load the best match */
|
|
59
|
+
loadBestMatch: () => Promise<any>
|
|
60
|
+
/** Function to refresh matches (useful if registry changes) */
|
|
61
|
+
refreshMatches: () => void
|
|
62
|
+
/** Function to manually trigger address resolution */
|
|
63
|
+
resolveAddresses: () => Promise<void>
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Framework-agnostic composable for finding and loading transaction views
|
|
68
|
+
*
|
|
69
|
+
* This is the main entry point for the view system. It handles:
|
|
70
|
+
* - Finding matching views based on transaction data
|
|
71
|
+
* - Loading components using the appropriate strategy
|
|
72
|
+
* - Providing a consistent API across all frameworks
|
|
73
|
+
*
|
|
74
|
+
* Usage in Vue:
|
|
75
|
+
* ```ts
|
|
76
|
+
* const { bestMatch, component, isLoading, resolvedAddresses } = useTransactionView(transaction, {
|
|
77
|
+
* framework: 'lit',
|
|
78
|
+
* autoLoad: true,
|
|
79
|
+
* resolve: true, // Auto-resolve addresses from transaction.addresses array
|
|
80
|
+
* chain: lukso, // Required for address resolution
|
|
81
|
+
* addressCache: cache // Optional cache adapter
|
|
82
|
+
* })
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* Usage in React/React Native:
|
|
86
|
+
* ```ts
|
|
87
|
+
* const { bestMatch, component, loadBestMatch, resolvedAddresses, isResolvingAddresses } = useTransactionView(transaction, {
|
|
88
|
+
* framework: 'react-native',
|
|
89
|
+
* resolve: true,
|
|
90
|
+
* chain: luksoTestnet,
|
|
91
|
+
* addressCache: myCache
|
|
92
|
+
* })
|
|
93
|
+
* useEffect(() => {
|
|
94
|
+
* loadBestMatch()
|
|
95
|
+
* }, [transaction])
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export function useTransactionView(
|
|
99
|
+
transaction: DecoderResult,
|
|
100
|
+
options: UseTransactionViewOptions
|
|
101
|
+
): UseTransactionViewResult {
|
|
102
|
+
const {
|
|
103
|
+
registry = getGlobalRegistry(),
|
|
104
|
+
autoLoad = false,
|
|
105
|
+
fallbackViewId,
|
|
106
|
+
framework = 'lit',
|
|
107
|
+
minScore = 0,
|
|
108
|
+
maxMatches,
|
|
109
|
+
includeFrameworkConfig = true,
|
|
110
|
+
resolve = false,
|
|
111
|
+
chain,
|
|
112
|
+
addressCache,
|
|
113
|
+
} = options
|
|
114
|
+
|
|
115
|
+
const loader = getGlobalLoader()
|
|
116
|
+
|
|
117
|
+
// Internal state (framework-specific implementations will handle reactivity)
|
|
118
|
+
let matches: ViewMatch[] = []
|
|
119
|
+
let bestMatch: ViewMatch | null = null
|
|
120
|
+
let component: any = null
|
|
121
|
+
let isLoading = false
|
|
122
|
+
let error: ViewLoadingError | null = null
|
|
123
|
+
let isResolvingAddresses = false
|
|
124
|
+
let resolvedAddresses: Record<string, EnhancedInfo> = {}
|
|
125
|
+
|
|
126
|
+
// Find matching views
|
|
127
|
+
const refreshMatches = () => {
|
|
128
|
+
const matchOptions: ViewMatchOptions = {
|
|
129
|
+
framework: framework as any,
|
|
130
|
+
minScore,
|
|
131
|
+
maxMatches,
|
|
132
|
+
includeFrameworkConfig,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
matches = registry.findMatches(transaction, matchOptions)
|
|
136
|
+
bestMatch = matches[0] || null
|
|
137
|
+
|
|
138
|
+
// If no matches and we have a fallback, try to get it
|
|
139
|
+
if (!bestMatch && fallbackViewId) {
|
|
140
|
+
const fallbackView = registry.getView(fallbackViewId)
|
|
141
|
+
if (fallbackView && fallbackView.frameworks[framework]) {
|
|
142
|
+
bestMatch = {
|
|
143
|
+
view: fallbackView,
|
|
144
|
+
score: 0,
|
|
145
|
+
matchedCriteria: [],
|
|
146
|
+
frameworkConfig: includeFrameworkConfig
|
|
147
|
+
? fallbackView.frameworks[framework]
|
|
148
|
+
: undefined,
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Load a specific match
|
|
155
|
+
const loadMatch = async (match: ViewMatch): Promise<any> => {
|
|
156
|
+
if (!match.frameworkConfig) {
|
|
157
|
+
throw new ViewLoadingError(
|
|
158
|
+
`No framework config for view "${match.view.id}" and framework "${framework}"`,
|
|
159
|
+
{ type: 'dynamic-import', path: '' },
|
|
160
|
+
{} as any
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
isLoading = true
|
|
165
|
+
error = null
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
const loadedComponent = await loader.loadFromMatch(match)
|
|
169
|
+
component = loadedComponent
|
|
170
|
+
return loadedComponent
|
|
171
|
+
} catch (err) {
|
|
172
|
+
error =
|
|
173
|
+
err instanceof ViewLoadingError
|
|
174
|
+
? err
|
|
175
|
+
: new ViewLoadingError(
|
|
176
|
+
'Unknown loading error',
|
|
177
|
+
match.frameworkConfig.loader,
|
|
178
|
+
match.frameworkConfig,
|
|
179
|
+
err instanceof Error ? err : new Error(String(err))
|
|
180
|
+
)
|
|
181
|
+
throw error
|
|
182
|
+
} finally {
|
|
183
|
+
isLoading = false
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Load the best match
|
|
188
|
+
const loadBestMatch = async (): Promise<any> => {
|
|
189
|
+
if (!bestMatch) {
|
|
190
|
+
throw new Error('No matching view found for transaction')
|
|
191
|
+
}
|
|
192
|
+
return loadMatch(bestMatch)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Address resolution function
|
|
196
|
+
const resolveAddressesFunc = async (): Promise<void> => {
|
|
197
|
+
if (
|
|
198
|
+
!resolve ||
|
|
199
|
+
!transaction.addresses ||
|
|
200
|
+
transaction.addresses.length === 0
|
|
201
|
+
) {
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!chain) {
|
|
206
|
+
console.warn(
|
|
207
|
+
'Address resolution requested but no chain provided. Pass chain option to resolve addresses.'
|
|
208
|
+
)
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
isResolvingAddresses = true
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
// Convert addresses to DataKey format
|
|
216
|
+
const dataKeys: DataKey[] = transaction.addresses
|
|
217
|
+
.filter((addr) => addr && typeof addr === 'string')
|
|
218
|
+
.map((addr) => addr.toLowerCase() as DataKey)
|
|
219
|
+
|
|
220
|
+
if (dataKeys.length === 0) {
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Get GraphQL endpoint based on chain
|
|
225
|
+
const endpoint = getGraphQLEndpoint(chain)
|
|
226
|
+
|
|
227
|
+
// Use raw fetchMultipleAddresses with optional cache
|
|
228
|
+
const resolvedMap = await fetchMultipleAddresses(
|
|
229
|
+
dataKeys,
|
|
230
|
+
endpoint,
|
|
231
|
+
addressCache
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
// Convert Map to Record for easier consumption
|
|
235
|
+
const resolved: Record<string, EnhancedInfo> = {}
|
|
236
|
+
resolvedMap.forEach((enhancedInfo, dataKey) => {
|
|
237
|
+
resolved[dataKey] = enhancedInfo
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
resolvedAddresses = resolved
|
|
241
|
+
} catch (err) {
|
|
242
|
+
console.error('Address resolution failed:', err)
|
|
243
|
+
} finally {
|
|
244
|
+
isResolvingAddresses = false
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Initialize
|
|
249
|
+
refreshMatches()
|
|
250
|
+
|
|
251
|
+
// Auto-resolve addresses if requested
|
|
252
|
+
if (resolve) {
|
|
253
|
+
resolveAddressesFunc().catch((err) => {
|
|
254
|
+
console.error('Auto address resolution failed:', err)
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Auto-load if requested
|
|
259
|
+
if (autoLoad && bestMatch) {
|
|
260
|
+
loadBestMatch().catch((err) => {
|
|
261
|
+
console.error('Auto-load failed:', err)
|
|
262
|
+
})
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
matches,
|
|
267
|
+
bestMatch,
|
|
268
|
+
component,
|
|
269
|
+
isLoading,
|
|
270
|
+
error,
|
|
271
|
+
isResolvingAddresses,
|
|
272
|
+
resolvedAddresses,
|
|
273
|
+
loadMatch,
|
|
274
|
+
loadBestMatch,
|
|
275
|
+
refreshMatches,
|
|
276
|
+
resolveAddresses: resolveAddressesFunc,
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Simplified version that just returns the best matching view without loading
|
|
282
|
+
*/
|
|
283
|
+
export function findBestTransactionView(
|
|
284
|
+
transaction: DecoderResult,
|
|
285
|
+
framework: 'lit' | 'rn',
|
|
286
|
+
registry?: ViewRegistry
|
|
287
|
+
): ViewMatch | null {
|
|
288
|
+
const reg = registry || getGlobalRegistry()
|
|
289
|
+
const matches = reg.findMatches(transaction, {
|
|
290
|
+
framework,
|
|
291
|
+
includeFrameworkConfig: true,
|
|
292
|
+
})
|
|
293
|
+
return matches.length > 0 ? matches[0] : null
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Get all matching views without loading
|
|
298
|
+
*/
|
|
299
|
+
export function findAllTransactionViews(
|
|
300
|
+
transaction: DecoderResult,
|
|
301
|
+
framework: 'lit' | 'rn',
|
|
302
|
+
registry?: ViewRegistry
|
|
303
|
+
): ViewMatch[] {
|
|
304
|
+
const reg = registry || getGlobalRegistry()
|
|
305
|
+
return reg.findMatches(transaction, {
|
|
306
|
+
framework,
|
|
307
|
+
includeFrameworkConfig: true,
|
|
308
|
+
})
|
|
309
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-agnostic LUKSO configuration system
|
|
3
|
+
* Works across Vue, React, and Lit components
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { signal } from '@preact/signals-core'
|
|
7
|
+
import type { Chain } from 'viem'
|
|
8
|
+
|
|
9
|
+
export interface LuksoConfig {
|
|
10
|
+
/** Chain ID for LUKSO network (42 for mainnet, 4201 for testnet) */
|
|
11
|
+
chainId: number
|
|
12
|
+
/** RPC endpoint for the LUKSO network */
|
|
13
|
+
rpcUrl: string
|
|
14
|
+
/** GraphQL endpoint for address resolution */
|
|
15
|
+
graphqlEndpoint?: string
|
|
16
|
+
/** IPFS gateway for profile images and metadata */
|
|
17
|
+
ipfsGateway?: string
|
|
18
|
+
/** Enable/disable address resolution */
|
|
19
|
+
enableAddressResolution?: boolean
|
|
20
|
+
/** Cache duration for resolved addresses (in milliseconds) */
|
|
21
|
+
cacheDuration?: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const DEFAULT_CONFIG: LuksoConfig = {
|
|
25
|
+
chainId: 42, // LUKSO mainnet
|
|
26
|
+
rpcUrl: 'https://rpc.lukso.network',
|
|
27
|
+
graphqlEndpoint: '/api/graphql', // Use local API proxy
|
|
28
|
+
ipfsGateway: 'https://api.universalprofile.cloud/ipfs',
|
|
29
|
+
enableAddressResolution: true,
|
|
30
|
+
cacheDuration: 5 * 60 * 1000, // 5 minutes
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let globalConfig: LuksoConfig = { ...DEFAULT_CONFIG }
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Set global LUKSO configuration
|
|
37
|
+
* Call this once to configure all transaction components
|
|
38
|
+
*/
|
|
39
|
+
export function setLuksoConfig(config: Partial<LuksoConfig>): void {
|
|
40
|
+
globalConfig = { ...globalConfig, ...config }
|
|
41
|
+
|
|
42
|
+
// Clear any existing address resolution cache when config changes
|
|
43
|
+
clearAddressCache()
|
|
44
|
+
|
|
45
|
+
if (typeof window !== 'undefined' && window.console) {
|
|
46
|
+
console.log('🌐 LUKSO Config Updated:', {
|
|
47
|
+
chainId: globalConfig.chainId,
|
|
48
|
+
network:
|
|
49
|
+
globalConfig.chainId === 42
|
|
50
|
+
? 'mainnet'
|
|
51
|
+
: globalConfig.chainId === 4201
|
|
52
|
+
? 'testnet'
|
|
53
|
+
: 'custom',
|
|
54
|
+
addressResolution: globalConfig.enableAddressResolution,
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get current LUKSO configuration
|
|
61
|
+
*/
|
|
62
|
+
export function getLuksoConfig(): LuksoConfig {
|
|
63
|
+
return { ...globalConfig }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Quick helper to set mainnet configuration
|
|
68
|
+
*/
|
|
69
|
+
export function useLuksoMainnet(): void {
|
|
70
|
+
setLuksoConfig({
|
|
71
|
+
chainId: 42,
|
|
72
|
+
rpcUrl: 'https://rpc.lukso.network',
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Quick helper to set testnet configuration
|
|
78
|
+
*/
|
|
79
|
+
export function useLuksoTestnet(): void {
|
|
80
|
+
setLuksoConfig({
|
|
81
|
+
chainId: 4201,
|
|
82
|
+
rpcUrl: 'https://rpc.testnet.lukso.network',
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Address resolution cache
|
|
88
|
+
*/
|
|
89
|
+
const addressCache = new Map<string, any>()
|
|
90
|
+
const cacheTimestamps = new Map<string, number>()
|
|
91
|
+
|
|
92
|
+
export function cacheAddress(address: string, data: any): void {
|
|
93
|
+
if (!globalConfig.enableAddressResolution) return
|
|
94
|
+
|
|
95
|
+
const key = address.toLowerCase()
|
|
96
|
+
addressCache.set(key, data)
|
|
97
|
+
cacheTimestamps.set(key, Date.now())
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function getCachedAddress(address: string): any | null {
|
|
101
|
+
if (!globalConfig.enableAddressResolution) return null
|
|
102
|
+
|
|
103
|
+
const key = address.toLowerCase()
|
|
104
|
+
const data = addressCache.get(key)
|
|
105
|
+
const timestamp = cacheTimestamps.get(key)
|
|
106
|
+
|
|
107
|
+
if (!data || !timestamp) return null
|
|
108
|
+
|
|
109
|
+
// Check if cache is expired
|
|
110
|
+
if (Date.now() - timestamp > globalConfig.cacheDuration!) {
|
|
111
|
+
addressCache.delete(key)
|
|
112
|
+
cacheTimestamps.delete(key)
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return data
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function clearAddressCache(): void {
|
|
120
|
+
addressCache.clear()
|
|
121
|
+
cacheTimestamps.clear()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Check if address resolution is enabled
|
|
126
|
+
*/
|
|
127
|
+
export function isAddressResolutionEnabled(): boolean {
|
|
128
|
+
return globalConfig.enableAddressResolution ?? true
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Current chain signal for reactive chain state
|
|
133
|
+
*/
|
|
134
|
+
export const currentChainSignal = signal<Chain | null>(null)
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Set current chain signal from viem chain object
|
|
138
|
+
*/
|
|
139
|
+
export function setCurrentChain(chain: Chain): void {
|
|
140
|
+
currentChainSignal.value = chain
|
|
141
|
+
|
|
142
|
+
// Also update the legacy global config for backwards compatibility
|
|
143
|
+
setLuksoConfig({
|
|
144
|
+
chainId: chain.id,
|
|
145
|
+
rpcUrl: chain.rpcUrls.default.http[0],
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get current chain, falling back to chain derived from global config
|
|
151
|
+
*/
|
|
152
|
+
export function getCurrentChain(): Chain | null {
|
|
153
|
+
return currentChainSignal.value
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Initialize chain signal from global config
|
|
158
|
+
*/
|
|
159
|
+
export async function initializeChainSignal(): Promise<void> {
|
|
160
|
+
if (currentChainSignal.value) return
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const { lukso, luksoTestnet } = await import('viem/chains')
|
|
164
|
+
const config = getLuksoConfig()
|
|
165
|
+
|
|
166
|
+
const chain = config.chainId === 42 ? lukso : luksoTestnet
|
|
167
|
+
currentChainSignal.value = chain
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.warn('Failed to initialize chain signal:', error)
|
|
170
|
+
}
|
|
171
|
+
}
|