@chillwhales/lsp4 0.1.1

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Chillwhales contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # @chillwhales/lsp4
2
+
3
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
4
+
5
+ LSP4 Digital Asset Metadata — schemas, types, and utilities for reading and validating LSP7/LSP8 token metadata on LUKSO.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add @chillwhales/lsp4
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```typescript
16
+ import { lsp4MetadataSchema, getAssetDisplayName } from "@chillwhales/lsp4";
17
+
18
+ // Validate raw token metadata fetched from IPFS / on-chain
19
+ const raw = {
20
+ name: "Chillwhale #42",
21
+ description: "A chill whale living on LUKSO",
22
+ category: "Collectible",
23
+ links: [],
24
+ icon: [{ url: "ipfs://QmIcon...", width: 64, height: 64 }],
25
+ images: [[{ url: "ipfs://QmFull...", width: 1024, height: 1024 }]],
26
+ assets: [],
27
+ attributes: [
28
+ { key: "Background", value: "Ocean", type: "string" },
29
+ { key: "Rarity", value: "42", type: "number" },
30
+ ],
31
+ };
32
+
33
+ const metadata = lsp4MetadataSchema.parse(raw);
34
+ const name = getAssetDisplayName(metadata); // "Chillwhale #42"
35
+ ```
36
+
37
+ > **Spec:** [LSP-4 Digital Asset Metadata](https://docs.lukso.tech/standards/tokens/LSP4-Digital-Asset-Metadata)
38
+
39
+ ## API
40
+
41
+ Types are exported and available in your editor via TypeScript IntelliSense.
42
+
43
+ ## License
44
+
45
+ [MIT](./LICENSE)
@@ -0,0 +1,435 @@
1
+ import * as _chillwhales_lsp2 from '@chillwhales/lsp2';
2
+ import { Image } from '@chillwhales/lsp2';
3
+ import { z } from 'zod';
4
+
5
+ /**
6
+ * Asset attribute schema
7
+ * Supports string, number, and boolean attribute types
8
+ */
9
+ declare const attributesSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
10
+ key: z.ZodString;
11
+ value: z.ZodString;
12
+ type: z.ZodLiteral<"string">;
13
+ }, "strip", z.ZodTypeAny, {
14
+ type: "string";
15
+ value: string;
16
+ key: string;
17
+ }, {
18
+ type: "string";
19
+ value: string;
20
+ key: string;
21
+ }>, z.ZodObject<{
22
+ key: z.ZodString;
23
+ value: z.ZodEffects<z.ZodString, string, string>;
24
+ type: z.ZodLiteral<"number">;
25
+ }, "strip", z.ZodTypeAny, {
26
+ type: "number";
27
+ value: string;
28
+ key: string;
29
+ }, {
30
+ type: "number";
31
+ value: string;
32
+ key: string;
33
+ }>, z.ZodObject<{
34
+ key: z.ZodString;
35
+ value: z.ZodBoolean;
36
+ type: z.ZodLiteral<"boolean">;
37
+ }, "strip", z.ZodTypeAny, {
38
+ type: "boolean";
39
+ value: boolean;
40
+ key: string;
41
+ }, {
42
+ type: "boolean";
43
+ value: boolean;
44
+ key: string;
45
+ }>]>;
46
+ /**
47
+ * LSP4 Digital Asset metadata schema
48
+ * Full metadata structure for LSP7/LSP8 tokens
49
+ */
50
+ declare const lsp4MetadataSchema: z.ZodObject<{
51
+ name: z.ZodNullable<z.ZodString>;
52
+ description: z.ZodNullable<z.ZodString>;
53
+ category: z.ZodNullable<z.ZodString>;
54
+ links: z.ZodArray<z.ZodObject<{
55
+ title: z.ZodString;
56
+ url: z.ZodString;
57
+ }, "strip", z.ZodTypeAny, {
58
+ url: string;
59
+ title: string;
60
+ }, {
61
+ url: string;
62
+ title: string;
63
+ }>, "many">;
64
+ icon: z.ZodArray<z.ZodObject<{
65
+ url: z.ZodString;
66
+ width: z.ZodNumber;
67
+ height: z.ZodNumber;
68
+ verification: z.ZodDiscriminatedUnion<"method", [z.ZodObject<{
69
+ data: z.ZodString;
70
+ method: z.ZodEnum<[_chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES, _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8]>;
71
+ }, "strip", z.ZodTypeAny, {
72
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
73
+ data: string;
74
+ }, {
75
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
76
+ data: string;
77
+ }>, z.ZodObject<{
78
+ method: z.ZodEnum<[_chillwhales_lsp2.VERIFICATION_METHODS.ECDSA]>;
79
+ data: z.ZodString;
80
+ source: z.ZodString;
81
+ }, "strip", z.ZodTypeAny, {
82
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
83
+ data: string;
84
+ source: string;
85
+ }, {
86
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
87
+ data: string;
88
+ source: string;
89
+ }>]>;
90
+ }, "strip", z.ZodTypeAny, {
91
+ url: string;
92
+ width: number;
93
+ height: number;
94
+ verification: {
95
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
96
+ data: string;
97
+ } | {
98
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
99
+ data: string;
100
+ source: string;
101
+ };
102
+ }, {
103
+ url: string;
104
+ width: number;
105
+ height: number;
106
+ verification: {
107
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
108
+ data: string;
109
+ } | {
110
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
111
+ data: string;
112
+ source: string;
113
+ };
114
+ }>, "many">;
115
+ images: z.ZodArray<z.ZodArray<z.ZodObject<{
116
+ url: z.ZodString;
117
+ width: z.ZodNumber;
118
+ height: z.ZodNumber;
119
+ verification: z.ZodDiscriminatedUnion<"method", [z.ZodObject<{
120
+ data: z.ZodString;
121
+ method: z.ZodEnum<[_chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES, _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8]>;
122
+ }, "strip", z.ZodTypeAny, {
123
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
124
+ data: string;
125
+ }, {
126
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
127
+ data: string;
128
+ }>, z.ZodObject<{
129
+ method: z.ZodEnum<[_chillwhales_lsp2.VERIFICATION_METHODS.ECDSA]>;
130
+ data: z.ZodString;
131
+ source: z.ZodString;
132
+ }, "strip", z.ZodTypeAny, {
133
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
134
+ data: string;
135
+ source: string;
136
+ }, {
137
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
138
+ data: string;
139
+ source: string;
140
+ }>]>;
141
+ }, "strip", z.ZodTypeAny, {
142
+ url: string;
143
+ width: number;
144
+ height: number;
145
+ verification: {
146
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
147
+ data: string;
148
+ } | {
149
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
150
+ data: string;
151
+ source: string;
152
+ };
153
+ }, {
154
+ url: string;
155
+ width: number;
156
+ height: number;
157
+ verification: {
158
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
159
+ data: string;
160
+ } | {
161
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
162
+ data: string;
163
+ source: string;
164
+ };
165
+ }>, "many">, "many">;
166
+ assets: z.ZodArray<z.ZodObject<{
167
+ url: z.ZodString;
168
+ fileType: z.ZodString;
169
+ verification: z.ZodDiscriminatedUnion<"method", [z.ZodObject<{
170
+ data: z.ZodString;
171
+ method: z.ZodEnum<[_chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES, _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8]>;
172
+ }, "strip", z.ZodTypeAny, {
173
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
174
+ data: string;
175
+ }, {
176
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
177
+ data: string;
178
+ }>, z.ZodObject<{
179
+ method: z.ZodEnum<[_chillwhales_lsp2.VERIFICATION_METHODS.ECDSA]>;
180
+ data: z.ZodString;
181
+ source: z.ZodString;
182
+ }, "strip", z.ZodTypeAny, {
183
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
184
+ data: string;
185
+ source: string;
186
+ }, {
187
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
188
+ data: string;
189
+ source: string;
190
+ }>]>;
191
+ }, "strip", z.ZodTypeAny, {
192
+ url: string;
193
+ verification: {
194
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
195
+ data: string;
196
+ } | {
197
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
198
+ data: string;
199
+ source: string;
200
+ };
201
+ fileType: string;
202
+ }, {
203
+ url: string;
204
+ verification: {
205
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
206
+ data: string;
207
+ } | {
208
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
209
+ data: string;
210
+ source: string;
211
+ };
212
+ fileType: string;
213
+ }>, "many">;
214
+ attributes: z.ZodArray<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
215
+ key: z.ZodString;
216
+ value: z.ZodString;
217
+ type: z.ZodLiteral<"string">;
218
+ }, "strip", z.ZodTypeAny, {
219
+ type: "string";
220
+ value: string;
221
+ key: string;
222
+ }, {
223
+ type: "string";
224
+ value: string;
225
+ key: string;
226
+ }>, z.ZodObject<{
227
+ key: z.ZodString;
228
+ value: z.ZodEffects<z.ZodString, string, string>;
229
+ type: z.ZodLiteral<"number">;
230
+ }, "strip", z.ZodTypeAny, {
231
+ type: "number";
232
+ value: string;
233
+ key: string;
234
+ }, {
235
+ type: "number";
236
+ value: string;
237
+ key: string;
238
+ }>, z.ZodObject<{
239
+ key: z.ZodString;
240
+ value: z.ZodBoolean;
241
+ type: z.ZodLiteral<"boolean">;
242
+ }, "strip", z.ZodTypeAny, {
243
+ type: "boolean";
244
+ value: boolean;
245
+ key: string;
246
+ }, {
247
+ type: "boolean";
248
+ value: boolean;
249
+ key: string;
250
+ }>]>, "many">;
251
+ }, "strip", z.ZodTypeAny, {
252
+ name: string | null;
253
+ description: string | null;
254
+ category: string | null;
255
+ links: {
256
+ url: string;
257
+ title: string;
258
+ }[];
259
+ icon: {
260
+ url: string;
261
+ width: number;
262
+ height: number;
263
+ verification: {
264
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
265
+ data: string;
266
+ } | {
267
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
268
+ data: string;
269
+ source: string;
270
+ };
271
+ }[];
272
+ images: {
273
+ url: string;
274
+ width: number;
275
+ height: number;
276
+ verification: {
277
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
278
+ data: string;
279
+ } | {
280
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
281
+ data: string;
282
+ source: string;
283
+ };
284
+ }[][];
285
+ assets: {
286
+ url: string;
287
+ verification: {
288
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
289
+ data: string;
290
+ } | {
291
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
292
+ data: string;
293
+ source: string;
294
+ };
295
+ fileType: string;
296
+ }[];
297
+ attributes: ({
298
+ type: "string";
299
+ value: string;
300
+ key: string;
301
+ } | {
302
+ type: "number";
303
+ value: string;
304
+ key: string;
305
+ } | {
306
+ type: "boolean";
307
+ value: boolean;
308
+ key: string;
309
+ })[];
310
+ }, {
311
+ name: string | null;
312
+ description: string | null;
313
+ category: string | null;
314
+ links: {
315
+ url: string;
316
+ title: string;
317
+ }[];
318
+ icon: {
319
+ url: string;
320
+ width: number;
321
+ height: number;
322
+ verification: {
323
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
324
+ data: string;
325
+ } | {
326
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
327
+ data: string;
328
+ source: string;
329
+ };
330
+ }[];
331
+ images: {
332
+ url: string;
333
+ width: number;
334
+ height: number;
335
+ verification: {
336
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
337
+ data: string;
338
+ } | {
339
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
340
+ data: string;
341
+ source: string;
342
+ };
343
+ }[][];
344
+ assets: {
345
+ url: string;
346
+ verification: {
347
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
348
+ data: string;
349
+ } | {
350
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
351
+ data: string;
352
+ source: string;
353
+ };
354
+ fileType: string;
355
+ }[];
356
+ attributes: ({
357
+ type: "string";
358
+ value: string;
359
+ key: string;
360
+ } | {
361
+ type: "number";
362
+ value: string;
363
+ key: string;
364
+ } | {
365
+ type: "boolean";
366
+ value: boolean;
367
+ key: string;
368
+ })[];
369
+ }>;
370
+
371
+ /**
372
+ * LSP4 Inferred Types
373
+ *
374
+ * TypeScript types inferred from LSP4 Zod schemas.
375
+ */
376
+
377
+ type LSP4Attribute = z.infer<typeof attributesSchema>;
378
+ type LSP4Metadata = z.infer<typeof lsp4MetadataSchema>;
379
+
380
+ /**
381
+ * LSP4 Digital Asset Metadata Utilities
382
+ *
383
+ * Pure functions for working with LSP4 digital asset metadata (LSP7/LSP8).
384
+ */
385
+
386
+ /**
387
+ * Gets the best image URL from pre-extracted LSP4 Image objects.
388
+ * By default prefers image over icon (for content display).
389
+ * Use preferIcon: true to prefer icon (for compact displays).
390
+ *
391
+ * @param options.image - Pre-extracted image object (from images[0][0])
392
+ * @param options.icon - Pre-extracted icon object (from icon[0])
393
+ * @param options.preferIcon - If true, prefer icon over image (default: false)
394
+ * @param options.parseUrl - Function to parse/transform the URL (e.g., IPFS parsing)
395
+ * @returns Parsed URL or undefined if no image found
396
+ *
397
+ * @example
398
+ * ```typescript
399
+ * // With destructured metadata: images: [[image]], icon: [icon]
400
+ * const imageUrl = getImageUrl({ image, icon, parseUrl });
401
+ * const iconUrl = getImageUrl({ image, icon, preferIcon: true, parseUrl });
402
+ * ```
403
+ */
404
+ declare function getImageUrl(options: {
405
+ image?: Image | null;
406
+ icon?: Image | null;
407
+ preferIcon?: boolean;
408
+ parseUrl: (url: string) => string;
409
+ }): string | undefined;
410
+ /**
411
+ * Get display name for Digital Asset.
412
+ * Uses name, falls back to 'Digital Asset'.
413
+ *
414
+ * @param metadata - Object with optional name field
415
+ * @returns Display name string
416
+ */
417
+ declare function getAssetDisplayName(metadata: LSP4Metadata): string;
418
+
419
+ /**
420
+ * LSP4 Type Guards
421
+ *
422
+ * Runtime type guards for LSP4 schemas.
423
+ */
424
+
425
+ /**
426
+ * Type guard for LSP4 attribute schema
427
+ */
428
+ declare function isAttributesSchema(obj: unknown): obj is z.infer<typeof attributesSchema>;
429
+ /**
430
+ * Type guard for LSP4 metadata schema
431
+ */
432
+ declare function isLsp4MetadataSchema(obj: unknown): obj is z.infer<typeof lsp4MetadataSchema>;
433
+
434
+ export { attributesSchema, getAssetDisplayName, getImageUrl, isAttributesSchema, isLsp4MetadataSchema, lsp4MetadataSchema };
435
+ export type { LSP4Attribute, LSP4Metadata };
@@ -0,0 +1,435 @@
1
+ import * as _chillwhales_lsp2 from '@chillwhales/lsp2';
2
+ import { Image } from '@chillwhales/lsp2';
3
+ import { z } from 'zod';
4
+
5
+ /**
6
+ * Asset attribute schema
7
+ * Supports string, number, and boolean attribute types
8
+ */
9
+ declare const attributesSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
10
+ key: z.ZodString;
11
+ value: z.ZodString;
12
+ type: z.ZodLiteral<"string">;
13
+ }, "strip", z.ZodTypeAny, {
14
+ type: "string";
15
+ value: string;
16
+ key: string;
17
+ }, {
18
+ type: "string";
19
+ value: string;
20
+ key: string;
21
+ }>, z.ZodObject<{
22
+ key: z.ZodString;
23
+ value: z.ZodEffects<z.ZodString, string, string>;
24
+ type: z.ZodLiteral<"number">;
25
+ }, "strip", z.ZodTypeAny, {
26
+ type: "number";
27
+ value: string;
28
+ key: string;
29
+ }, {
30
+ type: "number";
31
+ value: string;
32
+ key: string;
33
+ }>, z.ZodObject<{
34
+ key: z.ZodString;
35
+ value: z.ZodBoolean;
36
+ type: z.ZodLiteral<"boolean">;
37
+ }, "strip", z.ZodTypeAny, {
38
+ type: "boolean";
39
+ value: boolean;
40
+ key: string;
41
+ }, {
42
+ type: "boolean";
43
+ value: boolean;
44
+ key: string;
45
+ }>]>;
46
+ /**
47
+ * LSP4 Digital Asset metadata schema
48
+ * Full metadata structure for LSP7/LSP8 tokens
49
+ */
50
+ declare const lsp4MetadataSchema: z.ZodObject<{
51
+ name: z.ZodNullable<z.ZodString>;
52
+ description: z.ZodNullable<z.ZodString>;
53
+ category: z.ZodNullable<z.ZodString>;
54
+ links: z.ZodArray<z.ZodObject<{
55
+ title: z.ZodString;
56
+ url: z.ZodString;
57
+ }, "strip", z.ZodTypeAny, {
58
+ url: string;
59
+ title: string;
60
+ }, {
61
+ url: string;
62
+ title: string;
63
+ }>, "many">;
64
+ icon: z.ZodArray<z.ZodObject<{
65
+ url: z.ZodString;
66
+ width: z.ZodNumber;
67
+ height: z.ZodNumber;
68
+ verification: z.ZodDiscriminatedUnion<"method", [z.ZodObject<{
69
+ data: z.ZodString;
70
+ method: z.ZodEnum<[_chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES, _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8]>;
71
+ }, "strip", z.ZodTypeAny, {
72
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
73
+ data: string;
74
+ }, {
75
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
76
+ data: string;
77
+ }>, z.ZodObject<{
78
+ method: z.ZodEnum<[_chillwhales_lsp2.VERIFICATION_METHODS.ECDSA]>;
79
+ data: z.ZodString;
80
+ source: z.ZodString;
81
+ }, "strip", z.ZodTypeAny, {
82
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
83
+ data: string;
84
+ source: string;
85
+ }, {
86
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
87
+ data: string;
88
+ source: string;
89
+ }>]>;
90
+ }, "strip", z.ZodTypeAny, {
91
+ url: string;
92
+ width: number;
93
+ height: number;
94
+ verification: {
95
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
96
+ data: string;
97
+ } | {
98
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
99
+ data: string;
100
+ source: string;
101
+ };
102
+ }, {
103
+ url: string;
104
+ width: number;
105
+ height: number;
106
+ verification: {
107
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
108
+ data: string;
109
+ } | {
110
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
111
+ data: string;
112
+ source: string;
113
+ };
114
+ }>, "many">;
115
+ images: z.ZodArray<z.ZodArray<z.ZodObject<{
116
+ url: z.ZodString;
117
+ width: z.ZodNumber;
118
+ height: z.ZodNumber;
119
+ verification: z.ZodDiscriminatedUnion<"method", [z.ZodObject<{
120
+ data: z.ZodString;
121
+ method: z.ZodEnum<[_chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES, _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8]>;
122
+ }, "strip", z.ZodTypeAny, {
123
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
124
+ data: string;
125
+ }, {
126
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
127
+ data: string;
128
+ }>, z.ZodObject<{
129
+ method: z.ZodEnum<[_chillwhales_lsp2.VERIFICATION_METHODS.ECDSA]>;
130
+ data: z.ZodString;
131
+ source: z.ZodString;
132
+ }, "strip", z.ZodTypeAny, {
133
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
134
+ data: string;
135
+ source: string;
136
+ }, {
137
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
138
+ data: string;
139
+ source: string;
140
+ }>]>;
141
+ }, "strip", z.ZodTypeAny, {
142
+ url: string;
143
+ width: number;
144
+ height: number;
145
+ verification: {
146
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
147
+ data: string;
148
+ } | {
149
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
150
+ data: string;
151
+ source: string;
152
+ };
153
+ }, {
154
+ url: string;
155
+ width: number;
156
+ height: number;
157
+ verification: {
158
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
159
+ data: string;
160
+ } | {
161
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
162
+ data: string;
163
+ source: string;
164
+ };
165
+ }>, "many">, "many">;
166
+ assets: z.ZodArray<z.ZodObject<{
167
+ url: z.ZodString;
168
+ fileType: z.ZodString;
169
+ verification: z.ZodDiscriminatedUnion<"method", [z.ZodObject<{
170
+ data: z.ZodString;
171
+ method: z.ZodEnum<[_chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES, _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8]>;
172
+ }, "strip", z.ZodTypeAny, {
173
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
174
+ data: string;
175
+ }, {
176
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
177
+ data: string;
178
+ }>, z.ZodObject<{
179
+ method: z.ZodEnum<[_chillwhales_lsp2.VERIFICATION_METHODS.ECDSA]>;
180
+ data: z.ZodString;
181
+ source: z.ZodString;
182
+ }, "strip", z.ZodTypeAny, {
183
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
184
+ data: string;
185
+ source: string;
186
+ }, {
187
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
188
+ data: string;
189
+ source: string;
190
+ }>]>;
191
+ }, "strip", z.ZodTypeAny, {
192
+ url: string;
193
+ verification: {
194
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
195
+ data: string;
196
+ } | {
197
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
198
+ data: string;
199
+ source: string;
200
+ };
201
+ fileType: string;
202
+ }, {
203
+ url: string;
204
+ verification: {
205
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
206
+ data: string;
207
+ } | {
208
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
209
+ data: string;
210
+ source: string;
211
+ };
212
+ fileType: string;
213
+ }>, "many">;
214
+ attributes: z.ZodArray<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
215
+ key: z.ZodString;
216
+ value: z.ZodString;
217
+ type: z.ZodLiteral<"string">;
218
+ }, "strip", z.ZodTypeAny, {
219
+ type: "string";
220
+ value: string;
221
+ key: string;
222
+ }, {
223
+ type: "string";
224
+ value: string;
225
+ key: string;
226
+ }>, z.ZodObject<{
227
+ key: z.ZodString;
228
+ value: z.ZodEffects<z.ZodString, string, string>;
229
+ type: z.ZodLiteral<"number">;
230
+ }, "strip", z.ZodTypeAny, {
231
+ type: "number";
232
+ value: string;
233
+ key: string;
234
+ }, {
235
+ type: "number";
236
+ value: string;
237
+ key: string;
238
+ }>, z.ZodObject<{
239
+ key: z.ZodString;
240
+ value: z.ZodBoolean;
241
+ type: z.ZodLiteral<"boolean">;
242
+ }, "strip", z.ZodTypeAny, {
243
+ type: "boolean";
244
+ value: boolean;
245
+ key: string;
246
+ }, {
247
+ type: "boolean";
248
+ value: boolean;
249
+ key: string;
250
+ }>]>, "many">;
251
+ }, "strip", z.ZodTypeAny, {
252
+ name: string | null;
253
+ description: string | null;
254
+ category: string | null;
255
+ links: {
256
+ url: string;
257
+ title: string;
258
+ }[];
259
+ icon: {
260
+ url: string;
261
+ width: number;
262
+ height: number;
263
+ verification: {
264
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
265
+ data: string;
266
+ } | {
267
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
268
+ data: string;
269
+ source: string;
270
+ };
271
+ }[];
272
+ images: {
273
+ url: string;
274
+ width: number;
275
+ height: number;
276
+ verification: {
277
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
278
+ data: string;
279
+ } | {
280
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
281
+ data: string;
282
+ source: string;
283
+ };
284
+ }[][];
285
+ assets: {
286
+ url: string;
287
+ verification: {
288
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
289
+ data: string;
290
+ } | {
291
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
292
+ data: string;
293
+ source: string;
294
+ };
295
+ fileType: string;
296
+ }[];
297
+ attributes: ({
298
+ type: "string";
299
+ value: string;
300
+ key: string;
301
+ } | {
302
+ type: "number";
303
+ value: string;
304
+ key: string;
305
+ } | {
306
+ type: "boolean";
307
+ value: boolean;
308
+ key: string;
309
+ })[];
310
+ }, {
311
+ name: string | null;
312
+ description: string | null;
313
+ category: string | null;
314
+ links: {
315
+ url: string;
316
+ title: string;
317
+ }[];
318
+ icon: {
319
+ url: string;
320
+ width: number;
321
+ height: number;
322
+ verification: {
323
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
324
+ data: string;
325
+ } | {
326
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
327
+ data: string;
328
+ source: string;
329
+ };
330
+ }[];
331
+ images: {
332
+ url: string;
333
+ width: number;
334
+ height: number;
335
+ verification: {
336
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
337
+ data: string;
338
+ } | {
339
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
340
+ data: string;
341
+ source: string;
342
+ };
343
+ }[][];
344
+ assets: {
345
+ url: string;
346
+ verification: {
347
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_UTF8 | _chillwhales_lsp2.VERIFICATION_METHODS.HASH_KECCAK256_BYTES;
348
+ data: string;
349
+ } | {
350
+ method: _chillwhales_lsp2.VERIFICATION_METHODS.ECDSA;
351
+ data: string;
352
+ source: string;
353
+ };
354
+ fileType: string;
355
+ }[];
356
+ attributes: ({
357
+ type: "string";
358
+ value: string;
359
+ key: string;
360
+ } | {
361
+ type: "number";
362
+ value: string;
363
+ key: string;
364
+ } | {
365
+ type: "boolean";
366
+ value: boolean;
367
+ key: string;
368
+ })[];
369
+ }>;
370
+
371
+ /**
372
+ * LSP4 Inferred Types
373
+ *
374
+ * TypeScript types inferred from LSP4 Zod schemas.
375
+ */
376
+
377
+ type LSP4Attribute = z.infer<typeof attributesSchema>;
378
+ type LSP4Metadata = z.infer<typeof lsp4MetadataSchema>;
379
+
380
+ /**
381
+ * LSP4 Digital Asset Metadata Utilities
382
+ *
383
+ * Pure functions for working with LSP4 digital asset metadata (LSP7/LSP8).
384
+ */
385
+
386
+ /**
387
+ * Gets the best image URL from pre-extracted LSP4 Image objects.
388
+ * By default prefers image over icon (for content display).
389
+ * Use preferIcon: true to prefer icon (for compact displays).
390
+ *
391
+ * @param options.image - Pre-extracted image object (from images[0][0])
392
+ * @param options.icon - Pre-extracted icon object (from icon[0])
393
+ * @param options.preferIcon - If true, prefer icon over image (default: false)
394
+ * @param options.parseUrl - Function to parse/transform the URL (e.g., IPFS parsing)
395
+ * @returns Parsed URL or undefined if no image found
396
+ *
397
+ * @example
398
+ * ```typescript
399
+ * // With destructured metadata: images: [[image]], icon: [icon]
400
+ * const imageUrl = getImageUrl({ image, icon, parseUrl });
401
+ * const iconUrl = getImageUrl({ image, icon, preferIcon: true, parseUrl });
402
+ * ```
403
+ */
404
+ declare function getImageUrl(options: {
405
+ image?: Image | null;
406
+ icon?: Image | null;
407
+ preferIcon?: boolean;
408
+ parseUrl: (url: string) => string;
409
+ }): string | undefined;
410
+ /**
411
+ * Get display name for Digital Asset.
412
+ * Uses name, falls back to 'Digital Asset'.
413
+ *
414
+ * @param metadata - Object with optional name field
415
+ * @returns Display name string
416
+ */
417
+ declare function getAssetDisplayName(metadata: LSP4Metadata): string;
418
+
419
+ /**
420
+ * LSP4 Type Guards
421
+ *
422
+ * Runtime type guards for LSP4 schemas.
423
+ */
424
+
425
+ /**
426
+ * Type guard for LSP4 attribute schema
427
+ */
428
+ declare function isAttributesSchema(obj: unknown): obj is z.infer<typeof attributesSchema>;
429
+ /**
430
+ * Type guard for LSP4 metadata schema
431
+ */
432
+ declare function isLsp4MetadataSchema(obj: unknown): obj is z.infer<typeof lsp4MetadataSchema>;
433
+
434
+ export { attributesSchema, getAssetDisplayName, getImageUrl, isAttributesSchema, isLsp4MetadataSchema, lsp4MetadataSchema };
435
+ export type { LSP4Attribute, LSP4Metadata };
package/dist/index.mjs ADDED
@@ -0,0 +1,64 @@
1
+ import { assetSchema, imageSchema, linkSchema } from '@chillwhales/lsp2';
2
+ import { isNumeric } from '@chillwhales/utils';
3
+ import { z } from 'zod';
4
+
5
+ function getImageUrl(options) {
6
+ const { image, icon, preferIcon = false, parseUrl } = options;
7
+ const primary = preferIcon ? icon : image;
8
+ const fallback = preferIcon ? image : icon;
9
+ if (primary?.url) {
10
+ return parseUrl(primary.url);
11
+ }
12
+ if (fallback?.url) {
13
+ return parseUrl(fallback.url);
14
+ }
15
+ return void 0;
16
+ }
17
+ function getAssetDisplayName(metadata) {
18
+ return metadata.name || "Digital Asset";
19
+ }
20
+
21
+ const attributesSchema = z.discriminatedUnion("type", [
22
+ z.object({
23
+ key: z.string({ invalid_type_error: "Invalid value, not a string" }),
24
+ value: z.string({ invalid_type_error: "Invalid value, not a string" }),
25
+ type: z.literal("string")
26
+ }),
27
+ z.object({
28
+ key: z.string({ invalid_type_error: "Invalid value, not a string" }),
29
+ value: z.string({ required_error: "Value required" }).refine(isNumeric, "Invalid value, not a number"),
30
+ type: z.literal("number")
31
+ }),
32
+ z.object({
33
+ key: z.string({ invalid_type_error: "Invalid value, not a string" }),
34
+ value: z.boolean({ invalid_type_error: "Invalid value, not a boolean" }),
35
+ type: z.literal("boolean")
36
+ })
37
+ ]);
38
+ const lsp4MetadataSchema = z.object({
39
+ name: z.string({
40
+ invalid_type_error: "Name must be a string"
41
+ }).nullable(),
42
+ description: z.string({
43
+ invalid_type_error: "Description must be a string"
44
+ }).nullable(),
45
+ category: z.string({
46
+ invalid_type_error: "Category must be a string"
47
+ }).nullable(),
48
+ links: z.array(linkSchema),
49
+ icon: z.array(imageSchema),
50
+ images: z.array(z.array(imageSchema)),
51
+ assets: z.array(assetSchema),
52
+ attributes: z.array(attributesSchema)
53
+ });
54
+
55
+ function isAttributesSchema(obj) {
56
+ const { success } = attributesSchema.safeParse(obj);
57
+ return success;
58
+ }
59
+ function isLsp4MetadataSchema(obj) {
60
+ const { success } = lsp4MetadataSchema.safeParse(obj);
61
+ return success;
62
+ }
63
+
64
+ export { attributesSchema, getAssetDisplayName, getImageUrl, isAttributesSchema, isLsp4MetadataSchema, lsp4MetadataSchema };
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@chillwhales/lsp4",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "description": "LSP4 Digital Asset Metadata — schemas, types, and utilities for LSP7/LSP8 token metadata on LUKSO",
6
+ "author": "b00ste",
7
+ "license": "MIT",
8
+ "types": "./dist/index.d.mts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.mts",
12
+ "default": "./dist/index.mjs"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "LICENSE",
18
+ "README.md"
19
+ ],
20
+ "engines": {
21
+ "node": ">=22"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/chillwhales/LSPs.git",
26
+ "directory": "packages/lsp4"
27
+ },
28
+ "keywords": [
29
+ "chillwhales",
30
+ "lukso",
31
+ "lsp",
32
+ "lsp4",
33
+ "digital-asset",
34
+ "metadata",
35
+ "lsp7",
36
+ "lsp8"
37
+ ],
38
+ "sideEffects": false,
39
+ "dependencies": {
40
+ "zod": "^3.24.1",
41
+ "@chillwhales/lsp2": "0.1.1",
42
+ "@chillwhales/utils": "0.1.1"
43
+ },
44
+ "devDependencies": {
45
+ "typescript": "^5.9.3",
46
+ "unbuild": "^3.6.1",
47
+ "vitest": "^4.0.17",
48
+ "@chillwhales/config": "0.0.0"
49
+ },
50
+ "scripts": {
51
+ "build": "unbuild",
52
+ "build:watch": "unbuild --watch",
53
+ "clean": "rm -rf dist",
54
+ "test": "vitest run",
55
+ "test:watch": "vitest"
56
+ }
57
+ }