@silvana-one/nft 0.1.0

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.
Files changed (236) hide show
  1. package/README.md +1400 -0
  2. package/dist/node/admin/advanced.d.ts +469 -0
  3. package/dist/node/admin/advanced.js +525 -0
  4. package/dist/node/admin/advanced.js.map +1 -0
  5. package/dist/node/admin/index.d.ts +1 -0
  6. package/dist/node/admin/index.js +2 -0
  7. package/dist/node/admin/index.js.map +1 -0
  8. package/dist/node/contracts/admin.d.ts +140 -0
  9. package/dist/node/contracts/admin.js +336 -0
  10. package/dist/node/contracts/admin.js.map +1 -0
  11. package/dist/node/contracts/collection.d.ts +551 -0
  12. package/dist/node/contracts/collection.js +1049 -0
  13. package/dist/node/contracts/collection.js.map +1 -0
  14. package/dist/node/contracts/index.d.ts +3 -0
  15. package/dist/node/contracts/index.js +4 -0
  16. package/dist/node/contracts/index.js.map +1 -0
  17. package/dist/node/contracts/nft.d.ts +76 -0
  18. package/dist/node/contracts/nft.js +329 -0
  19. package/dist/node/contracts/nft.js.map +1 -0
  20. package/dist/node/contracts.d.ts +709 -0
  21. package/dist/node/contracts.js +61 -0
  22. package/dist/node/contracts.js.map +1 -0
  23. package/dist/node/index.cjs +5032 -0
  24. package/dist/node/index.d.ts +8 -0
  25. package/dist/node/index.js +9 -0
  26. package/dist/node/index.js.map +1 -0
  27. package/dist/node/interfaces/admin.d.ts +102 -0
  28. package/dist/node/interfaces/admin.js +2 -0
  29. package/dist/node/interfaces/admin.js.map +1 -0
  30. package/dist/node/interfaces/approval.d.ts +57 -0
  31. package/dist/node/interfaces/approval.js +62 -0
  32. package/dist/node/interfaces/approval.js.map +1 -0
  33. package/dist/node/interfaces/collection.d.ts +57 -0
  34. package/dist/node/interfaces/collection.js +2 -0
  35. package/dist/node/interfaces/collection.js.map +1 -0
  36. package/dist/node/interfaces/encoding.d.ts +24 -0
  37. package/dist/node/interfaces/encoding.js +32 -0
  38. package/dist/node/interfaces/encoding.js.map +1 -0
  39. package/dist/node/interfaces/events.d.ts +833 -0
  40. package/dist/node/interfaces/events.js +106 -0
  41. package/dist/node/interfaces/events.js.map +1 -0
  42. package/dist/node/interfaces/index.d.ts +10 -0
  43. package/dist/node/interfaces/index.js +11 -0
  44. package/dist/node/interfaces/index.js.map +1 -0
  45. package/dist/node/interfaces/ownable.d.ts +94 -0
  46. package/dist/node/interfaces/ownable.js +12 -0
  47. package/dist/node/interfaces/ownable.js.map +1 -0
  48. package/dist/node/interfaces/owner.d.ts +61 -0
  49. package/dist/node/interfaces/owner.js +101 -0
  50. package/dist/node/interfaces/owner.js.map +1 -0
  51. package/dist/node/interfaces/pausable.d.ts +74 -0
  52. package/dist/node/interfaces/pausable.js +14 -0
  53. package/dist/node/interfaces/pausable.js.map +1 -0
  54. package/dist/node/interfaces/types.d.ts +2297 -0
  55. package/dist/node/interfaces/types.js +507 -0
  56. package/dist/node/interfaces/types.js.map +1 -0
  57. package/dist/node/interfaces/update.d.ts +53 -0
  58. package/dist/node/interfaces/update.js +58 -0
  59. package/dist/node/interfaces/update.js.map +1 -0
  60. package/dist/node/marketplace/auction.d.ts +775 -0
  61. package/dist/node/marketplace/auction.js +430 -0
  62. package/dist/node/marketplace/auction.js.map +1 -0
  63. package/dist/node/marketplace/bid.d.ts +254 -0
  64. package/dist/node/marketplace/bid.js +260 -0
  65. package/dist/node/marketplace/bid.js.map +1 -0
  66. package/dist/node/marketplace/index.d.ts +5 -0
  67. package/dist/node/marketplace/index.js +6 -0
  68. package/dist/node/marketplace/index.js.map +1 -0
  69. package/dist/node/marketplace/nft-shares.d.ts +1083 -0
  70. package/dist/node/marketplace/nft-shares.js +398 -0
  71. package/dist/node/marketplace/nft-shares.js.map +1 -0
  72. package/dist/node/marketplace/offer.d.ts +192 -0
  73. package/dist/node/marketplace/offer.js +132 -0
  74. package/dist/node/marketplace/offer.js.map +1 -0
  75. package/dist/node/marketplace/types.d.ts +374 -0
  76. package/dist/node/marketplace/types.js +33 -0
  77. package/dist/node/marketplace/types.js.map +1 -0
  78. package/dist/node/metadata/index.d.ts +3 -0
  79. package/dist/node/metadata/index.js +4 -0
  80. package/dist/node/metadata/index.js.map +1 -0
  81. package/dist/node/metadata/metadata.d.ts +337 -0
  82. package/dist/node/metadata/metadata.js +439 -0
  83. package/dist/node/metadata/metadata.js.map +1 -0
  84. package/dist/node/metadata/text.d.ts +44 -0
  85. package/dist/node/metadata/text.js +42 -0
  86. package/dist/node/metadata/text.js.map +1 -0
  87. package/dist/node/metadata/tree.d.ts +75 -0
  88. package/dist/node/metadata/tree.js +85 -0
  89. package/dist/node/metadata/tree.js.map +1 -0
  90. package/dist/node/vk.d.ts +42 -0
  91. package/dist/node/vk.js +45 -0
  92. package/dist/node/vk.js.map +1 -0
  93. package/dist/node/zkprogram-example/game.d.ts +76 -0
  94. package/dist/node/zkprogram-example/game.js +108 -0
  95. package/dist/node/zkprogram-example/game.js.map +1 -0
  96. package/dist/node/zkprogram-example/index.d.ts +2 -0
  97. package/dist/node/zkprogram-example/index.js +3 -0
  98. package/dist/node/zkprogram-example/index.js.map +1 -0
  99. package/dist/node/zkprogram-example/update.d.ts +76 -0
  100. package/dist/node/zkprogram-example/update.js +85 -0
  101. package/dist/node/zkprogram-example/update.js.map +1 -0
  102. package/dist/tsconfig.tsbuildinfo +1 -0
  103. package/dist/tsconfig.web.tsbuildinfo +1 -0
  104. package/dist/web/admin/advanced.d.ts +469 -0
  105. package/dist/web/admin/advanced.js +525 -0
  106. package/dist/web/admin/advanced.js.map +1 -0
  107. package/dist/web/admin/index.d.ts +1 -0
  108. package/dist/web/admin/index.js +2 -0
  109. package/dist/web/admin/index.js.map +1 -0
  110. package/dist/web/contracts/admin.d.ts +140 -0
  111. package/dist/web/contracts/admin.js +336 -0
  112. package/dist/web/contracts/admin.js.map +1 -0
  113. package/dist/web/contracts/collection.d.ts +551 -0
  114. package/dist/web/contracts/collection.js +1049 -0
  115. package/dist/web/contracts/collection.js.map +1 -0
  116. package/dist/web/contracts/index.d.ts +3 -0
  117. package/dist/web/contracts/index.js +4 -0
  118. package/dist/web/contracts/index.js.map +1 -0
  119. package/dist/web/contracts/nft.d.ts +76 -0
  120. package/dist/web/contracts/nft.js +329 -0
  121. package/dist/web/contracts/nft.js.map +1 -0
  122. package/dist/web/contracts.d.ts +709 -0
  123. package/dist/web/contracts.js +61 -0
  124. package/dist/web/contracts.js.map +1 -0
  125. package/dist/web/index.d.ts +8 -0
  126. package/dist/web/index.js +9 -0
  127. package/dist/web/index.js.map +1 -0
  128. package/dist/web/interfaces/admin.d.ts +102 -0
  129. package/dist/web/interfaces/admin.js +2 -0
  130. package/dist/web/interfaces/admin.js.map +1 -0
  131. package/dist/web/interfaces/approval.d.ts +57 -0
  132. package/dist/web/interfaces/approval.js +62 -0
  133. package/dist/web/interfaces/approval.js.map +1 -0
  134. package/dist/web/interfaces/collection.d.ts +57 -0
  135. package/dist/web/interfaces/collection.js +2 -0
  136. package/dist/web/interfaces/collection.js.map +1 -0
  137. package/dist/web/interfaces/encoding.d.ts +24 -0
  138. package/dist/web/interfaces/encoding.js +32 -0
  139. package/dist/web/interfaces/encoding.js.map +1 -0
  140. package/dist/web/interfaces/events.d.ts +833 -0
  141. package/dist/web/interfaces/events.js +106 -0
  142. package/dist/web/interfaces/events.js.map +1 -0
  143. package/dist/web/interfaces/index.d.ts +10 -0
  144. package/dist/web/interfaces/index.js +11 -0
  145. package/dist/web/interfaces/index.js.map +1 -0
  146. package/dist/web/interfaces/ownable.d.ts +94 -0
  147. package/dist/web/interfaces/ownable.js +12 -0
  148. package/dist/web/interfaces/ownable.js.map +1 -0
  149. package/dist/web/interfaces/owner.d.ts +61 -0
  150. package/dist/web/interfaces/owner.js +101 -0
  151. package/dist/web/interfaces/owner.js.map +1 -0
  152. package/dist/web/interfaces/pausable.d.ts +74 -0
  153. package/dist/web/interfaces/pausable.js +14 -0
  154. package/dist/web/interfaces/pausable.js.map +1 -0
  155. package/dist/web/interfaces/types.d.ts +2297 -0
  156. package/dist/web/interfaces/types.js +507 -0
  157. package/dist/web/interfaces/types.js.map +1 -0
  158. package/dist/web/interfaces/update.d.ts +53 -0
  159. package/dist/web/interfaces/update.js +58 -0
  160. package/dist/web/interfaces/update.js.map +1 -0
  161. package/dist/web/marketplace/auction.d.ts +775 -0
  162. package/dist/web/marketplace/auction.js +430 -0
  163. package/dist/web/marketplace/auction.js.map +1 -0
  164. package/dist/web/marketplace/bid.d.ts +254 -0
  165. package/dist/web/marketplace/bid.js +260 -0
  166. package/dist/web/marketplace/bid.js.map +1 -0
  167. package/dist/web/marketplace/index.d.ts +5 -0
  168. package/dist/web/marketplace/index.js +6 -0
  169. package/dist/web/marketplace/index.js.map +1 -0
  170. package/dist/web/marketplace/nft-shares.d.ts +1083 -0
  171. package/dist/web/marketplace/nft-shares.js +398 -0
  172. package/dist/web/marketplace/nft-shares.js.map +1 -0
  173. package/dist/web/marketplace/offer.d.ts +192 -0
  174. package/dist/web/marketplace/offer.js +132 -0
  175. package/dist/web/marketplace/offer.js.map +1 -0
  176. package/dist/web/marketplace/types.d.ts +374 -0
  177. package/dist/web/marketplace/types.js +33 -0
  178. package/dist/web/marketplace/types.js.map +1 -0
  179. package/dist/web/metadata/index.d.ts +3 -0
  180. package/dist/web/metadata/index.js +4 -0
  181. package/dist/web/metadata/index.js.map +1 -0
  182. package/dist/web/metadata/metadata.d.ts +337 -0
  183. package/dist/web/metadata/metadata.js +439 -0
  184. package/dist/web/metadata/metadata.js.map +1 -0
  185. package/dist/web/metadata/text.d.ts +44 -0
  186. package/dist/web/metadata/text.js +42 -0
  187. package/dist/web/metadata/text.js.map +1 -0
  188. package/dist/web/metadata/tree.d.ts +75 -0
  189. package/dist/web/metadata/tree.js +85 -0
  190. package/dist/web/metadata/tree.js.map +1 -0
  191. package/dist/web/vk.d.ts +42 -0
  192. package/dist/web/vk.js +45 -0
  193. package/dist/web/vk.js.map +1 -0
  194. package/dist/web/zkprogram-example/game.d.ts +76 -0
  195. package/dist/web/zkprogram-example/game.js +108 -0
  196. package/dist/web/zkprogram-example/game.js.map +1 -0
  197. package/dist/web/zkprogram-example/index.d.ts +2 -0
  198. package/dist/web/zkprogram-example/index.js +3 -0
  199. package/dist/web/zkprogram-example/index.js.map +1 -0
  200. package/dist/web/zkprogram-example/update.d.ts +76 -0
  201. package/dist/web/zkprogram-example/update.js +85 -0
  202. package/dist/web/zkprogram-example/update.js.map +1 -0
  203. package/package.json +65 -0
  204. package/src/admin/advanced.ts +601 -0
  205. package/src/admin/index.ts +1 -0
  206. package/src/contracts/admin.ts +301 -0
  207. package/src/contracts/collection.ts +1172 -0
  208. package/src/contracts/index.ts +3 -0
  209. package/src/contracts/nft.ts +344 -0
  210. package/src/contracts.ts +107 -0
  211. package/src/index.ts +8 -0
  212. package/src/interfaces/admin.ts +127 -0
  213. package/src/interfaces/approval.ts +99 -0
  214. package/src/interfaces/collection.ts +68 -0
  215. package/src/interfaces/encoding.ts +32 -0
  216. package/src/interfaces/events.ts +115 -0
  217. package/src/interfaces/index.ts +10 -0
  218. package/src/interfaces/ownable.ts +32 -0
  219. package/src/interfaces/owner.ts +143 -0
  220. package/src/interfaces/pausable.ts +41 -0
  221. package/src/interfaces/types.ts +623 -0
  222. package/src/interfaces/update.ts +104 -0
  223. package/src/marketplace/auction.ts +527 -0
  224. package/src/marketplace/bid.ts +294 -0
  225. package/src/marketplace/index.ts +5 -0
  226. package/src/marketplace/nft-shares.ts +388 -0
  227. package/src/marketplace/offer.ts +153 -0
  228. package/src/marketplace/types.ts +33 -0
  229. package/src/metadata/index.ts +3 -0
  230. package/src/metadata/metadata.ts +603 -0
  231. package/src/metadata/text.ts +60 -0
  232. package/src/metadata/tree.ts +128 -0
  233. package/src/vk.ts +64 -0
  234. package/src/zkprogram-example/game.ts +136 -0
  235. package/src/zkprogram-example/index.ts +2 -0
  236. package/src/zkprogram-example/update.ts +98 -0
