@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,322 @@
|
|
|
1
|
+
import type { Hex } from 'viem'
|
|
2
|
+
import type { DecoderPlugin, Info, SchemaPlugin } from './types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Plugin metadata for registry
|
|
6
|
+
*/
|
|
7
|
+
export interface PluginMetadata {
|
|
8
|
+
name: string
|
|
9
|
+
plugin: DecoderPlugin
|
|
10
|
+
priority?: number
|
|
11
|
+
required?: boolean // Cannot be overridden, always runs first
|
|
12
|
+
description?: string
|
|
13
|
+
version?: string
|
|
14
|
+
usesAsync?: boolean // Whether this plugin performs async operations
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Schema plugin metadata
|
|
19
|
+
*/
|
|
20
|
+
export interface SchemaPluginMetadata {
|
|
21
|
+
name: string
|
|
22
|
+
plugin: SchemaPlugin
|
|
23
|
+
priority?: number
|
|
24
|
+
required?: boolean // Cannot be overridden, always runs first
|
|
25
|
+
description?: string
|
|
26
|
+
version?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Global plugin registry for auto-registration
|
|
31
|
+
*/
|
|
32
|
+
class PluginRegistry {
|
|
33
|
+
private plugins: Map<string, PluginMetadata> = new Map()
|
|
34
|
+
private schemaPlugins: Map<string, SchemaPluginMetadata> = new Map()
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Register a decoder plugin
|
|
38
|
+
* @param name - Unique plugin name
|
|
39
|
+
* @param plugin - Plugin instance
|
|
40
|
+
* @param options - Registration options
|
|
41
|
+
*/
|
|
42
|
+
register(
|
|
43
|
+
name: string,
|
|
44
|
+
plugin: DecoderPlugin,
|
|
45
|
+
options?: {
|
|
46
|
+
priority?: number
|
|
47
|
+
required?: boolean
|
|
48
|
+
override?: boolean
|
|
49
|
+
description?: string
|
|
50
|
+
version?: string
|
|
51
|
+
usesAsync?: boolean
|
|
52
|
+
}
|
|
53
|
+
): void {
|
|
54
|
+
const {
|
|
55
|
+
override = false,
|
|
56
|
+
priority = 0,
|
|
57
|
+
required = false,
|
|
58
|
+
description,
|
|
59
|
+
version,
|
|
60
|
+
usesAsync = false,
|
|
61
|
+
} = options || {}
|
|
62
|
+
|
|
63
|
+
const existing = this.plugins.get(name)
|
|
64
|
+
|
|
65
|
+
if (existing) {
|
|
66
|
+
if (existing.required) {
|
|
67
|
+
console.error(
|
|
68
|
+
`Plugin "${name}" is marked as required and cannot be overridden.`
|
|
69
|
+
)
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
if (!override) {
|
|
73
|
+
console.warn(
|
|
74
|
+
`Plugin "${name}" is already registered. Use override: true to replace.`
|
|
75
|
+
)
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this.plugins.set(name, {
|
|
81
|
+
name,
|
|
82
|
+
plugin: Object.freeze(plugin),
|
|
83
|
+
priority,
|
|
84
|
+
required,
|
|
85
|
+
description,
|
|
86
|
+
version,
|
|
87
|
+
usesAsync,
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Register a schema plugin
|
|
93
|
+
* @param name - Unique plugin name
|
|
94
|
+
* @param plugin - Schema plugin instance
|
|
95
|
+
* @param options - Registration options
|
|
96
|
+
*/
|
|
97
|
+
registerSchema(
|
|
98
|
+
name: string,
|
|
99
|
+
plugin: SchemaPlugin,
|
|
100
|
+
options?: {
|
|
101
|
+
priority?: number
|
|
102
|
+
required?: boolean
|
|
103
|
+
override?: boolean
|
|
104
|
+
description?: string
|
|
105
|
+
version?: string
|
|
106
|
+
}
|
|
107
|
+
): void {
|
|
108
|
+
const {
|
|
109
|
+
override = false,
|
|
110
|
+
priority = 0,
|
|
111
|
+
required = false,
|
|
112
|
+
description,
|
|
113
|
+
version,
|
|
114
|
+
} = options || {}
|
|
115
|
+
|
|
116
|
+
const existing = this.schemaPlugins.get(name)
|
|
117
|
+
|
|
118
|
+
if (existing) {
|
|
119
|
+
if (existing.required) {
|
|
120
|
+
console.error(
|
|
121
|
+
`Schema plugin "${name}" is marked as required and cannot be overridden.`
|
|
122
|
+
)
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
if (!override) {
|
|
126
|
+
console.warn(
|
|
127
|
+
`Schema plugin "${name}" is already registered. Use override: true to replace.`
|
|
128
|
+
)
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
this.schemaPlugins.set(name, {
|
|
134
|
+
name,
|
|
135
|
+
plugin: Object.freeze(plugin),
|
|
136
|
+
priority,
|
|
137
|
+
required,
|
|
138
|
+
description,
|
|
139
|
+
version,
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Unregister a plugin
|
|
145
|
+
*/
|
|
146
|
+
unregister(name: string): boolean {
|
|
147
|
+
return this.plugins.delete(name)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get all registered decoder plugins (excluding overridden ones)
|
|
152
|
+
* @param options - Filter options
|
|
153
|
+
*/
|
|
154
|
+
async getAll(options?: {
|
|
155
|
+
excludeNames?: string[]
|
|
156
|
+
syncOnly?: boolean
|
|
157
|
+
}): Promise<DecoderPlugin[]> {
|
|
158
|
+
const { excludeNames = [], syncOnly = false } = options || {}
|
|
159
|
+
const exclude = new Set(excludeNames)
|
|
160
|
+
let plugins = Array.from(this.plugins.values())
|
|
161
|
+
|
|
162
|
+
// Filter by syncOnly if requested
|
|
163
|
+
if (syncOnly) {
|
|
164
|
+
plugins = plugins.filter((meta) => !meta.usesAsync)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Check if any required plugins are being excluded
|
|
168
|
+
for (const name of exclude) {
|
|
169
|
+
const plugin = this.plugins.get(name)
|
|
170
|
+
if (plugin?.required) {
|
|
171
|
+
console.warn(
|
|
172
|
+
`Warning: Plugin "${name}" is required and cannot be excluded. It will run first.`
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Separate required and optional plugins
|
|
178
|
+
return plugins
|
|
179
|
+
.sort((a, b) => (b.priority || 0) - (a.priority || 0))
|
|
180
|
+
.map(({ plugin }) => plugin)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Get all schema plugins (excluding overridden ones)
|
|
185
|
+
* @param excludeNames - Plugin names to exclude
|
|
186
|
+
*/
|
|
187
|
+
getAllSchema(excludeNames?: string[]): SchemaPlugin[] {
|
|
188
|
+
const exclude = new Set(excludeNames || [])
|
|
189
|
+
const plugins = Array.from(this.schemaPlugins.values())
|
|
190
|
+
|
|
191
|
+
// Check if any required plugins are being excluded
|
|
192
|
+
for (const name of exclude) {
|
|
193
|
+
const plugin = this.schemaPlugins.get(name)
|
|
194
|
+
if (plugin?.required) {
|
|
195
|
+
console.warn(
|
|
196
|
+
`Warning: Schema plugin "${name}" is required and cannot be excluded. It will run first.`
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Separate required and optional plugins
|
|
202
|
+
const required = plugins
|
|
203
|
+
.filter((meta) => meta.required)
|
|
204
|
+
.sort((a, b) => (b.priority || 0) - (a.priority || 0))
|
|
205
|
+
|
|
206
|
+
const optional = plugins
|
|
207
|
+
.filter((meta) => !meta.required && !exclude.has(meta.name))
|
|
208
|
+
.sort((a, b) => (b.priority || 0) - (a.priority || 0))
|
|
209
|
+
|
|
210
|
+
// Required plugins always come first, then optional ones
|
|
211
|
+
return [...required, ...optional].map((meta) => meta.plugin)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Get a specific plugin by name
|
|
216
|
+
*/
|
|
217
|
+
async get(name: string): Promise<DecoderPlugin | undefined> {
|
|
218
|
+
const meta = this.plugins.get(name)
|
|
219
|
+
return meta?.plugin
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Get plugin metadata
|
|
224
|
+
*/
|
|
225
|
+
getMetadata(name: string): PluginMetadata | undefined {
|
|
226
|
+
return this.plugins.get(name)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get schema plugin metadata
|
|
231
|
+
*/
|
|
232
|
+
getSchemaMetadata(name: string): SchemaPluginMetadata | undefined {
|
|
233
|
+
return this.schemaPlugins.get(name)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Check if a plugin is registered
|
|
238
|
+
*/
|
|
239
|
+
has(name: string): boolean {
|
|
240
|
+
return this.plugins.has(name)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Clear all plugins
|
|
245
|
+
*/
|
|
246
|
+
clear(): void {
|
|
247
|
+
this.plugins.clear()
|
|
248
|
+
this.schemaPlugins.clear()
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Get the number of registered plugins
|
|
253
|
+
*/
|
|
254
|
+
get size(): number {
|
|
255
|
+
return this.plugins.size + this.schemaPlugins.size
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* List all registered plugins with metadata
|
|
260
|
+
*/
|
|
261
|
+
list(): { plugins: PluginMetadata[]; schemaPlugins: SchemaPluginMetadata[] } {
|
|
262
|
+
return {
|
|
263
|
+
plugins: Array.from(this.plugins.values()),
|
|
264
|
+
schemaPlugins: Array.from(this.schemaPlugins.values()),
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Freeze the PluginRegistry prototype to prevent modifications
|
|
270
|
+
Object.freeze(PluginRegistry.prototype)
|
|
271
|
+
Object.freeze(PluginRegistry)
|
|
272
|
+
|
|
273
|
+
// Global singleton instance
|
|
274
|
+
export const pluginRegistry = new PluginRegistry()
|
|
275
|
+
|
|
276
|
+
// Convenience function for plugin authors
|
|
277
|
+
export function registerPlugin(
|
|
278
|
+
name: string,
|
|
279
|
+
plugin: DecoderPlugin,
|
|
280
|
+
options?: {
|
|
281
|
+
priority?: number
|
|
282
|
+
required?: boolean
|
|
283
|
+
override?: boolean
|
|
284
|
+
description?: string
|
|
285
|
+
version?: string
|
|
286
|
+
usesAsync?: boolean
|
|
287
|
+
}
|
|
288
|
+
): void {
|
|
289
|
+
pluginRegistry.register(name, plugin, options)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Convenience function for schema plugin authors
|
|
293
|
+
export function registerSchemaPlugin(
|
|
294
|
+
name: string,
|
|
295
|
+
plugin: SchemaPlugin,
|
|
296
|
+
options?: {
|
|
297
|
+
priority?: number
|
|
298
|
+
required?: boolean
|
|
299
|
+
override?: boolean
|
|
300
|
+
description?: string
|
|
301
|
+
version?: string
|
|
302
|
+
}
|
|
303
|
+
): void {
|
|
304
|
+
pluginRegistry.registerSchema(name, plugin, options)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Helper to create a named schema plugin
|
|
308
|
+
export function createNamedSchemaPlugin(
|
|
309
|
+
name: string,
|
|
310
|
+
plugin: (key: Hex, value: Hex) => Promise<Info | undefined>
|
|
311
|
+
): SchemaPlugin {
|
|
312
|
+
const namedPlugin = Object.assign(plugin, { name })
|
|
313
|
+
return namedPlugin as SchemaPlugin
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Helper to create a named decoder plugin
|
|
317
|
+
export function createNamedPlugin(
|
|
318
|
+
name: string,
|
|
319
|
+
plugin: Omit<DecoderPlugin, 'name'>
|
|
320
|
+
): DecoderPlugin {
|
|
321
|
+
return { ...plugin, name }
|
|
322
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import request, { gql } from 'graphql-request'
|
|
2
|
+
import { type Address, type Chain, getAddress } from 'viem'
|
|
3
|
+
import { lukso } from 'viem/chains'
|
|
4
|
+
import type { EnhancedInfo } from '../types'
|
|
5
|
+
import { getWSClient } from './graphqlWS'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get controllers from a set of profile addresses
|
|
9
|
+
*/
|
|
10
|
+
const singleProfileControllersGql = gql`
|
|
11
|
+
query MyQuery($profileAddress: String = "") {
|
|
12
|
+
Profile_by_pk(id: $profileAddress) {
|
|
13
|
+
controllers {
|
|
14
|
+
address
|
|
15
|
+
tags
|
|
16
|
+
permissions
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
`
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get a single profile from it's address
|
|
24
|
+
*/
|
|
25
|
+
const singleProfileGql = gql`
|
|
26
|
+
query MyQuery($profileAddress: String = "") {
|
|
27
|
+
Profile_by_pk(id: $profileAddress) {
|
|
28
|
+
fullName
|
|
29
|
+
id
|
|
30
|
+
name
|
|
31
|
+
tags
|
|
32
|
+
description
|
|
33
|
+
links {
|
|
34
|
+
title
|
|
35
|
+
url
|
|
36
|
+
}
|
|
37
|
+
profileImages(
|
|
38
|
+
where: { error: { _is_null: true } }
|
|
39
|
+
order_by: { width: asc }
|
|
40
|
+
) {
|
|
41
|
+
width
|
|
42
|
+
src
|
|
43
|
+
verified
|
|
44
|
+
}
|
|
45
|
+
backgroundImages(
|
|
46
|
+
where: { error: { _is_null: true } }
|
|
47
|
+
order_by: { width: asc }
|
|
48
|
+
) {
|
|
49
|
+
width
|
|
50
|
+
src
|
|
51
|
+
verified
|
|
52
|
+
}
|
|
53
|
+
avatars(where: { error: { _is_null: true } }, order_by: { width: asc }) {
|
|
54
|
+
width
|
|
55
|
+
src
|
|
56
|
+
verified
|
|
57
|
+
}
|
|
58
|
+
followed {
|
|
59
|
+
follower {
|
|
60
|
+
id
|
|
61
|
+
name
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
followed_aggregate {
|
|
65
|
+
aggregate {
|
|
66
|
+
count
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
following_aggregate {
|
|
70
|
+
aggregate {
|
|
71
|
+
count
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
following {
|
|
75
|
+
followee {
|
|
76
|
+
id
|
|
77
|
+
name
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
controllers {
|
|
81
|
+
address
|
|
82
|
+
tags
|
|
83
|
+
permissions
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
`
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Subscribe to a single profile by it's address
|
|
91
|
+
*/
|
|
92
|
+
const singleProfileGqlSub = gql`
|
|
93
|
+
subscription MyQuery($profileAddress: String = "") {
|
|
94
|
+
Profile_by_pk(id: $profileAddress) {
|
|
95
|
+
fullName
|
|
96
|
+
id
|
|
97
|
+
name
|
|
98
|
+
tags
|
|
99
|
+
description
|
|
100
|
+
links {
|
|
101
|
+
title
|
|
102
|
+
url
|
|
103
|
+
}
|
|
104
|
+
profileImages(
|
|
105
|
+
where: { error: { _is_null: true } }
|
|
106
|
+
order_by: { width: asc }
|
|
107
|
+
) {
|
|
108
|
+
width
|
|
109
|
+
src
|
|
110
|
+
verified
|
|
111
|
+
}
|
|
112
|
+
backgroundImages(
|
|
113
|
+
where: { error: { _is_null: true } }
|
|
114
|
+
order_by: { width: asc }
|
|
115
|
+
) {
|
|
116
|
+
width
|
|
117
|
+
src
|
|
118
|
+
verified
|
|
119
|
+
}
|
|
120
|
+
avatars(where: { error: { _is_null: true } }, order_by: { width: asc }) {
|
|
121
|
+
width
|
|
122
|
+
src
|
|
123
|
+
verified
|
|
124
|
+
}
|
|
125
|
+
followed {
|
|
126
|
+
follower {
|
|
127
|
+
id
|
|
128
|
+
name
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
followed_aggregate {
|
|
132
|
+
aggregate {
|
|
133
|
+
count
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
following_aggregate {
|
|
137
|
+
aggregate {
|
|
138
|
+
count
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
following {
|
|
142
|
+
followee {
|
|
143
|
+
id
|
|
144
|
+
name
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
controllers {
|
|
148
|
+
address
|
|
149
|
+
tags
|
|
150
|
+
permissions
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
`
|
|
155
|
+
|
|
156
|
+
export type SubscribeResponse = {
|
|
157
|
+
data: {
|
|
158
|
+
Profile_by_pk: Record<string, unknown>
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Subscribe to profile by it's address
|
|
164
|
+
*
|
|
165
|
+
* @param chain which chain to use
|
|
166
|
+
* @param address the address of the profile
|
|
167
|
+
* @param profile callback to receive the profile data
|
|
168
|
+
* @returns
|
|
169
|
+
*/
|
|
170
|
+
export function subscribeToProfile(
|
|
171
|
+
chain: Chain,
|
|
172
|
+
address: Address,
|
|
173
|
+
profile?: (data: EnhancedInfo) => void
|
|
174
|
+
) {
|
|
175
|
+
const wsClient = getWSClient(chain)
|
|
176
|
+
let current = undefined as EnhancedInfo | undefined
|
|
177
|
+
return wsClient.subscribe(
|
|
178
|
+
{
|
|
179
|
+
query: singleProfileGqlSub,
|
|
180
|
+
variables: { profileAddress: address.toLowerCase() },
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
next: async (data: SubscribeResponse) => {
|
|
184
|
+
const { data: response } = data
|
|
185
|
+
const { Profile_by_pk: prof } = response as {
|
|
186
|
+
Profile_by_pk: Record<string, unknown>
|
|
187
|
+
}
|
|
188
|
+
const {
|
|
189
|
+
id,
|
|
190
|
+
backgroundImages,
|
|
191
|
+
profileImages,
|
|
192
|
+
avatars,
|
|
193
|
+
links,
|
|
194
|
+
tags,
|
|
195
|
+
controllers,
|
|
196
|
+
...rest
|
|
197
|
+
} = prof || {}
|
|
198
|
+
if (!id) {
|
|
199
|
+
console.error('Profile not found')
|
|
200
|
+
return
|
|
201
|
+
}
|
|
202
|
+
const result = {
|
|
203
|
+
address: getAddress(id as string),
|
|
204
|
+
backgroundImage: backgroundImages || [],
|
|
205
|
+
profileImage: profileImages || [],
|
|
206
|
+
avatar: avatars || [],
|
|
207
|
+
tags: tags || [],
|
|
208
|
+
links: links || [],
|
|
209
|
+
...rest,
|
|
210
|
+
} as EnhancedInfo
|
|
211
|
+
if (!current || JSON.stringify(current) !== JSON.stringify(result)) {
|
|
212
|
+
profile?.(result)
|
|
213
|
+
current = result
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
error: (err: unknown) => {
|
|
217
|
+
console.error('Subscription error:', err)
|
|
218
|
+
},
|
|
219
|
+
complete: () => {},
|
|
220
|
+
}
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Fetch the controllers for a profile address
|
|
226
|
+
*
|
|
227
|
+
* @param chain which chain to use
|
|
228
|
+
* @param address which profile to fetch
|
|
229
|
+
* @returns a list of controller addresses
|
|
230
|
+
*/
|
|
231
|
+
export async function fetchProfileControllers(
|
|
232
|
+
chain: Chain,
|
|
233
|
+
address: Address
|
|
234
|
+
): Promise<`0x${string}`[]> {
|
|
235
|
+
const response: {
|
|
236
|
+
Profile_by_pk: {
|
|
237
|
+
controllers?: { address: `0x${string}`; tags: string[] }[]
|
|
238
|
+
}
|
|
239
|
+
} = await request(
|
|
240
|
+
chain.id === lukso.id
|
|
241
|
+
? 'https://envio.lukso-mainnet.universal.tech/v1/graphql'
|
|
242
|
+
: 'https://envio.lukso-testnet.universal.tech/v1/graphql',
|
|
243
|
+
singleProfileControllersGql,
|
|
244
|
+
{
|
|
245
|
+
profileAddress: address.toLowerCase(),
|
|
246
|
+
}
|
|
247
|
+
)
|
|
248
|
+
const { Profile_by_pk: { controllers = [] } = {} } = response
|
|
249
|
+
return controllers
|
|
250
|
+
.filter((c) => !c.tags?.includes('ISRECOVERY'))
|
|
251
|
+
.map((C) => C.address)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export async function fetchProfile(
|
|
255
|
+
chain: Chain,
|
|
256
|
+
address: Address
|
|
257
|
+
): Promise<EnhancedInfo> {
|
|
258
|
+
const response: {
|
|
259
|
+
Profile_by_pk: Record<string, unknown>
|
|
260
|
+
} = await request(
|
|
261
|
+
chain.id === lukso.id
|
|
262
|
+
? 'https://envio.lukso-mainnet.universal.tech/v1/graphql'
|
|
263
|
+
: 'https://envio.lukso-testnet.universal.tech/v1/graphql',
|
|
264
|
+
singleProfileGql,
|
|
265
|
+
{
|
|
266
|
+
profileAddress: address.toLowerCase(),
|
|
267
|
+
}
|
|
268
|
+
)
|
|
269
|
+
const { Profile_by_pk: profile } = response
|
|
270
|
+
const {
|
|
271
|
+
id,
|
|
272
|
+
backgroundImages,
|
|
273
|
+
profileImages,
|
|
274
|
+
avatars,
|
|
275
|
+
links,
|
|
276
|
+
tags,
|
|
277
|
+
controllers,
|
|
278
|
+
...rest
|
|
279
|
+
} = profile || {}
|
|
280
|
+
if (!id) {
|
|
281
|
+
throw new Error('Profile not found')
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
address: getAddress(id as string),
|
|
285
|
+
backgroundImage: backgroundImages || [],
|
|
286
|
+
profileImage: profileImages || [],
|
|
287
|
+
avatar: avatars || [],
|
|
288
|
+
tags: tags || [],
|
|
289
|
+
links: links || [],
|
|
290
|
+
...rest,
|
|
291
|
+
__gqltype: 'Profile',
|
|
292
|
+
} as EnhancedInfo
|
|
293
|
+
}
|