@@ -0,0 +1,603 @@
1
+ import { Field, Poseidon, Struct, Experimental } from "o1js";
2
+ import { fieldFromString } from "../interfaces/index.js";
3
+ import { Text } from "./text.js";
4
+ import { MetadataTree } from "./tree.js";
5
+ export {
6
+ Metadata,
7
+ MetadataMap,
8
+ MetadataFieldType,
9
+ MetadataFieldTypeValues,
10
+ MetadataValue,
11
+ ColorPlugin,
12
+ };
13
+
14
+ /**
15
+ * The height of the metadata Merkle tree.
16
+ */
17
+ const METADATA_HEIGHT = 20;
18
+ const IndexedMerkleMap = Experimental.IndexedMerkleMap;
19
+ type IndexedMerkleMap = Experimental.IndexedMerkleMap;
20
+
21
+ /**
22
+ * A specialized IndexedMerkleMap for storing metadata.
23
+ */
24
+ class MetadataMap extends IndexedMerkleMap(METADATA_HEIGHT) {}
25
+
26
+ /**
27
+ * The possible types for metadata fields.
28
+ */
29
+ type MetadataFieldType =
30
+ | "string"
31
+ | "text"
32
+ | "image"
33
+ | "url"
34
+ | "field"
35
+ | "map"
36
+ | "tree";
37
+
38
+ /**
39
+ * Represents a metadata value with its type and associated data.
40
+ */
41
+ class MetadataValue extends Struct({
42
+ value: Field,
43
+ type: Field,
44
+ length: Field,
45
+ height: Field,
46
+ }) {
47
+ /**
48
+ * Creates a new MetadataValue instance.
49
+ * @param params - The parameters including value and type.
50
+ * @returns A new MetadataValue.
51
+ */
52
+ static new(params: {
53
+ value: Field | Text | Metadata | MetadataTree;
54
+ type: MetadataFieldType;
55
+ }) {
56
+ const { value, type } = params;
57
+ let valueField: Field;
58
+ let length = Field(0);
59
+ let height = Field(0);
60
+ switch (type) {
61
+ case "string":
62
+ if (!(value instanceof Field)) throw new Error(`Invalid value type`);
63
+ valueField = value;
64
+ break;
65
+ case "text":
66
+ case "image":
67
+ case "url":
68
+ if (!(value instanceof Text)) throw new Error(`Invalid value type`);
69
+ valueField = value.root;
70
+ length = Field(value.size);
71
+ height = Field(value.height);
72
+ break;
73
+ case "field":
74
+ if (!(value instanceof Field)) throw new Error(`Invalid value type`);
75
+ valueField = value;
76
+ break;
77
+ case "map":
78
+ if (!(value instanceof Metadata)) throw new Error(`Invalid value type`);
79
+ valueField = value.map.root;
80
+ length = Field(value.map.length);
81
+ height = Field(value.map.height);
82
+ break;
83
+ case "tree":
84
+ if (!(value instanceof MetadataTree))
85
+ throw new Error(`Invalid value type`);
86
+ valueField = value.root;
87
+ length = Field(value.values.length);
88
+ height = Field(value.height);
89
+ break;
90
+ default:
91
+ throw new Error(`Unknown value type`);
92
+ }
93
+
94
+ return new MetadataValue({
95
+ value: valueField,
96
+ type: Field(MetadataFieldTypeValues[type].code),
97
+ length,
98
+ height,
99
+ });
100
+ }
101
+ /**
102
+ * Computes the Poseidon hash of the metadata value.
103
+ * @returns The hash as a Field.
104
+ */
105
+ hash(): Field {
106
+ return Poseidon.hash(MetadataValue.toFields(this));
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Abstract class for creating custom metadata plugins.
112
+ */
113
+ abstract class MetadataPlugin {
114
+ /**
115
+ * The name of the plugin.
116
+ */
117
+ readonly name: string;
118
+ /**
119
+ * Retrieves the trait representation of the metadata value.
120
+ * @param params - The parameters including key, type, value, and isPrivate.
121
+ * @returns An object containing the key, value, and canonical representation.
122
+ */
123
+ abstract getTrait(params: {
124
+ key: string;
125
+ type: string;
126
+ value: unknown;
127
+ isPrivate?: boolean;
128
+ }): {
129
+ key: Field;
130
+ value: MetadataValue;
131
+ canonicalRepresentation: unknown;
132
+ };
133
+ /**
134
+ * Converts the value to JSON.
135
+ * @param value - The value to convert.
136
+ * @returns The JSON representation.
137
+ */
138
+ abstract toJSON(value: unknown): string | object;
139
+ /**
140
+ * Parses the value from JSON.
141
+ * @param value - The JSON value to parse.
142
+ * @returns The parsed value.
143
+ */
144
+ abstract fromJSON(value: string | object): unknown;
145
+ }
146
+
147
+ /**
148
+ * Type representing color values.
149
+ */
150
+ type Color = "blue" | "red" | "green" | "yellow" | "purple" | "orange" | "pink";
151
+ /**
152
+ * A plugin for handling color metadata.
153
+ */
154
+ class ColorPlugin extends MetadataPlugin {
155
+ /**
156
+ * The name of the plugin.
157
+ */
158
+ readonly name = "color";
159
+
160
+ /**
161
+ * Converts a color name or value into its numeric representation.
162
+ * @param value - The color value (name, string, or number).
163
+ * @returns The numeric representation of the color.
164
+ */
165
+ getColor(value: Color | string | number): number {
166
+ if (
167
+ typeof value === "string" &&
168
+ ["blue", "red", "green", "yellow", "purple", "orange", "pink"].includes(
169
+ value
170
+ )
171
+ ) {
172
+ const colors: { [key in Color]: number } = {
173
+ blue: 0x0000ff,
174
+ red: 0xff0000,
175
+ green: 0x00ff00,
176
+ yellow: 0xffff00,
177
+ purple: 0x800080,
178
+ orange: 0xffa500,
179
+ pink: 0xffc0cb,
180
+ };
181
+ return colors[value as Color];
182
+ } else if (typeof value === "number") {
183
+ return value;
184
+ } else if (typeof value === "string") {
185
+ try {
186
+ // parse hex color like #0000ff
187
+ return parseInt(value.slice(1), 16);
188
+ } catch (e) {
189
+ throw new Error("Invalid color value");
190
+ }
191
+ }
192
+ throw new Error("Invalid color value");
193
+ }
194
+ /**
195
+ * Retrieves the trait representation of the color value.
196
+ * @param params - The parameters including key, type, and value.
197
+ * @returns An object containing the key, value, and canonical representation.
198
+ */
199
+ getTrait(params: {
200
+ key: string;
201
+ type: string;
202
+ value: Color | string | number;
203
+ }): {
204
+ key: Field;
205
+ value: MetadataValue;
206
+ canonicalRepresentation: number;
207
+ } {
208
+ const { key, value } = params;
209
+ const color = this.getColor(value);
210
+ return {
211
+ key: fieldFromString(key),
212
+ value: new MetadataValue({
213
+ value: Field(color),
214
+ type: Field(10),
215
+ length: Field(0),
216
+ height: Field(0),
217
+ }),
218
+ canonicalRepresentation: color,
219
+ };
220
+ }
221
+ /**
222
+ * Converts the color value to a JSON string.
223
+ * @param value - The color value.
224
+ * @returns The JSON string representation.
225
+ */
226
+ toJSON(value: Color | string | number): string {
227
+ return this.getColor(value).toString(16);
228
+ }
229
+ /**
230
+ * Parses the color value from a JSON string or object.
231
+ * @param value - The JSON value.
232
+ * @returns The numeric representation of the color.
233
+ */
234
+ fromJSON(value: string | object): number {
235
+ if (typeof value !== "string") throw new Error("Invalid color value");
236
+ return this.getColor(parseInt(value, 16));
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Represents the metadata for an NFT, including traits and associated data.
242
+ */
243
+ class Metadata {
244
+ /**
245
+ * The underlying map storing the metadata key-value pairs.
246
+ */
247
+ readonly map: MetadataMap;
248
+ /**
249
+ * The name of the NFT.
250
+ */
251
+ readonly name: string;
252
+ /**
253
+ * The image associated with the NFT.
254
+ */
255
+ image: string;
256
+ /**
257
+ * Optional banner image for the NFT.
258
+ */
259
+ banner?: string;
260
+ /**
261
+ * Optional description of the NFT.
262
+ */
263
+ description?: string;
264
+ /**
265
+ * Array of metadata plugins used for custom traits.
266
+ */
267
+ plugins: MetadataPlugin[];
268
+ /**
269
+ * Object containing the traits of the NFT.
270
+ */
271
+ traits: {
272
+ [key: string]: {
273
+ type: string;
274
+ value: string | Field | Metadata | MetadataTree | unknown;
275
+ isPrivate: boolean;
276
+ };
277
+ } = {};
278
+
279
+ /**
280
+ * Creates a new Metadata instance.
281
+ * @param params - The parameters for the metadata, including name, image, description, banner, and plugins.
282
+ */
283
+ constructor(params: {
284
+ name: string;
285
+ image: string;
286
+ description?: string;
287
+ banner?: string;
288
+ plugins?: MetadataPlugin[];
289
+ }) {
290
+ const { name, description, image, banner, plugins } = params;
291
+ this.plugins = plugins ?? [];
292
+ this.map = new MetadataMap();
293
+ this.addTrait({
294
+ key: "name",
295
+ type: "string",
296
+ value: name,
297
+ });
298
+ this.addTrait({
299
+ key: "image",
300
+ type: "image",
301
+ value: image,
302
+ });
303
+ if (description) {
304
+ this.addTrait({
305
+ key: "description",
306
+ type: "text",
307
+ value: description,
308
+ });
309
+ }
310
+ if (banner) {
311
+ this.addTrait({
312
+ key: "banner",
313
+ type: "image",
314
+ value: banner,
315
+ });
316
+ }
317
+ this.name = name;
318
+ this.image = image;
319
+ this.banner = banner;
320
+ this.description = description;
321
+ }
322
+
323
+ /**
324
+ * Adds a trait to the metadata.
325
+ * @param params - The parameters including key, type, value, and isPrivate.
326
+ * @returns An object containing the key and the metadata value.
327
+ */
328
+ addTrait(params: {
329
+ key: string;
330
+ type: string;
331
+ value: string | Field | Metadata | MetadataTree | unknown;
332
+ isPrivate?: boolean;
333
+ }): {
334
+ key: Field;
335
+ value: MetadataValue;
336
+ } {
337
+ const { key, type, value, isPrivate = false } = params;
338
+ let keyField = fieldFromString(key);
339
+ let metadataValue: MetadataValue;
340
+ let canonicalRepresentation: unknown = value;
341
+
342
+ if (type in MetadataFieldTypeValues) {
343
+ let valueObject: Field | Text | Metadata | MetadataTree;
344
+ switch (type) {
345
+ case "string":
346
+ if (typeof value !== "string")
347
+ throw new Error(`Invalid trait value type`);
348
+ valueObject = fieldFromString(value);
349
+ break;
350
+ case "text":
351
+ case "image":
352
+ case "url":
353
+ if (typeof value !== "string")
354
+ throw new Error(`Invalid trait value type`);
355
+ valueObject = new Text(value);
356
+ break;
357
+ case "field":
358
+ if (!(value instanceof Field))
359
+ throw new Error(`Invalid trait value type`);
360
+ valueObject = value;
361
+ break;
362
+ case "map":
363
+ if (!(value instanceof Metadata))
364
+ throw new Error(`Invalid trait value type`);
365
+ valueObject = value;
366
+ break;
367
+ case "tree":
368
+ if (!(value instanceof MetadataTree))
369
+ throw new Error(`Invalid trait value type`);
370
+ valueObject = value;
371
+ break;
372
+ default:
373
+ throw new Error(`Unknown trait value type - internal error`);
374
+ }
375
+ metadataValue = MetadataValue.new({ value: valueObject, type });
376
+ } else {
377
+ const index = this.plugins.findIndex((plugin) => plugin.name === type);
378
+ if (index !== -1) {
379
+ const plugin = this.plugins[index];
380
+ const pluginTrait = plugin.getTrait({ key, type, value, isPrivate });
381
+ metadataValue = pluginTrait.value;
382
+ keyField = pluginTrait.key;
383
+ canonicalRepresentation = pluginTrait.canonicalRepresentation;
384
+ } else throw new Error(`Unknown trait type`);
385
+ }
386
+
387
+ this.map.set(keyField, metadataValue.hash());
388
+ this.traits[key] = { type, value: canonicalRepresentation, isPrivate };
389
+ return { key: keyField, value: metadataValue };
390
+ }
391
+
392
+ /**
393
+ * Converts the metadata to a JSON representation.
394
+ * @param includePrivateTraits - Whether to include private traits.
395
+ * @returns The JSON representation of the metadata.
396
+ */
397
+ toJSON(includePrivateTraits = false): {
398
+ name: string;
399
+ image: string;
400
+ description?: string;
401
+ banner?: string;
402
+ metadataRoot: string;
403
+ traits: {
404
+ key: string;
405
+ type: string;
406
+ value: string | object;
407
+ isPrivate?: boolean;
408
+ }[];
409
+ } {
410
+ return {
411
+ name: this.name,
412
+ description: this.description,
413
+ image: this.image,
414
+ banner: this.banner,
415
+ metadataRoot: this.map.root.toJSON(),
416
+ traits: Object.entries(this.traits)
417
+ .filter(([_, { isPrivate }]) => includePrivateTraits || !isPrivate)
418
+ .map(([key, { type, value, isPrivate }]) => {
419
+ let jsonValue: string | object;
420
+ switch (type) {
421
+ case "string":
422
+ case "text":
423
+ case "image":
424
+ case "url":
425
+ if (typeof value !== "string")
426
+ throw new Error(`Invalid trait value type`);
427
+ jsonValue = value;
428
+ break;
429
+ case "field":
430
+ if (!(value instanceof Field))
431
+ throw new Error(`Invalid trait value type`);
432
+ jsonValue = value.toJSON();
433
+ break;
434
+ case "map":
435
+ if (!(value instanceof Metadata))
436
+ throw new Error(`Invalid trait value type`);
437
+ jsonValue = value.toJSON(includePrivateTraits);
438
+ break;
439
+ case "tree":
440
+ if (!(value instanceof MetadataTree))
441
+ throw new Error(`Invalid trait value type`);
442
+ jsonValue = value.toJSON();
443
+ break;
444
+ default:
445
+ const plugin = this.plugins.find(
446
+ (plugin) => plugin.name === type
447
+ );
448
+ if (!plugin) throw new Error(`Unknown trait type`);
449
+ jsonValue = plugin.toJSON(value);
450
+ }
451
+ return {
452
+ key,
453
+ type,
454
+ ...(isPrivate ? { isPrivate } : {}),
455
+ value: jsonValue,
456
+ };
457
+ }),
458
+ };
459
+ }
460
+
461
+ /**
462
+ * Constructs a Metadata instance from JSON data.
463
+ * @param params - The parameters including json data, checkRoot flag, and plugins.
464
+ * @returns A new Metadata instance.
465
+ */
466
+ static fromJSON(params: {
467
+ json: {
468
+ name: string;
469
+ image: string;
470
+ description?: string;
471
+ banner?: string;
472
+ metadataRoot: string;
473
+ traits: {
474
+ key: string;
475
+ type: string;
476
+ value: string | object;
477
+ isPrivate?: boolean;
478
+ }[];
479
+ };
480
+ checkRoot?: boolean;
481
+ plugins?: MetadataPlugin[];
482
+ }): Metadata {
483
+ const { json, checkRoot = false, plugins } = params;
484
+ const { name, description, image, banner, metadataRoot, traits } = json;
485
+ if (!name) throw new Error(`Metadata name is required`);
486
+ if (typeof name !== "string") throw new Error(`Invalid metadata name`);
487
+ if (!image || typeof image !== "string")
488
+ throw new Error(`Invalid metadata image`);
489
+ if (description && typeof description !== "string")
490
+ throw new Error(`Invalid metadata description`);
491
+ if (banner && typeof banner !== "string")
492
+ throw new Error(`Invalid metadata banner`);
493
+ if (!metadataRoot || typeof metadataRoot !== "string")
494
+ throw new Error(`Invalid metadata root`);
495
+ if (!traits || !Array.isArray(traits))
496
+ throw new Error(`Metadata traits are required`);
497
+ for (const { key, type, value, isPrivate } of traits) {
498
+ if (!key || typeof key !== "string") throw new Error(`Invalid trait key`);
499
+ if (!type || typeof type !== "string")
500
+ throw new Error(`Invalid trait type`);
501
+ if (!value || (typeof value !== "string" && typeof value !== "object"))
502
+ throw new Error(`Invalid trait value`);
503
+ if (isPrivate && typeof isPrivate !== "boolean")
504
+ throw new Error(`Invalid trait isPrivate`);
505
+ }
506
+ const metadata = new Metadata({
507
+ name,
508
+ description,
509
+ image,
510
+ banner,
511
+ plugins,
512
+ });
513
+ for (const { key, type, value, isPrivate } of traits) {
514
+ let valueField: string | Field | Metadata | MetadataTree | unknown;
515
+ switch (type) {
516
+ case "string":
517
+ case "text":
518
+ case "image":
519
+ case "url":
520
+ if (typeof value !== "string")
521
+ throw new Error(`Invalid trait value type`);
522
+ valueField = value;
523
+ break;
524
+ case "field":
525
+ if (typeof value !== "string")
526
+ throw new Error(`Invalid trait value type`);
527
+ valueField = Field.fromJSON(value);
528
+ break;
529
+ case "map":
530
+ if (typeof value !== "object")
531
+ throw new Error(`Invalid trait value type`);
532
+ valueField = Metadata.fromJSON({
533
+ json: value as unknown as {
534
+ name: string;
535
+ image: string;
536
+ description?: string;
537
+ metadataRoot: string;
538
+ traits: {
539
+ key: string;
540
+ type: string;
541
+ value: string | object;
542
+ isPrivate?: boolean;
543
+ }[];
544
+ },
545
+ checkRoot,
546
+ });
547
+ break;
548
+ case "tree":
549
+ if (typeof value !== "object")
550
+ throw new Error(`Invalid trait value type`);
551
+ valueField = MetadataTree.fromJSON(
552
+ value as unknown as {
553
+ height: number;
554
+ root: string;
555
+ values: { key: string; value: string }[];
556
+ }
557
+ );
558
+ break;
559
+ default:
560
+ const plugin = metadata.plugins.find(
561
+ (plugin) => plugin.name === type
562
+ );
563
+ if (!plugin) throw new Error(`Unknown trait type`);
564
+ valueField = plugin.fromJSON(value);
565
+ }
566
+ metadata.addTrait({
567
+ key,
568
+ type,
569
+ value: valueField,
570
+ isPrivate: isPrivate ?? false,
571
+ });
572
+ }
573
+ if (checkRoot === true && metadata.map.root.toJSON() !== metadataRoot) {
574
+ throw new Error(
575
+ `Invalid metadata root:${JSON.stringify(
576
+ {
577
+ params,
578
+ root: metadata.map.root.toJSON(),
579
+ checkRoot,
580
+ metadata: metadata.toJSON(true),
581
+ },
582
+ null,
583
+ 2
584
+ )}`
585
+ );
586
+ }
587
+
588
+ return metadata;
589
+ }
590
+ }
591
+
592
+ /**
593
+ * Mapping of metadata field types to their code values and associated types.
594
+ */
595
+ const MetadataFieldTypeValues = {
596
+ string: { code: 1n, inputType: "string", storedType: Field }, // Field
597
+ text: { code: 2n, inputType: "string", storedType: Text }, // Text
598
+ image: { code: 3n, inputType: "string", storedType: Text }, // Text
599
+ url: { code: 4n, inputType: "string", storedType: Text }, // Text
600
+ field: { code: 5n, inputType: Field, storedType: Field }, // Field
601
+ map: { code: 6n, inputType: Metadata, storedType: Metadata }, // Metadata
602
+ tree: { code: 7n, inputType: MetadataTree, storedType: MetadataTree }, // MetadataTree
603
+ } as const;
@@ -0,0 +1,60 @@
1
+ import { MerkleTree, Field } from "o1js";
2
+ export { Text, TEXT_TREE_HEIGHT };
3
+
4
+ /**
5
+ * The default height of the Merkle tree used to represent the text data.
6
+ */
7
+ const TEXT_TREE_HEIGHT = 20;
8
+
9
+ /**
10
+ * The `Text` class represents textual data in the form of a Merkle tree. Each character of the text is converted to its
11
+ * ASCII code and stored as a leaf in the Merkle tree. The root of the tree can be used as a compact representation
12
+ * of the text data in cryptographic proofs.
13
+ */
14
+ class Text {
15
+ /**
16
+ * The length of the text.
17
+ */
18
+ readonly size: number;
19
+ /**
20
+ * The original text string.
21
+ */
22
+ readonly text: string;
23
+ /**
24
+ * The root of the Merkle tree representing the text data.
25
+ */
26
+ readonly root: Field;
27
+ /**
28
+ * The height of the Merkle tree.
29
+ */
30
+ readonly height: number;
31
+
32
+ /**
33
+ * Constructs a new `Text` instance by creating a Merkle tree from the given text string.
34
+ * Each character in the text is converted to its ASCII code and stored as a leaf in the tree.
35
+ *
36
+ * @param text - The text string to be represented.
37
+ * @param height - The height of the Merkle tree. Defaults to `TEXT_TREE_HEIGHT`.
38
+ * @throws Will throw an error if the text length exceeds the number of leaves in the Merkle tree.
39
+ */
40
+ constructor(text: string, height: number = TEXT_TREE_HEIGHT) {
41
+ this.text = text;
42
+ this.size = text.length;
43
+ const tree = new MerkleTree(height);
44
+ if (this.size > tree.leafCount) throw new Error(`Text is too long`);
45
+ for (let i = 0; i < this.size; i++) {
46
+ tree.setLeaf(BigInt(i), Field.from(this.text.charCodeAt(i)));
47
+ }
48
+ this.root = tree.getRoot();
49
+ this.height = height;
50
+ }
51
+
52
+ /**
53
+ * Returns the original text string.
54
+ *
55
+ * @returns The text string.
56
+ */
57
+ public toString(): string {
58
+ return this.text;
59
+ }
60
+ }