@opendatalabs/vana-sdk 0.1.0-alpha.e64ec83 → 0.1.0-alpha.f05a34e

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 (40) hide show
  1. package/README.md +189 -425
  2. package/dist/chains.browser.cjs +4 -1
  3. package/dist/chains.browser.cjs.map +1 -1
  4. package/dist/chains.browser.d.cts +2 -1
  5. package/dist/chains.browser.d.ts +2 -1
  6. package/dist/chains.browser.js +3 -1
  7. package/dist/chains.browser.js.map +1 -1
  8. package/dist/chains.cjs +4 -1
  9. package/dist/chains.cjs.map +1 -1
  10. package/dist/chains.d.cts +1 -1
  11. package/dist/chains.d.ts +1 -1
  12. package/dist/chains.js +3 -1
  13. package/dist/chains.js.map +1 -1
  14. package/dist/chains.node.cjs +4 -1
  15. package/dist/chains.node.cjs.map +1 -1
  16. package/dist/chains.node.d.cts +1 -1
  17. package/dist/chains.node.d.ts +1 -1
  18. package/dist/chains.node.js +3 -1
  19. package/dist/chains.node.js.map +1 -1
  20. package/dist/index.browser.d.ts +3883 -2669
  21. package/dist/index.browser.js +1412 -1094
  22. package/dist/index.browser.js.map +1 -1
  23. package/dist/index.d.cts +1 -31277
  24. package/dist/index.node.cjs +1448 -1131
  25. package/dist/index.node.cjs.map +1 -1
  26. package/dist/index.node.d.cts +3885 -2671
  27. package/dist/index.node.d.ts +3885 -2671
  28. package/dist/index.node.js +1427 -1110
  29. package/dist/index.node.js.map +1 -1
  30. package/dist/platform.browser.js +32 -181
  31. package/dist/platform.browser.js.map +1 -1
  32. package/dist/platform.cjs +47 -197
  33. package/dist/platform.cjs.map +1 -1
  34. package/dist/platform.js +47 -197
  35. package/dist/platform.js.map +1 -1
  36. package/dist/platform.node.cjs +47 -197
  37. package/dist/platform.node.cjs.map +1 -1
  38. package/dist/platform.node.js +47 -197
  39. package/dist/platform.node.js.map +1 -1
  40. package/package.json +24 -18
@@ -49,11 +49,6 @@ function parseEncryptedDataBuffer(encryptedBuffer) {
49
49
  mac: encryptedBuffer.slice(-32)
50
50
  };
51
51
  }
52
- function uint8ArrayToHex(array) {
53
- return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join(
54
- ""
55
- );
56
- }
57
52
  var init_crypto_utils = __esm({
58
53
  "src/platform/shared/crypto-utils.ts"() {
59
54
  "use strict";
@@ -215,14 +210,7 @@ __export(browser_exports, {
215
210
  BrowserPlatformAdapter: () => BrowserPlatformAdapter,
216
211
  browserPlatformAdapter: () => browserPlatformAdapter
217
212
  });
218
- function hexToUint8Array(hex) {
219
- const result = new Uint8Array(hex.length / 2);
220
- for (let i = 0; i < hex.length; i += 2) {
221
- result[i / 2] = parseInt(hex.substr(i, 2), 16);
222
- }
223
- return result;
224
- }
225
- var openpgp2, BrowserECDH, BrowserCryptoAdapter, BrowserPGPAdapter, BrowserHttpAdapter, BrowserPlatformAdapter, browserPlatformAdapter;
213
+ var openpgp2, BrowserCryptoAdapter, BrowserPGPAdapter, BrowserHttpAdapter, BrowserPlatformAdapter, browserPlatformAdapter;
226
214
  var init_browser = __esm({
227
215
  "src/platform/browser.ts"() {
228
216
  "use strict";
@@ -230,133 +218,52 @@ var init_browser = __esm({
230
218
  init_crypto_utils();
231
219
  init_pgp_utils();
232
220
  init_error_utils();
233
- BrowserECDH = class {
234
- async generateKeyPair() {
235
- const keyPair = await crypto.subtle.generateKey(
236
- {
237
- name: "ECDH",
238
- namedCurve: "P-256"
239
- },
240
- true,
241
- ["deriveKey", "deriveBits"]
242
- );
243
- return keyPair;
244
- }
245
- async encrypt(publicKeyHex, message) {
246
- const ephemeralKeyPair = await this.generateKeyPair();
247
- const publicKeyData = hexToUint8Array(publicKeyHex);
248
- const importedPublicKey = await crypto.subtle.importKey(
249
- "raw",
250
- publicKeyData,
251
- { name: "ECDH", namedCurve: "P-256" },
252
- false,
253
- []
254
- );
255
- const sharedKey = await crypto.subtle.deriveKey(
256
- { name: "ECDH", public: importedPublicKey },
257
- ephemeralKeyPair.privateKey,
258
- { name: "AES-GCM", length: 256 },
259
- false,
260
- ["encrypt"]
261
- );
262
- const iv = crypto.getRandomValues(new Uint8Array(12));
263
- const encoder = new TextEncoder();
264
- const data = encoder.encode(message);
265
- const encrypted = await crypto.subtle.encrypt(
266
- { name: "AES-GCM", iv },
267
- sharedKey,
268
- data
269
- );
270
- const ephemeralPublicKeyData = await crypto.subtle.exportKey(
271
- "raw",
272
- ephemeralKeyPair.publicKey
273
- );
274
- return JSON.stringify({
275
- encrypted: Array.from(new Uint8Array(encrypted)),
276
- iv: Array.from(iv),
277
- ephemeralPublicKey: Array.from(new Uint8Array(ephemeralPublicKeyData)),
278
- publicKey: publicKeyHex
279
- });
280
- }
281
- async decrypt(privateKeyHex, encryptedData) {
282
- try {
283
- const data = JSON.parse(encryptedData);
284
- if (!data.encrypted || !data.iv || !data.ephemeralPublicKey) {
285
- throw new Error("Invalid encrypted data format");
286
- }
287
- const privateKeyData = hexToUint8Array(privateKeyHex);
288
- const importedPrivateKey = await crypto.subtle.importKey(
289
- "pkcs8",
290
- privateKeyData,
291
- { name: "ECDH", namedCurve: "P-256" },
292
- false,
293
- ["deriveKey"]
294
- );
295
- const ephemeralPublicKey = await crypto.subtle.importKey(
296
- "raw",
297
- new Uint8Array(data.ephemeralPublicKey),
298
- { name: "ECDH", namedCurve: "P-256" },
299
- false,
300
- []
301
- );
302
- const sharedKey = await crypto.subtle.deriveKey(
303
- { name: "ECDH", public: ephemeralPublicKey },
304
- importedPrivateKey,
305
- { name: "AES-GCM", length: 256 },
306
- false,
307
- ["decrypt"]
308
- );
309
- const decrypted = await crypto.subtle.decrypt(
310
- { name: "AES-GCM", iv: new Uint8Array(data.iv) },
311
- sharedKey,
312
- new Uint8Array(data.encrypted)
313
- );
314
- return new TextDecoder().decode(decrypted);
315
- } catch (error) {
316
- throw new Error(
317
- `Decryption failed: ${error instanceof Error ? error.message : "Unknown error"}`
318
- );
319
- }
320
- }
321
- };
322
221
  BrowserCryptoAdapter = class {
323
222
  async encryptWithPublicKey(data, publicKeyHex) {
324
223
  try {
325
- const ecdh = new BrowserECDH();
326
- return await ecdh.encrypt(publicKeyHex, data);
224
+ const eccrypto2 = await import("eccrypto-js");
225
+ const publicKeyBuffer = Buffer.from(publicKeyHex, "hex");
226
+ const encrypted = await eccrypto2.encrypt(
227
+ publicKeyBuffer,
228
+ Buffer.from(data, "utf8")
229
+ );
230
+ const result = Buffer.concat([
231
+ encrypted.iv,
232
+ encrypted.ephemPublicKey,
233
+ encrypted.ciphertext,
234
+ encrypted.mac
235
+ ]);
236
+ return result.toString("hex");
327
237
  } catch (error) {
328
238
  throw new Error(`Encryption failed: ${error}`);
329
239
  }
330
240
  }
331
241
  async decryptWithPrivateKey(encryptedData, privateKeyHex) {
332
242
  try {
333
- const ecdh = new BrowserECDH();
334
- return await ecdh.decrypt(privateKeyHex, encryptedData);
243
+ const eccrypto2 = await import("eccrypto-js");
244
+ const privateKeyBuffer = processWalletPrivateKey(privateKeyHex);
245
+ const encryptedBuffer = Buffer.from(encryptedData, "hex");
246
+ const { iv, ephemPublicKey, ciphertext, mac } = parseEncryptedDataBuffer(encryptedBuffer);
247
+ const encryptedObj = { iv, ephemPublicKey, ciphertext, mac };
248
+ const decryptedBuffer = await eccrypto2.decrypt(
249
+ privateKeyBuffer,
250
+ encryptedObj
251
+ );
252
+ return decryptedBuffer.toString("utf8");
335
253
  } catch (error) {
336
254
  throw new Error(`Decryption failed: ${error}`);
337
255
  }
338
256
  }
339
257
  async generateKeyPair() {
340
258
  try {
341
- const keyPair = await crypto.subtle.generateKey(
342
- {
343
- name: "ECDH",
344
- namedCurve: "P-256"
345
- },
346
- true,
347
- ["deriveKey", "deriveBits"]
348
- );
349
- const publicKeyBuffer = await crypto.subtle.exportKey(
350
- "raw",
351
- keyPair.publicKey
352
- );
353
- const privateKeyBuffer = await crypto.subtle.exportKey(
354
- "pkcs8",
355
- keyPair.privateKey
356
- );
259
+ const eccrypto2 = await import("eccrypto-js");
260
+ const privateKeyBytes = new Uint8Array(32);
261
+ crypto.getRandomValues(privateKeyBytes);
262
+ const privateKey = Buffer.from(privateKeyBytes);
263
+ const publicKey = eccrypto2.getPublicCompressed(privateKey);
357
264
  return {
358
- publicKey: uint8ArrayToHex(new Uint8Array(publicKeyBuffer)),
359
- privateKey: uint8ArrayToHex(new Uint8Array(privateKeyBuffer))
265
+ privateKey: privateKey.toString("hex"),
266
+ publicKey: publicKey.toString("hex")
360
267
  };
361
268
  } catch (error) {
362
269
  throw wrapCryptoError("key generation", error);
@@ -366,65 +273,9 @@ var init_browser = __esm({
366
273
  try {
367
274
  const eccrypto2 = await import("eccrypto-js");
368
275
  const uncompressedKey = processWalletPublicKey(publicKey);
369
- const iv = Buffer.from([
370
- 1,
371
- 2,
372
- 3,
373
- 4,
374
- 5,
375
- 6,
376
- 7,
377
- 8,
378
- 9,
379
- 10,
380
- 11,
381
- 12,
382
- 13,
383
- 14,
384
- 15,
385
- 16
386
- ]);
387
- const ephemeralKey = Buffer.from([
388
- 17,
389
- 34,
390
- 51,
391
- 68,
392
- 85,
393
- 102,
394
- 119,
395
- 136,
396
- 153,
397
- 170,
398
- 187,
399
- 204,
400
- 221,
401
- 238,
402
- 255,
403
- 0,
404
- 16,
405
- 32,
406
- 48,
407
- 64,
408
- 80,
409
- 96,
410
- 112,
411
- 128,
412
- 144,
413
- 160,
414
- 176,
415
- 192,
416
- 208,
417
- 224,
418
- 240,
419
- 0
420
- ]);
421
276
  const encryptedBuffer = await eccrypto2.encrypt(
422
277
  uncompressedKey,
423
- Buffer.from(data),
424
- {
425
- iv,
426
- ephemPrivateKey: ephemeralKey
427
- }
278
+ Buffer.from(data)
428
279
  );
429
280
  const result = Buffer.concat([
430
281
  encryptedBuffer.iv,
@@ -591,6 +442,7 @@ __export(index_node_exports, {
591
442
  RateLimiter: () => RateLimiter,
592
443
  RelayerError: () => RelayerError,
593
444
  RetryUtility: () => RetryUtility,
445
+ SchemaController: () => SchemaController,
594
446
  SchemaValidationError: () => SchemaValidationError,
595
447
  SchemaValidator: () => SchemaValidator,
596
448
  SerializationError: () => SerializationError,
@@ -601,10 +453,11 @@ __export(index_node_exports, {
601
453
  StorageError: () => StorageError,
602
454
  StorageManager: () => StorageManager,
603
455
  UserRejectedRequestError: () => UserRejectedRequestError,
604
- Vana: () => VanaNode,
456
+ Vana: () => Vana,
605
457
  VanaCore: () => VanaCore,
458
+ VanaCoreFactory: () => VanaCoreFactory,
606
459
  VanaError: () => VanaError,
607
- VanaNode: () => VanaNode,
460
+ VanaNodeImpl: () => VanaNodeImpl,
608
461
  __contractCache: () => __contractCache,
609
462
  chains: () => chains,
610
463
  checkGrantAccess: () => checkGrantAccess,
@@ -619,13 +472,13 @@ __export(index_node_exports, {
619
472
  createPlatformAdapterFor: () => createPlatformAdapterFor,
620
473
  createPlatformAdapterSafe: () => createPlatformAdapterSafe,
621
474
  createValidatedGrant: () => createValidatedGrant,
622
- decryptUserData: () => decryptUserData,
475
+ decryptBlobWithSignedKey: () => decryptBlobWithSignedKey,
623
476
  decryptWithPrivateKey: () => decryptWithPrivateKey,
624
477
  decryptWithWalletPrivateKey: () => decryptWithWalletPrivateKey,
625
478
  default: () => index_node_default,
626
479
  detectPlatform: () => detectPlatform,
480
+ encryptBlobWithSignedKey: () => encryptBlobWithSignedKey,
627
481
  encryptFileKey: () => encryptFileKey,
628
- encryptUserData: () => encryptUserData,
629
482
  encryptWithWalletPublicKey: () => encryptWithWalletPublicKey,
630
483
  extractIpfsHash: () => extractIpfsHash,
631
484
  fetchAndValidateSchema: () => fetchAndValidateSchema,
@@ -650,13 +503,11 @@ __export(index_node_exports, {
650
503
  handleRelayerRequest: () => handleRelayerRequest,
651
504
  isAPIResponse: () => isAPIResponse,
652
505
  isGrantExpired: () => isGrantExpired,
653
- isIdentityServerOutput: () => isIdentityServerOutput,
654
506
  isIpfsUrl: () => isIpfsUrl,
655
- isPersonalServerOutput: () => isPersonalServerOutput,
656
507
  isPlatformSupported: () => isPlatformSupported,
657
508
  isReplicateAPIResponse: () => isReplicateAPIResponse,
658
509
  moksha: () => moksha,
659
- mokshaTestnet: () => mokshaTestnet,
510
+ mokshaTestnet: () => mokshaTestnet2,
660
511
  parseReplicateOutput: () => parseReplicateOutput,
661
512
  retrieveAndValidateGrant: () => retrieveAndValidateGrant,
662
513
  retrieveGrantFile: () => retrieveGrantFile,
@@ -672,7 +523,7 @@ __export(index_node_exports, {
672
523
  validateGrantFile: () => validateGrantFile,
673
524
  validateGranteeAccess: () => validateGranteeAccess,
674
525
  validateOperationAccess: () => validateOperationAccess,
675
- vanaMainnet: () => vanaMainnet
526
+ vanaMainnet: () => vanaMainnet2
676
527
  });
677
528
  module.exports = __toCommonJS(index_node_exports);
678
529
 
@@ -730,13 +581,13 @@ var NodeCryptoAdapter = class {
730
581
  const publicKey = Buffer.from(publicKeyHex, "hex");
731
582
  const message = Buffer.from(data, "utf8");
732
583
  const encrypted = await eccryptoLib.encrypt(publicKey, message);
733
- const serialized = {
734
- iv: encrypted.iv.toString("hex"),
735
- ephemPublicKey: encrypted.ephemPublicKey.toString("hex"),
736
- ciphertext: encrypted.ciphertext.toString("hex"),
737
- mac: encrypted.mac.toString("hex")
738
- };
739
- return JSON.stringify(serialized);
584
+ const result = Buffer.concat([
585
+ encrypted.iv,
586
+ encrypted.ephemPublicKey,
587
+ encrypted.ciphertext,
588
+ encrypted.mac
589
+ ]);
590
+ return result.toString("hex");
740
591
  } catch (error) {
741
592
  throw new Error(`Encryption failed: ${error}`);
742
593
  }
@@ -744,15 +595,14 @@ var NodeCryptoAdapter = class {
744
595
  async decryptWithPrivateKey(encryptedData, privateKeyHex) {
745
596
  try {
746
597
  const eccryptoLib = await getEccrypto();
747
- const privateKey = Buffer.from(privateKeyHex, "hex");
748
- const serialized = JSON.parse(encryptedData);
749
- const encrypted = {
750
- iv: Buffer.from(serialized.iv, "hex"),
751
- ephemPublicKey: Buffer.from(serialized.ephemPublicKey, "hex"),
752
- ciphertext: Buffer.from(serialized.ciphertext, "hex"),
753
- mac: Buffer.from(serialized.mac, "hex")
754
- };
755
- const decrypted = await eccryptoLib.decrypt(privateKey, encrypted);
598
+ const privateKeyBuffer = processWalletPrivateKey(privateKeyHex);
599
+ const encryptedBuffer = Buffer.from(encryptedData, "hex");
600
+ const { iv, ephemPublicKey, ciphertext, mac } = parseEncryptedDataBuffer(encryptedBuffer);
601
+ const encryptedObj = { iv, ephemPublicKey, ciphertext, mac };
602
+ const decrypted = await eccryptoLib.decrypt(
603
+ privateKeyBuffer,
604
+ encryptedObj
605
+ );
756
606
  return decrypted.toString("utf8");
757
607
  } catch (error) {
758
608
  throw new Error(`Decryption failed: ${error}`);
@@ -916,6 +766,9 @@ function isWalletConfig(config) {
916
766
  function isChainConfig(config) {
917
767
  return "chainId" in config && !("walletClient" in config);
918
768
  }
769
+ function hasStorageConfig(config) {
770
+ return config.storage?.providers !== void 0 && Object.keys(config.storage.providers).length > 0;
771
+ }
919
772
 
920
773
  // src/types/chains.ts
921
774
  function isVanaChainId(chainId) {
@@ -939,8 +792,8 @@ var StorageError = class extends Error {
939
792
  var import_ajv = __toESM(require("ajv"), 1);
940
793
  var import_ajv_formats = __toESM(require("ajv-formats"), 1);
941
794
 
942
- // src/schemas/dataContract.schema.json
943
- var dataContract_schema_default = {
795
+ // src/schemas/dataSchema.schema.json
796
+ var dataSchema_schema_default = {
944
797
  $id: "https://vana.org/data-schema.json",
945
798
  $schema: "http://json-schema.org/draft-07/schema#",
946
799
  title: "Schema",
@@ -1012,7 +865,7 @@ var SchemaValidator = class {
1012
865
  strict: false
1013
866
  });
1014
867
  (0, import_ajv_formats.default)(this.ajv);
1015
- this.dataSchemaValidator = this.ajv.compile(dataContract_schema_default);
868
+ this.dataSchemaValidator = this.ajv.compile(dataSchema_schema_default);
1016
869
  }
1017
870
  /**
1018
871
  * Validates a data schema against the Vana meta-schema
@@ -1211,55 +1064,6 @@ function isReplicateAPIResponse(value) {
1211
1064
  obj.status
1212
1065
  );
1213
1066
  }
1214
- function isIdentityServerOutput(value) {
1215
- console.debug("\u{1F50D} Type Guard: Checking value:", value);
1216
- console.debug("\u{1F50D} Type Guard: Value type:", typeof value);
1217
- if (typeof value !== "object" || value === null) {
1218
- console.debug("\u{1F50D} Type Guard: Failed - not object or null");
1219
- return false;
1220
- }
1221
- const obj = value;
1222
- console.debug("\u{1F50D} Type Guard: Object keys:", Object.keys(obj));
1223
- console.debug(
1224
- "\u{1F50D} Type Guard: user_address:",
1225
- obj.user_address,
1226
- typeof obj.user_address
1227
- );
1228
- if (typeof obj.user_address !== "string") {
1229
- console.debug("\u{1F50D} Type Guard: Failed - user_address not string");
1230
- return false;
1231
- }
1232
- console.debug(
1233
- "\u{1F50D} Type Guard: personal_server:",
1234
- obj.personal_server,
1235
- typeof obj.personal_server
1236
- );
1237
- if (typeof obj.personal_server !== "object" || obj.personal_server === null) {
1238
- console.debug("\u{1F50D} Type Guard: Failed - personal_server not object or null");
1239
- return false;
1240
- }
1241
- const personalServer = obj.personal_server;
1242
- console.debug(
1243
- "\u{1F50D} Type Guard: Personal server keys:",
1244
- Object.keys(personalServer)
1245
- );
1246
- console.debug("\u{1F50D} Type Guard: address:", personalServer.address);
1247
- console.debug("\u{1F50D} Type Guard: public_key:", personalServer.public_key);
1248
- const hasAddress = "address" in personalServer;
1249
- const hasPublicKey = "public_key" in personalServer;
1250
- console.debug(
1251
- "\u{1F50D} Type Guard: Has address:",
1252
- hasAddress,
1253
- "Has public_key:",
1254
- hasPublicKey
1255
- );
1256
- return hasAddress && hasPublicKey;
1257
- }
1258
- function isPersonalServerOutput(value) {
1259
- if (typeof value !== "object" || value === null) return false;
1260
- const obj = value;
1261
- return "user_address" in obj && "identity" in obj && typeof obj.user_address === "string" && typeof obj.identity === "object";
1262
- }
1263
1067
  function isAPIResponse(value) {
1264
1068
  if (typeof value !== "object" || value === null) return false;
1265
1069
  const obj = value;
@@ -8290,6 +8094,12 @@ var DataRefinerRegistryABI = [
8290
8094
  name: "schemaId",
8291
8095
  type: "uint256"
8292
8096
  },
8097
+ {
8098
+ indexed: false,
8099
+ internalType: "string",
8100
+ name: "schemaDefinitionUrl",
8101
+ type: "string"
8102
+ },
8293
8103
  {
8294
8104
  indexed: false,
8295
8105
  internalType: "string",
@@ -8393,7 +8203,7 @@ var DataRefinerRegistryABI = [
8393
8203
  {
8394
8204
  indexed: false,
8395
8205
  internalType: "string",
8396
- name: "typ",
8206
+ name: "dialect",
8397
8207
  type: "string"
8398
8208
  },
8399
8209
  {
@@ -8489,6 +8299,40 @@ var DataRefinerRegistryABI = [
8489
8299
  stateMutability: "nonpayable",
8490
8300
  type: "function"
8491
8301
  },
8302
+ {
8303
+ inputs: [
8304
+ {
8305
+ internalType: "uint256",
8306
+ name: "dlpId",
8307
+ type: "uint256"
8308
+ },
8309
+ {
8310
+ internalType: "string",
8311
+ name: "name",
8312
+ type: "string"
8313
+ },
8314
+ {
8315
+ internalType: "string",
8316
+ name: "schemaDefinitionUrl",
8317
+ type: "string"
8318
+ },
8319
+ {
8320
+ internalType: "string",
8321
+ name: "refinementInstructionUrl",
8322
+ type: "string"
8323
+ }
8324
+ ],
8325
+ name: "addRefiner",
8326
+ outputs: [
8327
+ {
8328
+ internalType: "uint256",
8329
+ name: "",
8330
+ type: "uint256"
8331
+ }
8332
+ ],
8333
+ stateMutability: "nonpayable",
8334
+ type: "function"
8335
+ },
8492
8336
  {
8493
8337
  inputs: [
8494
8338
  {
@@ -8512,7 +8356,7 @@ var DataRefinerRegistryABI = [
8512
8356
  type: "string"
8513
8357
  }
8514
8358
  ],
8515
- name: "addRefiner",
8359
+ name: "addRefinerWithSchemaId",
8516
8360
  outputs: [
8517
8361
  {
8518
8362
  internalType: "uint256",
@@ -8532,7 +8376,7 @@ var DataRefinerRegistryABI = [
8532
8376
  },
8533
8377
  {
8534
8378
  internalType: "string",
8535
- name: "typ",
8379
+ name: "dialect",
8536
8380
  type: "string"
8537
8381
  },
8538
8382
  {
@@ -8895,7 +8739,7 @@ var DataRefinerRegistryABI = [
8895
8739
  },
8896
8740
  {
8897
8741
  internalType: "string",
8898
- name: "typ",
8742
+ name: "dialect",
8899
8743
  type: "string"
8900
8744
  },
8901
8745
  {
@@ -33812,7 +33656,7 @@ function getAbi(contract) {
33812
33656
  var import_viem = require("viem");
33813
33657
  function createGrantFile(params) {
33814
33658
  const grantFile = {
33815
- grantee: params.to,
33659
+ grantee: params.grantee,
33816
33660
  operation: params.operation,
33817
33661
  parameters: params.parameters
33818
33662
  };
@@ -34232,7 +34076,7 @@ var PermissionsController = class {
34232
34076
  * @example
34233
34077
  * ```typescript
34234
34078
  * const txHash = await vana.permissions.grant({
34235
- * to: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
34079
+ * grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
34236
34080
  * operation: "llm_inference",
34237
34081
  * parameters: {
34238
34082
  * model: "gpt-4",
@@ -34261,7 +34105,7 @@ var PermissionsController = class {
34261
34105
  * @example
34262
34106
  * ```typescript
34263
34107
  * const { preview, confirm } = await vana.permissions.prepareGrant({
34264
- * to: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
34108
+ * grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
34265
34109
  * operation: "llm_inference",
34266
34110
  * files: [1, 2, 3],
34267
34111
  * parameters: { model: "gpt-4", prompt: "Analyze my social media data" }
@@ -34310,9 +34154,13 @@ var PermissionsController = class {
34310
34154
  console.debug("\u{1F50D} Debug - Grant URL from params:", grantUrl);
34311
34155
  if (!grantUrl) {
34312
34156
  if (!this.context.relayerCallbacks?.storeGrantFile && !this.context.storageManager) {
34313
- throw new Error(
34314
- "No storage available. Provide a grantUrl, configure relayerCallbacks.storeGrantFile, or storageManager."
34315
- );
34157
+ if (this.context.validateStorageRequired) {
34158
+ this.context.validateStorageRequired();
34159
+ } else {
34160
+ throw new Error(
34161
+ "No storage available. Provide a grantUrl, configure relayerCallbacks.storeGrantFile, or storageManager."
34162
+ );
34163
+ }
34316
34164
  }
34317
34165
  if (this.context.relayerCallbacks?.storeGrantFile) {
34318
34166
  grantUrl = await this.context.relayerCallbacks.storeGrantFile(grantFile);
@@ -34336,7 +34184,7 @@ var PermissionsController = class {
34336
34184
  grantUrl
34337
34185
  );
34338
34186
  const typedData = await this.composePermissionGrantMessage({
34339
- to: params.to,
34187
+ grantee: params.grantee,
34340
34188
  operation: params.operation,
34341
34189
  // Placeholder - real data is in IPFS
34342
34190
  files: params.files,
@@ -34383,7 +34231,7 @@ var PermissionsController = class {
34383
34231
  * @example
34384
34232
  * ```typescript
34385
34233
  * const { typedData, signature } = await vana.permissions.createAndSign({
34386
- * to: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
34234
+ * grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
34387
34235
  * operation: "data_analysis",
34388
34236
  * parameters: { analysisType: "sentiment" },
34389
34237
  * });
@@ -34399,9 +34247,13 @@ var PermissionsController = class {
34399
34247
  console.debug("\u{1F50D} Debug - Grant URL from params:", grantUrl);
34400
34248
  if (!grantUrl) {
34401
34249
  if (!this.context.relayerCallbacks?.storeGrantFile && !this.context.storageManager) {
34402
- throw new Error(
34403
- "No storage available. Provide a grantUrl, configure relayerCallbacks.storeGrantFile, or storageManager."
34404
- );
34250
+ if (this.context.validateStorageRequired) {
34251
+ this.context.validateStorageRequired();
34252
+ } else {
34253
+ throw new Error(
34254
+ "No storage available. Provide a grantUrl, configure relayerCallbacks.storeGrantFile, or storageManager."
34255
+ );
34256
+ }
34405
34257
  }
34406
34258
  if (this.context.relayerCallbacks?.storeGrantFile) {
34407
34259
  grantUrl = await this.context.relayerCallbacks.storeGrantFile(grantFile);
@@ -34425,7 +34277,7 @@ var PermissionsController = class {
34425
34277
  grantUrl
34426
34278
  );
34427
34279
  const typedData = await this.composePermissionGrantMessage({
34428
- to: params.to,
34280
+ grantee: params.grantee,
34429
34281
  operation: params.operation,
34430
34282
  // Placeholder - real data is in IPFS
34431
34283
  files: params.files,
@@ -34776,7 +34628,7 @@ var PermissionsController = class {
34776
34628
  * Composes the EIP-712 typed data for PermissionGrant (new simplified format).
34777
34629
  *
34778
34630
  * @param params - The parameters for composing the permission grant message
34779
- * @param params.to - The recipient address for the permission grant
34631
+ * @param params.grantee - The recipient address for the permission grant
34780
34632
  * @param params.operation - The type of operation being granted permission for
34781
34633
  * @param params.files - Array of file IDs that the permission applies to
34782
34634
  * @param params.grantUrl - URL where the grant details are stored
@@ -34869,36 +34721,45 @@ var PermissionsController = class {
34869
34721
  return addresses[0];
34870
34722
  }
34871
34723
  /**
34872
- * Retrieves all permissions granted by the current user using subgraph queries.
34724
+ * Gets on-chain permission grant data without expensive off-chain resolution.
34873
34725
  *
34874
34726
  * @remarks
34875
- * This method queries the Vana subgraph to find permissions directly granted by the user
34876
- * using the Permission entity. It efficiently handles millions of permissions by leveraging
34877
- * indexed subgraph data instead of scanning contract logs. The method fetches complete
34878
- * grant files from IPFS to provide detailed permission information including operation
34879
- * parameters and grantee details.
34880
- * @param params - Optional query parameters
34881
- * @param params.limit - Maximum number of permissions to return (default: 50)
34882
- * @param params.subgraphUrl - Optional subgraph URL to override the default endpoint
34883
- * @returns A Promise that resolves to an array of `GrantedPermission` objects
34884
- * @throws {BlockchainError} When subgraph is unavailable or returns invalid data
34727
+ * This method provides a fast, performance-focused way to retrieve permission grants
34728
+ * by querying only the subgraph without making expensive IPFS or individual contract calls.
34729
+ * It eliminates the N+1 query problem of the legacy `getUserPermissions()` method.
34730
+ *
34731
+ * The returned data contains all on-chain information but does NOT include resolved
34732
+ * operation details, parameters, or file IDs. Use `retrieveGrantFile()` separately
34733
+ * for specific grants when detailed data is needed.
34734
+ *
34735
+ * **Performance**: Completes in ~100-500ms regardless of permission count.
34736
+ * **Reliability**: Single point of failure (subgraph) with clear RPC fallback path.
34737
+ *
34738
+ * @param options - Options for retrieving permissions (limit, subgraph URL)
34739
+ * @returns A Promise that resolves to an array of `OnChainPermissionGrant` objects
34740
+ * @throws {BlockchainError} When subgraph query fails
34741
+ * @throws {NetworkError} When network requests fail
34885
34742
  * @example
34886
34743
  * ```typescript
34887
- * // Get all permissions granted by current user
34888
- * const permissions = await vana.permissions.getUserPermissions();
34744
+ * // Fast: Get all on-chain permission data
34745
+ * const grants = await vana.permissions.getUserPermissionGrantsOnChain({ limit: 20 });
34889
34746
  *
34890
- * permissions.forEach(permission => {
34891
- * console.log(`Granted ${permission.operation} to ${permission.grantee}`);
34747
+ * // Display in UI immediately
34748
+ * grants.forEach(grant => {
34749
+ * console.log(`Permission ${grant.id}: ${grant.grantUrl}`);
34892
34750
  * });
34893
34751
  *
34894
- * // Limit results
34895
- * const recent = await vana.permissions.getUserPermissions({ limit: 10 });
34752
+ * // Lazy load detailed data for specific permission when user clicks
34753
+ * const grantFile = await retrieveGrantFile(grants[0].grantUrl);
34754
+ * console.log(`Operation: ${grantFile.operation}`);
34755
+ * console.log(`Parameters:`, grantFile.parameters);
34896
34756
  * ```
34897
34757
  */
34898
- async getUserPermissions(params) {
34758
+ async getUserPermissionGrantsOnChain(options = {}) {
34759
+ const { limit = 50, subgraphUrl } = options;
34899
34760
  try {
34900
34761
  const userAddress = await this.getUserAddress();
34901
- const graphqlEndpoint = params?.subgraphUrl || this.context.subgraphUrl;
34762
+ const graphqlEndpoint = subgraphUrl || this.context.subgraphUrl;
34902
34763
  if (!graphqlEndpoint) {
34903
34764
  throw new BlockchainError(
34904
34765
  "subgraphUrl is required. Please provide a valid subgraph endpoint or configure it in Vana constructor."
@@ -34920,16 +34781,6 @@ var PermissionsController = class {
34920
34781
  }
34921
34782
  }
34922
34783
  `;
34923
- console.info("Query:", query);
34924
- console.info(
34925
- "Body:",
34926
- JSON.stringify({
34927
- query,
34928
- variables: {
34929
- userId: userAddress.toLowerCase()
34930
- }
34931
- })
34932
- );
34933
34784
  const response = await fetch(graphqlEndpoint, {
34934
34785
  method: "POST",
34935
34786
  headers: {
@@ -34948,7 +34799,6 @@ var PermissionsController = class {
34948
34799
  );
34949
34800
  }
34950
34801
  const result = await response.json();
34951
- console.info("Result:", result);
34952
34802
  if (result.errors) {
34953
34803
  throw new BlockchainError(
34954
34804
  `Subgraph errors: ${result.errors.map((e) => e.message).join(", ")}`
@@ -34956,64 +34806,32 @@ var PermissionsController = class {
34956
34806
  }
34957
34807
  const userData = result.data?.user;
34958
34808
  if (!userData || !userData.permissions?.length) {
34959
- console.warn("No permissions found for user:", userAddress);
34960
34809
  return [];
34961
34810
  }
34962
- const userPermissions = [];
34963
- const limit = params?.limit || 50;
34964
- const permissionsToProcess = userData.permissions.slice(0, limit);
34965
- for (const permission of permissionsToProcess) {
34966
- try {
34967
- let operation;
34968
- let files = [];
34969
- let parameters;
34970
- let granteeAddress;
34971
- try {
34972
- const grantFile = await retrieveGrantFile(permission.grant);
34973
- operation = grantFile.operation;
34974
- parameters = grantFile.parameters;
34975
- granteeAddress = grantFile.grantee;
34976
- } catch {
34977
- }
34978
- try {
34979
- const fileIds = await this.getPermissionFileIds(
34980
- BigInt(permission.id)
34981
- );
34982
- files = fileIds.map((id) => Number(id));
34983
- } catch {
34984
- }
34985
- userPermissions.push({
34986
- id: BigInt(permission.id),
34987
- files,
34988
- operation: operation || "",
34989
- parameters: parameters || {},
34990
- grant: permission.grant,
34991
- grantor: userAddress.toLowerCase(),
34992
- // Current user is the grantor
34993
- grantee: granteeAddress || userAddress,
34994
- // Application that received permission
34995
- active: true,
34996
- // Default to active if not specified
34997
- grantedAt: Number(permission.addedAtBlock),
34998
- nonce: Number(permission.nonce)
34999
- });
35000
- } catch (error) {
35001
- console.error(
35002
- "SDK Error: Failed to process permission:",
35003
- permission.id,
35004
- error
35005
- );
35006
- }
35007
- }
35008
- return userPermissions.sort((a, b) => {
34811
+ const onChainGrants = userData.permissions.slice(0, limit).map((permission) => ({
34812
+ id: BigInt(permission.id),
34813
+ grantUrl: permission.grant,
34814
+ grantSignature: permission.grantSignature,
34815
+ grantHash: permission.grantHash,
34816
+ nonce: BigInt(permission.nonce),
34817
+ addedAtBlock: BigInt(permission.addedAtBlock),
34818
+ addedAtTimestamp: BigInt(permission.addedAtTimestamp || "0"),
34819
+ transactionHash: permission.transactionHash || "",
34820
+ grantor: userAddress,
34821
+ active: true
34822
+ // TODO: Add revocation status from subgraph when available
34823
+ }));
34824
+ return onChainGrants.sort((a, b) => {
35009
34825
  if (a.id < b.id) return 1;
35010
34826
  if (a.id > b.id) return -1;
35011
34827
  return 0;
35012
34828
  });
35013
34829
  } catch (error) {
35014
- console.error("Failed to fetch user permissions:", error);
34830
+ if (error instanceof BlockchainError || error instanceof NetworkError) {
34831
+ throw error;
34832
+ }
35015
34833
  throw new BlockchainError(
35016
- `Failed to fetch user permissions: ${error instanceof Error ? error.message : "Unknown error"}`
34834
+ `Failed to fetch user permission grants: ${error instanceof Error ? error.message : "Unknown error"}`
35017
34835
  );
35018
34836
  }
35019
34837
  }
@@ -35711,590 +35529,6 @@ var PermissionsController = class {
35711
35529
  // src/controllers/data.ts
35712
35530
  var import_viem2 = require("viem");
35713
35531
 
35714
- // src/controllers/server.ts
35715
- var ServerController = class {
35716
- constructor(context) {
35717
- this.context = context;
35718
- }
35719
- REPLICATE_API_URL = "https://api.replicate.com/v1/predictions";
35720
- PERSONAL_SERVER_VERSION = "vana-com/personal-server:6dae0fead4017557a2e6bd020643359ac1769228a9cfe3bd2365eeeb9711610e";
35721
- IDENTITY_SERVER_VERSION = "vana-com/identity-server:8e357fbeb87c0558b545809cabd0ef2f311082d8ce1f12b93cb8ad2f38cfbfd2";
35722
- /**
35723
- * Posts a computation request to a user's personal server.
35724
- *
35725
- * @remarks
35726
- * This method submits a computation request to the specified user's personal server
35727
- * via the Replicate API. It creates a signed request with the user address and
35728
- * permission ID, then submits it for processing. The response includes URLs for
35729
- * polling results and canceling the computation if needed.
35730
- *
35731
- * The method requires a valid Replicate API token and uses the application's
35732
- * wallet client for request signing to ensure authenticity.
35733
- * @param params - The request parameters object
35734
- * @param params.permissionId - The permission ID authorizing this computation
35735
- * @returns A Promise that resolves to a prediction response with status and control URLs
35736
- * @throws {PersonalServerError} When server request fails or parameters are invalid
35737
- * @throws {SignatureError} When request signing fails
35738
- * @throws {NetworkError} When Replicate API communication fails
35739
- * @example
35740
- * ```typescript
35741
- * const response = await vana.server.postRequest({
35742
- * permissionId: 123,
35743
- * });
35744
- *
35745
- * console.log(`Request submitted: ${response.id}`);
35746
- * console.log(`Poll for results: ${response.urls.get}`);
35747
- * ```
35748
- */
35749
- async postRequest(params) {
35750
- try {
35751
- this.validatePostRequestParams(params);
35752
- const requestJson = this.createRequestJson(params);
35753
- const signature = await this.createSignature(requestJson);
35754
- const replicateInput = {
35755
- replicate_api_token: this.getReplicateApiToken(),
35756
- signature,
35757
- request_json: requestJson
35758
- };
35759
- console.debug("\u{1F50D} Debug - replicateInput", replicateInput);
35760
- const response = await this.makeReplicateRequest(replicateInput);
35761
- return response;
35762
- } catch (error) {
35763
- if (error instanceof Error) {
35764
- if (error instanceof NetworkError || error instanceof SerializationError || error instanceof SignatureError || error instanceof PersonalServerError) {
35765
- throw error;
35766
- }
35767
- throw new PersonalServerError(
35768
- `Personal server request failed: ${error.message}`,
35769
- error
35770
- );
35771
- }
35772
- throw new PersonalServerError(
35773
- "Personal server request failed with unknown error"
35774
- );
35775
- }
35776
- }
35777
- /**
35778
- * Initializes the personal server and fetches user identity.
35779
- *
35780
- * @param params - The request parameters containing user address
35781
- * @returns Promise resolving to the user's identity information
35782
- */
35783
- async initPersonalServer(params) {
35784
- try {
35785
- this.validateInitPersonalServerParams(params);
35786
- const response = await this.makePersonalServerRequest(params);
35787
- const result = await this.pollPersonalServerResult(response);
35788
- return result;
35789
- } catch (error) {
35790
- if (error instanceof Error) {
35791
- if (error instanceof NetworkError || error instanceof SerializationError || error instanceof PersonalServerError) {
35792
- throw error;
35793
- }
35794
- throw new PersonalServerError(
35795
- `Personal server initialization failed: ${error.message}`,
35796
- error
35797
- );
35798
- }
35799
- throw new PersonalServerError(
35800
- "Personal server initialization failed with unknown error"
35801
- );
35802
- }
35803
- }
35804
- /**
35805
- * Retrieves the public key for a user's personal server via the Identity Server.
35806
- *
35807
- * @remarks
35808
- * This method uses the Identity Server to deterministically derive the personal server's
35809
- * public key from the user's EVM address. This enables anyone to encrypt data for a
35810
- * specific user's server without requiring that server to be online. The Identity Server
35811
- * provides a reliable way to obtain encryption keys for secure data sharing across the
35812
- * Vana network.
35813
- *
35814
- * The derived public key is deterministic and consistent, allowing for predictable
35815
- * encryption workflows in decentralized applications.
35816
- * @param userAddress - The user's EVM address to derive the server public key for
35817
- * @returns A Promise that resolves to the server's public key as a hex string
35818
- * @throws {PersonalServerError} When user address is invalid or server lookup fails
35819
- * @throws {NetworkError} When Identity Server API request fails
35820
- * @example
35821
- * ```typescript
35822
- * // Get public key for encrypting data to a user's server
35823
- * const publicKey = await vana.server.getTrustedServerPublicKey(
35824
- * "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36"
35825
- * );
35826
- *
35827
- * // Use the public key for encryption
35828
- * const encryptedData = await encryptForServer(data, publicKey);
35829
- * ```
35830
- */
35831
- async getTrustedServerPublicKey(userAddress) {
35832
- try {
35833
- if (!userAddress || typeof userAddress !== "string") {
35834
- throw new PersonalServerError(
35835
- "User address is required and must be a valid string"
35836
- );
35837
- }
35838
- if (!userAddress.startsWith("0x") || userAddress.length !== 42) {
35839
- throw new PersonalServerError(
35840
- "User address must be a valid EVM address"
35841
- );
35842
- }
35843
- const requestBody = {
35844
- version: this.IDENTITY_SERVER_VERSION,
35845
- input: {
35846
- user_address: userAddress
35847
- }
35848
- };
35849
- console.debug("Identity Server Request for Public Key:", {
35850
- url: this.REPLICATE_API_URL,
35851
- method: "POST",
35852
- body: requestBody
35853
- });
35854
- const response = await fetch(this.REPLICATE_API_URL, {
35855
- method: "POST",
35856
- headers: {
35857
- Authorization: `Token ${this.getReplicateApiToken()}`,
35858
- "Content-Type": "application/json"
35859
- },
35860
- body: JSON.stringify(requestBody)
35861
- });
35862
- if (!response.ok) {
35863
- const errorText = await response.text();
35864
- console.debug("Identity Server Error Response:", {
35865
- status: response.status,
35866
- statusText: response.statusText,
35867
- error: errorText
35868
- });
35869
- throw new NetworkError(
35870
- `Identity Server API request failed: ${response.status} ${response.statusText} - ${errorText}`
35871
- );
35872
- }
35873
- const data = await response.json();
35874
- console.debug("Identity Server Success Response:", data);
35875
- const result = await this.pollIdentityServerResult({
35876
- id: data.id,
35877
- status: data.status,
35878
- urls: {
35879
- get: data.urls?.get || "",
35880
- cancel: data.urls?.cancel || ""
35881
- },
35882
- input: data.input,
35883
- output: data.output,
35884
- error: data.error
35885
- });
35886
- return result;
35887
- } catch (error) {
35888
- if (error instanceof Error) {
35889
- if (error instanceof NetworkError || error instanceof PersonalServerError) {
35890
- throw error;
35891
- }
35892
- throw new PersonalServerError(
35893
- `Failed to get trusted server public key: ${error.message}`,
35894
- error
35895
- );
35896
- }
35897
- throw new PersonalServerError(
35898
- "Failed to get trusted server public key with unknown error"
35899
- );
35900
- }
35901
- }
35902
- /**
35903
- * Polls the status of a computation request for updates and results.
35904
- *
35905
- * @remarks
35906
- * This method checks the current status of a computation request by querying
35907
- * the Replicate API using the provided polling URL. It returns the current
35908
- * status, any available output, and error information. The method should be
35909
- * called periodically until the computation completes or fails.
35910
- *
35911
- * Common status values include: `starting`, `processing`, `succeeded`, `failed`, `canceled`.
35912
- * @param getUrl - The polling URL returned from the initial request submission
35913
- * @returns A Promise that resolves to the current prediction response with status and results
35914
- * @throws {NetworkError} When the polling request fails or returns invalid data
35915
- * @example
35916
- * ```typescript
35917
- * // Poll until completion
35918
- * let result = await vana.server.pollStatus(response.urls.get);
35919
- *
35920
- * while (result.status === "processing") {
35921
- * await new Promise(resolve => setTimeout(resolve, 1000));
35922
- * result = await vana.server.pollStatus(response.urls.get);
35923
- * }
35924
- *
35925
- * if (result.status === "succeeded") {
35926
- * console.log("Computation completed:", result.output);
35927
- * }
35928
- * ```
35929
- */
35930
- async pollStatus(getUrl) {
35931
- try {
35932
- console.debug("Polling Replicate Status:", getUrl);
35933
- const response = await fetch(getUrl, {
35934
- method: "GET",
35935
- headers: {
35936
- Authorization: `Token ${this.getReplicateApiToken()}`,
35937
- "Content-Type": "application/json"
35938
- }
35939
- });
35940
- if (!response.ok) {
35941
- const errorText = await response.text();
35942
- console.debug("Polling Error Response:", {
35943
- status: response.status,
35944
- statusText: response.statusText,
35945
- error: errorText
35946
- });
35947
- throw new NetworkError(
35948
- `Status polling failed: ${response.status} ${response.statusText} - ${errorText}`
35949
- );
35950
- }
35951
- const data = await response.json();
35952
- console.debug("Polling Success Response:", data);
35953
- return {
35954
- id: data.id,
35955
- status: data.status,
35956
- urls: {
35957
- get: data.urls?.get || getUrl,
35958
- cancel: data.urls?.cancel || ""
35959
- },
35960
- input: data.input,
35961
- output: data.output,
35962
- error: data.error
35963
- };
35964
- } catch (error) {
35965
- if (error instanceof NetworkError) {
35966
- throw error;
35967
- }
35968
- throw new NetworkError(
35969
- `Failed to poll status: ${error instanceof Error ? error.message : "Unknown error"}`
35970
- );
35971
- }
35972
- }
35973
- /**
35974
- * Validates the post request parameters.
35975
- *
35976
- * @param params - The post request parameters to validate
35977
- */
35978
- validatePostRequestParams(params) {
35979
- if (typeof params.permissionId !== "number" || params.permissionId <= 0) {
35980
- throw new PersonalServerError(
35981
- "Permission ID is required and must be a valid positive number"
35982
- );
35983
- }
35984
- }
35985
- /**
35986
- * Validates the init personal server parameters.
35987
- *
35988
- * @param params - The initialization parameters to validate
35989
- */
35990
- validateInitPersonalServerParams(params) {
35991
- if (!params.userAddress || typeof params.userAddress !== "string") {
35992
- throw new PersonalServerError(
35993
- "User address is required and must be a valid string"
35994
- );
35995
- }
35996
- if (!params.userAddress.startsWith("0x") || params.userAddress.length !== 42) {
35997
- throw new PersonalServerError(
35998
- "User address must be a valid Vana address"
35999
- );
36000
- }
36001
- }
36002
- /**
36003
- * Creates the request JSON string for the personal server.
36004
- *
36005
- * @param params - The post request parameters to serialize
36006
- * @returns JSON string representation of the request data
36007
- */
36008
- createRequestJson(params) {
36009
- try {
36010
- const requestData = {
36011
- permission_id: params.permissionId
36012
- };
36013
- return JSON.stringify(requestData);
36014
- } catch (error) {
36015
- throw new SerializationError(
36016
- `Failed to create request JSON: ${error instanceof Error ? error.message : "Unknown error"}`
36017
- );
36018
- }
36019
- }
36020
- /**
36021
- * Creates a signature for the request JSON.
36022
- *
36023
- * @param requestJson - The JSON string to sign
36024
- * @returns Promise resolving to the cryptographic signature
36025
- */
36026
- async createSignature(requestJson) {
36027
- try {
36028
- console.debug("\u{1F50D} Debug - createSignature", requestJson);
36029
- const client = this.context.applicationClient || this.context.walletClient;
36030
- const account = client.account;
36031
- if (!account) {
36032
- throw new SignatureError("No account available for signing");
36033
- }
36034
- if (account.type !== "local") {
36035
- throw new SignatureError(
36036
- "Only local accounts are supported for signing"
36037
- );
36038
- }
36039
- const signature = await account.signMessage({
36040
- message: requestJson
36041
- });
36042
- return signature;
36043
- } catch (error) {
36044
- if (error instanceof Error && error.message.includes("User rejected")) {
36045
- throw new SignatureError("User rejected the signature request");
36046
- }
36047
- throw new SignatureError(
36048
- `Failed to create signature: ${error instanceof Error ? error.message : "Unknown error"}`
36049
- );
36050
- }
36051
- }
36052
- /**
36053
- * Gets the Replicate API token from environment.
36054
- *
36055
- * @returns The Replicate API token from environment variables
36056
- */
36057
- getReplicateApiToken() {
36058
- const token = process.env.REPLICATE_API_TOKEN || process.env.NEXT_PUBLIC_REPLICATE_API_TOKEN;
36059
- if (!token) {
36060
- throw new PersonalServerError(
36061
- "REPLICATE_API_TOKEN environment variable is required"
36062
- );
36063
- }
36064
- return token;
36065
- }
36066
- /**
36067
- * Makes the request to the Replicate API.
36068
- *
36069
- * @param input - The input parameters for the Replicate API request
36070
- * @returns Promise resolving to the Replicate prediction response
36071
- */
36072
- async makeReplicateRequest(input) {
36073
- try {
36074
- const requestBody = {
36075
- version: this.PERSONAL_SERVER_VERSION,
36076
- input
36077
- };
36078
- console.debug("Replicate Request:", {
36079
- url: this.REPLICATE_API_URL,
36080
- method: "POST",
36081
- headers: {
36082
- Authorization: `Token ${this.getReplicateApiToken().substring(0, 10)}...`,
36083
- "Content-Type": "application/json"
36084
- },
36085
- body: requestBody
36086
- });
36087
- const response = await fetch(this.REPLICATE_API_URL, {
36088
- method: "POST",
36089
- headers: {
36090
- Authorization: `Token ${this.getReplicateApiToken()}`,
36091
- "Content-Type": "application/json"
36092
- },
36093
- body: JSON.stringify(requestBody)
36094
- });
36095
- if (!response.ok) {
36096
- const errorText = await response.text();
36097
- console.debug("Replicate Error Response:", {
36098
- status: response.status,
36099
- statusText: response.statusText,
36100
- error: errorText
36101
- });
36102
- throw new NetworkError(
36103
- `Replicate API request failed: ${response.status} ${response.statusText} - ${errorText}`
36104
- );
36105
- }
36106
- const data = await response.json();
36107
- console.debug("Replicate Success Response:", data);
36108
- return {
36109
- id: data.id,
36110
- status: data.status,
36111
- urls: {
36112
- get: data.urls?.get || "",
36113
- cancel: data.urls?.cancel || ""
36114
- },
36115
- input: data.input,
36116
- output: data.output,
36117
- error: data.error
36118
- };
36119
- } catch (error) {
36120
- if (error instanceof NetworkError) {
36121
- throw error;
36122
- }
36123
- throw new NetworkError(
36124
- `Failed to make Replicate API request: ${error instanceof Error ? error.message : "Unknown error"}`
36125
- );
36126
- }
36127
- }
36128
- /**
36129
- * Makes the request to the personal server.
36130
- *
36131
- * @param params - The initialization parameters for the personal server
36132
- * @returns Promise resolving to the Replicate prediction response
36133
- */
36134
- async makePersonalServerRequest(params) {
36135
- try {
36136
- const requestBody = {
36137
- version: this.IDENTITY_SERVER_VERSION,
36138
- input: {
36139
- user_address: params.userAddress
36140
- }
36141
- };
36142
- console.debug("Personal Server Request:", {
36143
- url: this.REPLICATE_API_URL,
36144
- method: "POST",
36145
- headers: {
36146
- Authorization: `Token ${this.getReplicateApiToken().substring(0, 10)}...`,
36147
- "Content-Type": "application/json"
36148
- },
36149
- body: requestBody
36150
- });
36151
- const response = await fetch(this.REPLICATE_API_URL, {
36152
- method: "POST",
36153
- headers: {
36154
- Authorization: `Token ${this.getReplicateApiToken()}`,
36155
- "Content-Type": "application/json"
36156
- },
36157
- body: JSON.stringify(requestBody)
36158
- });
36159
- if (!response.ok) {
36160
- const errorText = await response.text();
36161
- console.debug("Personal Server Error Response:", {
36162
- status: response.status,
36163
- statusText: response.statusText,
36164
- error: errorText
36165
- });
36166
- throw new NetworkError(
36167
- `Personal Server API request failed: ${response.status} ${response.statusText} - ${errorText}`
36168
- );
36169
- }
36170
- const data = await response.json();
36171
- console.debug("Personal Server Success Response:", data);
36172
- return {
36173
- id: data.id,
36174
- status: data.status,
36175
- urls: {
36176
- get: data.urls?.get || "",
36177
- cancel: data.urls?.cancel || ""
36178
- },
36179
- input: data.input,
36180
- output: data.output,
36181
- error: data.error
36182
- };
36183
- } catch (error) {
36184
- if (error instanceof NetworkError) {
36185
- throw error;
36186
- }
36187
- throw new NetworkError(
36188
- `Failed to make Personal Server API request: ${error instanceof Error ? error.message : "Unknown error"}`
36189
- );
36190
- }
36191
- }
36192
- /**
36193
- * Polls the identity server result until completion and extracts the public key.
36194
- *
36195
- * @param initialResponse - The initial response from the identity server
36196
- * @returns Promise resolving to the extracted public key
36197
- */
36198
- async pollIdentityServerResult(initialResponse) {
36199
- const maxAttempts = 60;
36200
- let attempts = 0;
36201
- let currentResponse = initialResponse;
36202
- while (attempts < maxAttempts) {
36203
- if (currentResponse.status === "succeeded") {
36204
- const output = currentResponse.output;
36205
- let parsedOutput;
36206
- if (typeof output === "string") {
36207
- try {
36208
- parsedOutput = JSON.parse(output);
36209
- } catch {
36210
- throw new PersonalServerError(
36211
- "Failed to parse Identity Server response as JSON"
36212
- );
36213
- }
36214
- } else {
36215
- parsedOutput = output;
36216
- }
36217
- const personalServer = parsedOutput.personal_server;
36218
- if (!personalServer || !personalServer.public_key) {
36219
- throw new PersonalServerError(
36220
- "Identity Server response missing personal_server.public_key"
36221
- );
36222
- }
36223
- return personalServer.public_key;
36224
- } else if (currentResponse.status === "failed") {
36225
- throw new PersonalServerError(
36226
- `Identity Server request failed: ${currentResponse.error || "Unknown error"}`
36227
- );
36228
- } else if (currentResponse.status === "canceled") {
36229
- throw new PersonalServerError("Identity Server request was canceled");
36230
- }
36231
- await new Promise((resolve) => setTimeout(resolve, 1e3));
36232
- attempts++;
36233
- if (currentResponse.urls.get) {
36234
- currentResponse = await this.pollStatus(currentResponse.urls.get);
36235
- }
36236
- }
36237
- throw new PersonalServerError(
36238
- "Identity Server request timed out after 60 seconds"
36239
- );
36240
- }
36241
- /**
36242
- * Polls the personal server result until completion.
36243
- *
36244
- * @param initialResponse - The initial response from the personal server
36245
- * @returns Promise resolving to the personal server response data
36246
- */
36247
- async pollPersonalServerResult(initialResponse) {
36248
- const maxAttempts = 60;
36249
- let attempts = 0;
36250
- let currentResponse = initialResponse;
36251
- while (attempts < maxAttempts) {
36252
- if (currentResponse.status === "succeeded") {
36253
- const output = currentResponse.output;
36254
- let parsedOutput;
36255
- if (typeof output === "string") {
36256
- try {
36257
- parsedOutput = JSON.parse(output);
36258
- } catch {
36259
- parsedOutput = output;
36260
- }
36261
- } else {
36262
- parsedOutput = output;
36263
- }
36264
- const personalServer = parsedOutput.personal_server;
36265
- const derivedAddress = personalServer?.address;
36266
- const publicKey = personalServer?.public_key;
36267
- return {
36268
- userAddress: parsedOutput.user_address,
36269
- identity: {
36270
- metadata: {
36271
- derivedAddress,
36272
- publicKey
36273
- }
36274
- },
36275
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
36276
- };
36277
- } else if (currentResponse.status === "failed") {
36278
- throw new PersonalServerError(
36279
- `Personal server initialization failed: ${currentResponse.error || "Unknown error"}`
36280
- );
36281
- } else if (currentResponse.status === "canceled") {
36282
- throw new PersonalServerError(
36283
- "Personal server initialization was canceled"
36284
- );
36285
- }
36286
- await new Promise((resolve) => setTimeout(resolve, 1e3));
36287
- attempts++;
36288
- if (currentResponse.urls.get) {
36289
- currentResponse = await this.pollStatus(currentResponse.urls.get);
36290
- }
36291
- }
36292
- throw new PersonalServerError(
36293
- "Personal server initialization timed out after 60 seconds"
36294
- );
36295
- }
36296
- };
36297
-
36298
35532
  // src/utils/encryption.ts
36299
35533
  var DEFAULT_ENCRYPTION_SEED = "Please sign to retrieve your encryption key";
36300
35534
  async function generateEncryptionKey(wallet, seed = DEFAULT_ENCRYPTION_SEED) {
@@ -36359,32 +35593,19 @@ async function decryptWithPrivateKey(encryptedData, privateKey, platformAdapter)
36359
35593
  throw new Error(`Failed to decrypt with private key: ${error}`);
36360
35594
  }
36361
35595
  }
36362
- async function encryptUserData(data, walletSignature, platformAdapter) {
35596
+ async function encryptBlobWithSignedKey(data, key, platformAdapter) {
36363
35597
  try {
36364
35598
  const dataBuffer = data instanceof Blob ? await data.arrayBuffer() : new TextEncoder().encode(data);
36365
35599
  const dataArray = new Uint8Array(dataBuffer);
36366
35600
  const encrypted = await platformAdapter.crypto.encryptWithPassword(
36367
35601
  dataArray,
36368
- walletSignature
35602
+ key
36369
35603
  );
36370
35604
  return new Blob([encrypted], {
36371
35605
  type: "application/octet-stream"
36372
35606
  });
36373
35607
  } catch (error) {
36374
- throw new Error(`Failed to encrypt user data: ${error}`);
36375
- }
36376
- }
36377
- async function decryptUserData(encryptedData, walletSignature, platformAdapter) {
36378
- try {
36379
- const encryptedBuffer = encryptedData instanceof Blob ? await encryptedData.arrayBuffer() : new TextEncoder().encode(encryptedData);
36380
- const encryptedArray = new Uint8Array(encryptedBuffer);
36381
- const decrypted = await platformAdapter.crypto.decryptWithPassword(
36382
- encryptedArray,
36383
- walletSignature
36384
- );
36385
- return new Blob([decrypted], { type: "text/plain" });
36386
- } catch (error) {
36387
- throw new Error(`Failed to decrypt user data: ${error}`);
35608
+ throw new Error(`Failed to encrypt data: ${error}`);
36388
35609
  }
36389
35610
  }
36390
35611
  async function generateEncryptionKeyPair(platformAdapter) {
@@ -36401,14 +35622,316 @@ async function generatePGPKeyPair(platformAdapter, options) {
36401
35622
  throw new Error(`Failed to generate PGP key pair: ${error}`);
36402
35623
  }
36403
35624
  }
35625
+ async function decryptBlobWithSignedKey(encryptedData, key, platformAdapter) {
35626
+ try {
35627
+ const encryptedBuffer = encryptedData instanceof Blob ? await encryptedData.arrayBuffer() : new TextEncoder().encode(encryptedData);
35628
+ const encryptedArray = new Uint8Array(encryptedBuffer);
35629
+ const decrypted = await platformAdapter.crypto.decryptWithPassword(
35630
+ encryptedArray,
35631
+ key
35632
+ );
35633
+ return new Blob([decrypted], { type: "text/plain" });
35634
+ } catch (error) {
35635
+ throw new Error(`Failed to decrypt data: ${error}`);
35636
+ }
35637
+ }
36404
35638
 
36405
35639
  // src/controllers/data.ts
36406
35640
  var DataController = class {
36407
35641
  constructor(context) {
36408
35642
  this.context = context;
36409
- this.serverController = new ServerController(context);
36410
35643
  }
36411
- serverController;
35644
+ async upload(params) {
35645
+ const {
35646
+ content,
35647
+ filename,
35648
+ schemaId,
35649
+ permissions = [],
35650
+ encrypt: encrypt3 = true,
35651
+ providerName
35652
+ } = params;
35653
+ try {
35654
+ let blob;
35655
+ if (content instanceof Blob) {
35656
+ blob = content;
35657
+ } else if (typeof content === "string") {
35658
+ blob = new Blob([content], { type: "text/plain" });
35659
+ } else if (content instanceof Buffer) {
35660
+ blob = new Blob([content], { type: "application/octet-stream" });
35661
+ } else {
35662
+ blob = new Blob([JSON.stringify(content)], {
35663
+ type: "application/json"
35664
+ });
35665
+ }
35666
+ let isValid = true;
35667
+ let validationErrors = [];
35668
+ if (schemaId !== void 0) {
35669
+ try {
35670
+ const schema = await this.getSchema(schemaId);
35671
+ const response = await fetch(schema.definitionUrl);
35672
+ if (!response.ok) {
35673
+ throw new Error(
35674
+ `Failed to fetch schema definition: ${response.status}`
35675
+ );
35676
+ }
35677
+ const schemaDefinition = await response.json();
35678
+ const dataSchema = {
35679
+ name: schema.name,
35680
+ version: "1.0.0",
35681
+ dialect: "json",
35682
+ schema: schemaDefinition
35683
+ };
35684
+ let parsedContent;
35685
+ if (typeof content === "string") {
35686
+ try {
35687
+ parsedContent = JSON.parse(content);
35688
+ } catch {
35689
+ parsedContent = content;
35690
+ }
35691
+ } else {
35692
+ parsedContent = content;
35693
+ }
35694
+ validateDataAgainstSchema(parsedContent, dataSchema);
35695
+ } catch (error) {
35696
+ isValid = false;
35697
+ validationErrors = [
35698
+ error instanceof Error ? error.message : "Schema validation failed"
35699
+ ];
35700
+ }
35701
+ }
35702
+ let finalBlob = blob;
35703
+ if (encrypt3) {
35704
+ const encryptionKey = await generateEncryptionKey(
35705
+ this.context.walletClient,
35706
+ DEFAULT_ENCRYPTION_SEED
35707
+ );
35708
+ finalBlob = await encryptBlobWithSignedKey(
35709
+ blob,
35710
+ encryptionKey,
35711
+ this.context.platform
35712
+ );
35713
+ }
35714
+ if (!this.context.storageManager) {
35715
+ if (this.context.validateStorageRequired) {
35716
+ this.context.validateStorageRequired();
35717
+ throw new Error("Storage validation failed");
35718
+ } else {
35719
+ throw new Error(
35720
+ "Storage manager not configured. Please provide storage providers in VanaConfig."
35721
+ );
35722
+ }
35723
+ }
35724
+ const uploadResult = await this.context.storageManager.upload(
35725
+ finalBlob,
35726
+ filename,
35727
+ providerName
35728
+ );
35729
+ const userAddress = await this.getUserAddress();
35730
+ let encryptedPermissions = [];
35731
+ if (permissions.length > 0 && encrypt3) {
35732
+ const userEncryptionKey = await generateEncryptionKey(
35733
+ this.context.walletClient,
35734
+ DEFAULT_ENCRYPTION_SEED
35735
+ );
35736
+ encryptedPermissions = await Promise.all(
35737
+ permissions.map(async (permission) => {
35738
+ const publicKey = permission.publicKey;
35739
+ if (!publicKey) {
35740
+ throw new Error(
35741
+ `Public key required for permission to ${permission.grantee} when encryption is enabled`
35742
+ );
35743
+ }
35744
+ const encryptedKey = await encryptWithWalletPublicKey(
35745
+ userEncryptionKey,
35746
+ publicKey,
35747
+ this.context.platform
35748
+ );
35749
+ return {
35750
+ account: permission.grantee,
35751
+ key: encryptedKey
35752
+ };
35753
+ })
35754
+ );
35755
+ }
35756
+ let result;
35757
+ if (this.context.relayerCallbacks?.submitFileAdditionComplete) {
35758
+ result = await this.context.relayerCallbacks.submitFileAdditionComplete(
35759
+ {
35760
+ url: uploadResult.url,
35761
+ userAddress,
35762
+ permissions: encryptedPermissions,
35763
+ schemaId: schemaId || 0
35764
+ }
35765
+ );
35766
+ } else if (this.context.relayerCallbacks?.submitFileAddition) {
35767
+ const needsComplexRegistration = schemaId !== void 0 || encryptedPermissions.length > 0;
35768
+ if (needsComplexRegistration) {
35769
+ throw new Error(
35770
+ "The configured relay callback does not support schemas or permissions. Please update your relay server implementation to provide the `submitFileAdditionComplete` callback."
35771
+ );
35772
+ }
35773
+ result = await this.context.relayerCallbacks.submitFileAddition(
35774
+ uploadResult.url,
35775
+ userAddress
35776
+ );
35777
+ } else {
35778
+ result = await this.addFileWithPermissionsAndSchema(
35779
+ uploadResult.url,
35780
+ userAddress,
35781
+ encryptedPermissions,
35782
+ schemaId || 0
35783
+ );
35784
+ }
35785
+ return {
35786
+ fileId: result.fileId,
35787
+ url: uploadResult.url,
35788
+ transactionHash: result.transactionHash,
35789
+ size: uploadResult.size,
35790
+ isValid,
35791
+ validationErrors: validationErrors.length > 0 ? validationErrors : void 0
35792
+ };
35793
+ } catch (error) {
35794
+ throw new Error(
35795
+ `Upload failed: ${error instanceof Error ? error.message : "Unknown error"}`
35796
+ );
35797
+ }
35798
+ }
35799
+ /**
35800
+ * Decrypts a file owned by the user using their wallet signature.
35801
+ *
35802
+ * @remarks
35803
+ * This is the high-level convenience method for decrypting user files, serving as the
35804
+ * symmetrical counterpart to the `upload` method. It handles the complete decryption
35805
+ * workflow including key generation, URL protocol detection, content fetching, and
35806
+ * decryption.
35807
+ *
35808
+ * The method automatically:
35809
+ * - Generates the decryption key from the user's wallet signature
35810
+ * - Determines the appropriate fetch method based on the file URL protocol
35811
+ * - Fetches the encrypted content from IPFS or standard HTTP URLs
35812
+ * - Decrypts the content using the generated key
35813
+ *
35814
+ * For IPFS URLs, the method uses gateway fallback for improved reliability. For
35815
+ * standard HTTP URLs, it uses a simple fetch. If you need custom authentication
35816
+ * headers or specific gateway configurations, use the low-level primitives directly.
35817
+ *
35818
+ * @param file - The user file to decrypt (typically from getUserFiles)
35819
+ * @param encryptionSeed - Optional custom encryption seed (defaults to Vana standard)
35820
+ * @returns Promise resolving to the decrypted file content as a Blob
35821
+ * @throws {Error} When the wallet is not connected
35822
+ * @throws {Error} When fetching the encrypted content fails
35823
+ * @throws {Error} When decryption fails (wrong key or corrupted data)
35824
+ * @example
35825
+ * ```typescript
35826
+ * // Basic file decryption
35827
+ * const files = await vana.data.getUserFiles({ owner: userAddress });
35828
+ * const decryptedBlob = await vana.data.decryptFile(files[0]);
35829
+ *
35830
+ * // Convert to text
35831
+ * const text = await decryptedBlob.text();
35832
+ * console.log('Decrypted content:', text);
35833
+ *
35834
+ * // Convert to JSON
35835
+ * const json = JSON.parse(await decryptedBlob.text());
35836
+ * console.log('Decrypted data:', json);
35837
+ *
35838
+ * // With custom encryption seed
35839
+ * const decryptedBlob = await vana.data.decryptFile(
35840
+ * files[0],
35841
+ * "My custom encryption seed"
35842
+ * );
35843
+ *
35844
+ * // Save to file (in Node.js)
35845
+ * const buffer = await decryptedBlob.arrayBuffer();
35846
+ * fs.writeFileSync('decrypted-file.txt', Buffer.from(buffer));
35847
+ * ```
35848
+ */
35849
+ async decryptFile(file, encryptionSeed) {
35850
+ try {
35851
+ const encryptionKey = await generateEncryptionKey(
35852
+ this.context.walletClient,
35853
+ encryptionSeed || DEFAULT_ENCRYPTION_SEED
35854
+ );
35855
+ let encryptedBlob;
35856
+ try {
35857
+ if (file.url.startsWith("ipfs://")) {
35858
+ encryptedBlob = await this.fetchFromIPFS(file.url);
35859
+ } else {
35860
+ encryptedBlob = await this.fetch(file.url);
35861
+ }
35862
+ } catch (fetchError) {
35863
+ const errorMessage = fetchError instanceof Error ? fetchError.message : "Unknown error";
35864
+ if (errorMessage.includes("Failed to fetch IPFS content") && errorMessage.includes("from all gateways")) {
35865
+ throw new Error(
35866
+ "Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
35867
+ );
35868
+ } else if (errorMessage.includes("Empty response")) {
35869
+ throw new Error("File is empty or could not be retrieved");
35870
+ } else if (errorMessage.includes("Network error:") || errorMessage.includes("Failed to fetch")) {
35871
+ throw new Error(
35872
+ "Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
35873
+ );
35874
+ } else if (errorMessage.includes("HTTP error!")) {
35875
+ const statusMatch = errorMessage.match(/status: (\d+)/);
35876
+ const status = statusMatch ? statusMatch[1] : "unknown";
35877
+ if (status === "500") {
35878
+ throw new Error(
35879
+ "Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
35880
+ );
35881
+ } else if (status === "403") {
35882
+ throw new Error(
35883
+ "Access denied. You may not have permission to access this file"
35884
+ );
35885
+ } else if (status === "404") {
35886
+ throw new Error(
35887
+ "File not found: The encrypted file is no longer available at the stored URL."
35888
+ );
35889
+ } else {
35890
+ throw new Error(
35891
+ "Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
35892
+ );
35893
+ }
35894
+ }
35895
+ throw fetchError;
35896
+ }
35897
+ if (encryptedBlob.size === 0) {
35898
+ throw new Error("File is empty or could not be retrieved");
35899
+ }
35900
+ let decryptedBlob;
35901
+ try {
35902
+ decryptedBlob = await decryptBlobWithSignedKey(
35903
+ encryptedBlob,
35904
+ encryptionKey,
35905
+ this.context.platform
35906
+ );
35907
+ } catch (decryptError) {
35908
+ const errorMessage = decryptError instanceof Error ? decryptError.message : "Unknown error";
35909
+ if (errorMessage.includes("not a valid OpenPGP message")) {
35910
+ throw new Error(
35911
+ "Invalid file format: This file doesn't appear to be encrypted with the Vana protocol"
35912
+ );
35913
+ } else if (errorMessage.includes("Session key decryption failed")) {
35914
+ throw new Error("Wrong encryption key");
35915
+ } else if (errorMessage.includes("Error decrypting message")) {
35916
+ throw new Error("Wrong encryption key");
35917
+ } else if (errorMessage.includes("File not found")) {
35918
+ throw new Error(
35919
+ "File not found: The encrypted file is no longer available"
35920
+ );
35921
+ } else {
35922
+ throw decryptError;
35923
+ }
35924
+ }
35925
+ return decryptedBlob;
35926
+ } catch (error) {
35927
+ if (error instanceof Error && (error.message.includes("Network error:") || error.message.includes("Invalid file format:") || error.message.includes("Wrong encryption key") || error.message.includes("Access denied") || error.message.includes("File not found:") || error.message.includes("File is empty"))) {
35928
+ throw error;
35929
+ }
35930
+ throw new Error(
35931
+ `Failed to decrypt file: ${error instanceof Error ? error.message : "Unknown error"}`
35932
+ );
35933
+ }
35934
+ }
36412
35935
  /**
36413
35936
  * Retrieves all data files owned by a specific user address.
36414
35937
  *
@@ -36992,6 +36515,7 @@ var DataController = class {
36992
36515
  /**
36993
36516
  * Uploads an encrypted file to storage and registers it on the blockchain.
36994
36517
  *
36518
+ * @deprecated Use vana.data.upload() instead for the high-level API with automatic encryption
36995
36519
  * @param encryptedFile - The encrypted file blob to upload
36996
36520
  * @param filename - Optional filename for the upload
36997
36521
  * @param providerName - Optional storage provider to use
@@ -37079,6 +36603,7 @@ var DataController = class {
37079
36603
  /**
37080
36604
  * Uploads an encrypted file to storage and registers it on the blockchain with a schema.
37081
36605
  *
36606
+ * @deprecated Use vana.data.upload() instead for the high-level API with automatic encryption and schema validation
37082
36607
  * @param encryptedFile - The encrypted file blob to upload
37083
36608
  * @param schemaId - The schema ID to associate with the file
37084
36609
  * @param filename - Optional filename for the upload
@@ -37157,81 +36682,6 @@ var DataController = class {
37157
36682
  );
37158
36683
  }
37159
36684
  }
37160
- /**
37161
- * Decrypts a file that was encrypted using the Vana protocol.
37162
- *
37163
- * @param file - The UserFile object containing the file URL and metadata
37164
- * @param encryptionSeed - Optional custom encryption seed (defaults to Vana standard)
37165
- * @returns Promise resolving to the decrypted file as a Blob
37166
- *
37167
- * This method handles the complete flow of:
37168
- * 1. Generating the encryption key from the user's wallet signature
37169
- * 2. Fetching the encrypted file from the stored URL
37170
- * 3. Decrypting the file using the canonical Vana decryption method
37171
- */
37172
- async decryptFile(file, encryptionSeed = DEFAULT_ENCRYPTION_SEED) {
37173
- try {
37174
- const encryptionKey = await generateEncryptionKey(
37175
- this.context.walletClient,
37176
- encryptionSeed
37177
- );
37178
- const fetchUrl = this.convertToDownloadUrl(file.url);
37179
- console.debug(
37180
- `Fetching encrypted file from URL: ${fetchUrl} (original: ${file.url})`
37181
- );
37182
- const response = await fetch(fetchUrl);
37183
- if (!response.ok) {
37184
- if (response.status === 404) {
37185
- throw new Error(
37186
- "File not found. The encrypted file may have been moved or deleted."
37187
- );
37188
- } else if (response.status === 403) {
37189
- throw new Error(
37190
- "Access denied. You may not have permission to access this file."
37191
- );
37192
- } else {
37193
- throw new Error(
37194
- `Network error: ${response.status} ${response.statusText}`
37195
- );
37196
- }
37197
- }
37198
- const encryptedBlob = await response.blob();
37199
- console.debug(
37200
- `Retrieved blob of size: ${encryptedBlob.size} bytes, type: ${encryptedBlob.type}`
37201
- );
37202
- if (encryptedBlob.size === 0) {
37203
- throw new Error("File is empty or could not be retrieved.");
37204
- }
37205
- const decryptedBlob = await decryptUserData(
37206
- encryptedBlob,
37207
- encryptionKey,
37208
- this.context.platform
37209
- );
37210
- return decryptedBlob;
37211
- } catch (error) {
37212
- console.error("Failed to decrypt file:", error);
37213
- if (error instanceof Error) {
37214
- if (error.message.includes("Session key decryption failed") || error.message.includes("Error decrypting message")) {
37215
- throw new Error(
37216
- "Wrong encryption key. This file may have been encrypted with a different wallet or encryption seed. Try using the same wallet that originally encrypted this file."
37217
- );
37218
- } else if (error.message.includes("Failed to fetch") || error.message.includes("Network error")) {
37219
- throw new Error(
37220
- "Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
37221
- );
37222
- } else if (error.message.includes("File not found")) {
37223
- throw new Error(
37224
- "File not found: The encrypted file is no longer available at the stored URL."
37225
- );
37226
- } else if (error.message.includes("not a valid OpenPGP message") || error.message.includes("does not conform to a valid OpenPGP format")) {
37227
- throw new Error(
37228
- "Invalid file format: This file doesn't appear to be encrypted with the Vana protocol."
37229
- );
37230
- }
37231
- }
37232
- throw error;
37233
- }
37234
- }
37235
36685
  /**
37236
36686
  * Registers a file URL directly on the blockchain with a schema ID.
37237
36687
  *
@@ -37292,26 +36742,6 @@ var DataController = class {
37292
36742
  );
37293
36743
  }
37294
36744
  }
37295
- /**
37296
- * Converts IPFS and Google Drive URLs to direct download URLs for fetching.
37297
- *
37298
- * @param url - The URL to convert to a direct download URL
37299
- * @returns The converted direct download URL or the original URL if not a special URL
37300
- */
37301
- convertToDownloadUrl(url) {
37302
- if (url.startsWith("ipfs://")) {
37303
- const hash = url.replace("ipfs://", "");
37304
- return `https://ipfs.io/ipfs/${hash}`;
37305
- }
37306
- if (url.includes("drive.google.com/file/d/")) {
37307
- const fileIdMatch = url.match(/\/file\/d\/([a-zA-Z0-9-_]+)/);
37308
- if (fileIdMatch) {
37309
- const fileId = fileIdMatch[1];
37310
- return `https://drive.google.com/uc?id=${fileId}&export=download`;
37311
- }
37312
- }
37313
- return url;
37314
- }
37315
36745
  /**
37316
36746
  * Gets the user's address from the wallet client.
37317
36747
  *
@@ -37386,9 +36816,70 @@ var DataController = class {
37386
36816
  );
37387
36817
  }
37388
36818
  }
36819
+ /**
36820
+ * Adds a file to the registry with permissions and schema.
36821
+ * This combines the functionality of addFileWithPermissions and schema validation.
36822
+ *
36823
+ * @param url - The URL of the file to register
36824
+ * @param ownerAddress - The address of the file owner
36825
+ * @param permissions - Array of permissions to grant (account and encrypted key)
36826
+ * @param schemaId - The schema ID to associate with the file (0 for no schema)
36827
+ * @returns Promise resolving to object with fileId and transactionHash
36828
+ */
36829
+ async addFileWithPermissionsAndSchema(url, ownerAddress, permissions = [], schemaId = 0) {
36830
+ try {
36831
+ const chainId = this.context.walletClient.chain?.id;
36832
+ if (!chainId) {
36833
+ throw new Error("Chain ID not available");
36834
+ }
36835
+ const dataRegistryAddress = getContractAddress(chainId, "DataRegistry");
36836
+ const dataRegistryAbi = getAbi("DataRegistry");
36837
+ const txHash = await this.context.walletClient.writeContract({
36838
+ address: dataRegistryAddress,
36839
+ abi: dataRegistryAbi,
36840
+ functionName: "addFileWithPermissionsAndSchema",
36841
+ args: [url, ownerAddress, permissions, BigInt(schemaId)],
36842
+ account: this.context.walletClient.account || ownerAddress,
36843
+ chain: this.context.walletClient.chain || null
36844
+ });
36845
+ const receipt = await this.context.publicClient.waitForTransactionReceipt(
36846
+ {
36847
+ hash: txHash,
36848
+ timeout: 3e4
36849
+ // 30 seconds timeout
36850
+ }
36851
+ );
36852
+ let fileId = 0;
36853
+ for (const log of receipt.logs) {
36854
+ try {
36855
+ const decoded = (0, import_viem2.decodeEventLog)({
36856
+ abi: dataRegistryAbi,
36857
+ data: log.data,
36858
+ topics: log.topics
36859
+ });
36860
+ if (decoded.eventName === "FileAdded") {
36861
+ fileId = Number(decoded.args.fileId);
36862
+ break;
36863
+ }
36864
+ } catch {
36865
+ continue;
36866
+ }
36867
+ }
36868
+ return {
36869
+ fileId,
36870
+ transactionHash: txHash
36871
+ };
36872
+ } catch (error) {
36873
+ console.error("Failed to add file with permissions and schema:", error);
36874
+ throw new Error(
36875
+ `Failed to add file with permissions and schema: ${error instanceof Error ? error.message : "Unknown error"}`
36876
+ );
36877
+ }
36878
+ }
37389
36879
  /**
37390
36880
  * Adds a new schema to the DataRefinerRegistry.
37391
36881
  *
36882
+ * @deprecated Use vana.schemas.create() instead for the high-level API with automatic IPFS upload
37392
36883
  * @param params - Schema parameters including name, type, and definition URL
37393
36884
  * @returns Promise resolving to the new schema ID and transaction hash
37394
36885
  */
@@ -37447,6 +36938,7 @@ var DataController = class {
37447
36938
  /**
37448
36939
  * Retrieves a schema by its ID.
37449
36940
  *
36941
+ * @deprecated Use vana.schemas.get() instead
37450
36942
  * @param schemaId - The schema ID to retrieve
37451
36943
  * @returns Promise resolving to the schema information
37452
36944
  */
@@ -37472,11 +36964,15 @@ var DataController = class {
37472
36964
  if (!schemaData) {
37473
36965
  throw new Error("Schema not found");
37474
36966
  }
36967
+ const schemaObj = schemaData;
36968
+ if (!schemaObj.name || !schemaObj.typ || !schemaObj.definitionUrl) {
36969
+ throw new Error("Incomplete schema data");
36970
+ }
37475
36971
  return {
37476
36972
  id: schemaId,
37477
- name: schemaData.name,
37478
- type: schemaData.typ,
37479
- definitionUrl: schemaData.definitionUrl
36973
+ name: schemaObj.name,
36974
+ type: schemaObj.typ,
36975
+ definitionUrl: schemaObj.definitionUrl
37480
36976
  };
37481
36977
  } catch (error) {
37482
36978
  console.error("Failed to get schema:", error);
@@ -37488,6 +36984,7 @@ var DataController = class {
37488
36984
  /**
37489
36985
  * Gets the total number of schemas in the registry.
37490
36986
  *
36987
+ * @deprecated Use vana.schemas.count() instead
37491
36988
  * @returns Promise resolving to the total schema count
37492
36989
  */
37493
36990
  async getSchemasCount() {
@@ -37533,7 +37030,7 @@ var DataController = class {
37533
37030
  const txHash = await this.context.walletClient.writeContract({
37534
37031
  address: dataRefinerRegistryAddress,
37535
37032
  abi: dataRefinerRegistryAbi,
37536
- functionName: "addRefiner",
37033
+ functionName: "addRefinerWithSchemaId",
37537
37034
  args: [
37538
37035
  BigInt(params.dlpId),
37539
37036
  params.name,
@@ -37738,7 +37235,7 @@ var DataController = class {
37738
37235
  this.context.walletClient,
37739
37236
  DEFAULT_ENCRYPTION_SEED
37740
37237
  );
37741
- const encryptedData = await encryptUserData(
37238
+ const encryptedData = await encryptBlobWithSignedKey(
37742
37239
  data,
37743
37240
  userEncryptionKey,
37744
37241
  this.context.platform
@@ -37872,25 +37369,6 @@ var DataController = class {
37872
37369
  );
37873
37370
  }
37874
37371
  }
37875
- /**
37876
- * Gets the trusted server public key for a given server address.
37877
- * This method reads from the permissions contract to find servers and their public keys.
37878
- *
37879
- * @param serverAddress - The address of the trusted server
37880
- * @returns Promise resolving to the server's public key
37881
- */
37882
- async getTrustedServerPublicKey(serverAddress) {
37883
- try {
37884
- return await this.serverController.getTrustedServerPublicKey(
37885
- serverAddress
37886
- );
37887
- } catch (error) {
37888
- console.error("Failed to get trusted server public key:", error);
37889
- throw new Error(
37890
- `Failed to get trusted server public key: ${error instanceof Error ? error.message : "Unknown error"}`
37891
- );
37892
- }
37893
- }
37894
37372
  /**
37895
37373
  * Decrypts a file that the user has permission to access using their private key.
37896
37374
  *
@@ -37921,12 +37399,13 @@ var DataController = class {
37921
37399
  privateKey,
37922
37400
  this.context.platform
37923
37401
  );
37924
- const response = await fetch(this.convertToDownloadUrl(file.url));
37925
- if (!response.ok) {
37926
- throw new Error(`Failed to download file: ${response.statusText}`);
37402
+ let encryptedData;
37403
+ if (file.url.startsWith("ipfs://")) {
37404
+ encryptedData = await this.fetchFromIPFS(file.url);
37405
+ } else {
37406
+ encryptedData = await this.fetch(file.url);
37927
37407
  }
37928
- const encryptedData = await response.blob();
37929
- const decryptedData = await decryptUserData(
37408
+ const decryptedData = await decryptBlobWithSignedKey(
37930
37409
  encryptedData,
37931
37410
  userEncryptionKey,
37932
37411
  this.context.platform
@@ -37939,6 +37418,162 @@ var DataController = class {
37939
37418
  );
37940
37419
  }
37941
37420
  }
37421
+ /**
37422
+ * Simple network-agnostic fetch utility for retrieving file content.
37423
+ *
37424
+ * @remarks
37425
+ * This is a thin wrapper around the global fetch API that returns the response as a Blob.
37426
+ * It provides a consistent interface for fetching encrypted content before decryption.
37427
+ * For IPFS URLs, consider using fetchFromIPFS for better reliability.
37428
+ *
37429
+ * @param url - The URL to fetch content from
37430
+ * @returns Promise resolving to the fetched content as a Blob
37431
+ * @throws {Error} When the fetch fails or returns a non-ok response
37432
+ *
37433
+ * @example
37434
+ * ```typescript
37435
+ * // Fetch and decrypt a file
37436
+ * const encryptionKey = await generateEncryptionKey(walletClient);
37437
+ * const encryptedBlob = await vana.data.fetch(file.url);
37438
+ * const decryptedBlob = await decryptBlob(encryptedBlob, encryptionKey, platform);
37439
+ *
37440
+ * // With custom headers for authentication
37441
+ * const response = await fetch(file.url, {
37442
+ * headers: { 'Authorization': 'Bearer token' }
37443
+ * });
37444
+ * const encryptedBlob = await response.blob();
37445
+ * ```
37446
+ */
37447
+ async fetch(url) {
37448
+ try {
37449
+ const response = await fetch(url);
37450
+ if (!response.ok) {
37451
+ throw new Error(
37452
+ `HTTP error! status: ${response.status} ${response.statusText}`
37453
+ );
37454
+ }
37455
+ const blob = await response.blob();
37456
+ if (blob.size === 0) {
37457
+ throw new Error("Empty response");
37458
+ }
37459
+ return blob;
37460
+ } catch (error) {
37461
+ if (error instanceof TypeError && error.message.includes("fetch")) {
37462
+ throw new Error(
37463
+ `Network error: Failed to fetch from ${url}. The URL may be invalid or the server may not be accessible.`
37464
+ );
37465
+ }
37466
+ throw error;
37467
+ }
37468
+ }
37469
+ /**
37470
+ * Specialized IPFS fetcher with gateway fallback mechanism.
37471
+ *
37472
+ * @remarks
37473
+ * This method provides robust IPFS content fetching by trying multiple gateways
37474
+ * in sequence until one succeeds. It supports both ipfs:// URLs and raw CIDs.
37475
+ *
37476
+ * The default gateway list includes public gateways, but you should provide
37477
+ * your own gateways for production use to ensure reliability and privacy.
37478
+ *
37479
+ * @param url - The IPFS URL (ipfs://...) or CID to fetch
37480
+ * @param options - Optional configuration
37481
+ * @param options.gateways - Array of IPFS gateway URLs to try (must end with /)
37482
+ * @returns Promise resolving to the fetched content as a Blob
37483
+ * @throws {Error} When all gateways fail to fetch the content
37484
+ *
37485
+ * @example
37486
+ * ```typescript
37487
+ * // Fetch from IPFS with custom gateways
37488
+ * const encryptedBlob = await vana.data.fetchFromIPFS(file.url, {
37489
+ * gateways: [
37490
+ * 'https://my-private-gateway.com/ipfs/',
37491
+ * 'https://dweb.link/ipfs/',
37492
+ * 'https://ipfs.io/ipfs/'
37493
+ * ]
37494
+ * });
37495
+ *
37496
+ * // Decrypt the fetched content
37497
+ * const encryptionKey = await generateEncryptionKey(walletClient);
37498
+ * const decryptedBlob = await decryptBlob(encryptedBlob, encryptionKey, platform);
37499
+ *
37500
+ * // With raw CID
37501
+ * const blob = await vana.data.fetchFromIPFS('QmXxx...', {
37502
+ * gateways: ['https://ipfs.io/ipfs/']
37503
+ * });
37504
+ * ```
37505
+ */
37506
+ async fetchFromIPFS(url, options) {
37507
+ const defaultGateways = [
37508
+ "https://dweb.link/ipfs/",
37509
+ "https://ipfs.io/ipfs/"
37510
+ ];
37511
+ const gateways = options?.gateways || this.context.ipfsGateways || defaultGateways;
37512
+ let cid;
37513
+ if (url.startsWith("ipfs://")) {
37514
+ cid = url.replace("ipfs://", "");
37515
+ } else if (url.startsWith("Qm") || url.startsWith("bafy")) {
37516
+ cid = url;
37517
+ } else {
37518
+ throw new Error(
37519
+ `Invalid IPFS URL format. Expected ipfs://... or a raw CID, got: ${url}`
37520
+ );
37521
+ }
37522
+ const errors = [];
37523
+ for (let i = 0; i < gateways.length; i++) {
37524
+ const gateway = gateways[i];
37525
+ const isLastGateway = i === gateways.length - 1;
37526
+ const gatewayUrl = gateway.endsWith("/") ? `${gateway}${cid}` : `${gateway}/${cid}`;
37527
+ try {
37528
+ console.debug(`Trying IPFS gateway: ${gatewayUrl}`);
37529
+ const response = await fetch(gatewayUrl);
37530
+ if (response.ok) {
37531
+ const blob = await response.blob();
37532
+ if (blob.size > 0) {
37533
+ console.debug(`Successfully fetched from gateway: ${gateway}`);
37534
+ return blob;
37535
+ } else {
37536
+ if (isLastGateway) {
37537
+ throw new Error("Empty response");
37538
+ }
37539
+ errors.push({
37540
+ gateway,
37541
+ error: "Empty response"
37542
+ });
37543
+ }
37544
+ } else {
37545
+ if (isLastGateway) {
37546
+ if (response.status === 403) {
37547
+ throw new Error(`HTTP error! status: 403 ${response.statusText}`);
37548
+ } else if (response.status === 404) {
37549
+ throw new Error(`HTTP error! status: 404 ${response.statusText}`);
37550
+ } else {
37551
+ throw new Error(
37552
+ `HTTP error! status: ${response.status} ${response.statusText}`
37553
+ );
37554
+ }
37555
+ }
37556
+ errors.push({
37557
+ gateway,
37558
+ error: `HTTP ${response.status} ${response.statusText}`
37559
+ });
37560
+ }
37561
+ } catch (error) {
37562
+ if (isLastGateway && error instanceof Error && (error.message.includes("Empty response") || error.message.includes("HTTP error!"))) {
37563
+ throw error;
37564
+ }
37565
+ errors.push({
37566
+ gateway,
37567
+ error: error instanceof Error ? error.message : "Unknown error"
37568
+ });
37569
+ }
37570
+ }
37571
+ const errorDetails = errors.map((e) => `${e.gateway}: ${e.error}`).join("\n ");
37572
+ throw new Error(
37573
+ `Failed to fetch IPFS content ${cid} from all gateways:
37574
+ ${errorDetails}`
37575
+ );
37576
+ }
37942
37577
  /**
37943
37578
  * Validates a data schema against the Vana meta-schema.
37944
37579
  *
@@ -38060,15 +37695,595 @@ var DataController = class {
38060
37695
  }
38061
37696
  };
38062
37697
 
37698
+ // src/controllers/schemas.ts
37699
+ var import_viem3 = require("viem");
37700
+ var SchemaController = class {
37701
+ constructor(context) {
37702
+ this.context = context;
37703
+ }
37704
+ /**
37705
+ * Creates a new schema with automatic validation and IPFS upload.
37706
+ *
37707
+ * @remarks
37708
+ * This is the primary method for creating schemas on the Vana network. It handles
37709
+ * the complete workflow including schema validation, IPFS upload, and blockchain
37710
+ * registration. The schema definition is stored unencrypted on IPFS to enable
37711
+ * public access and reusability.
37712
+ *
37713
+ * The method automatically:
37714
+ * - Validates the schema definition against the Vana metaschema
37715
+ * - Uploads the definition to IPFS to generate a permanent URL
37716
+ * - Registers the schema on the blockchain with the generated URL
37717
+ *
37718
+ * @param params - Schema creation parameters including name, type, and definition
37719
+ * @returns Promise resolving to creation results with schema ID and transaction hash
37720
+ * @throws {SchemaValidationError} When the schema definition is invalid
37721
+ * @throws {Error} When IPFS upload or blockchain registration fails
37722
+ * @example
37723
+ * ```typescript
37724
+ * // Create a JSON schema for user profiles
37725
+ * const result = await vana.schemas.create({
37726
+ * name: "User Profile",
37727
+ * type: "personal",
37728
+ * definition: {
37729
+ * type: "object",
37730
+ * properties: {
37731
+ * name: { type: "string" },
37732
+ * age: { type: "number", minimum: 0 }
37733
+ * },
37734
+ * required: ["name"]
37735
+ * }
37736
+ * });
37737
+ *
37738
+ * console.log(`Schema created with ID: ${result.schemaId}`);
37739
+ * ```
37740
+ */
37741
+ async create(params) {
37742
+ const { name, type, definition } = params;
37743
+ try {
37744
+ let schemaDefinition;
37745
+ if (typeof definition === "string") {
37746
+ try {
37747
+ schemaDefinition = JSON.parse(definition);
37748
+ } catch {
37749
+ throw new SchemaValidationError(
37750
+ "Invalid JSON in schema definition",
37751
+ []
37752
+ );
37753
+ }
37754
+ } else {
37755
+ schemaDefinition = definition;
37756
+ }
37757
+ const dataSchema = {
37758
+ name,
37759
+ version: "1.0.0",
37760
+ dialect: "json",
37761
+ schema: schemaDefinition
37762
+ };
37763
+ validateDataSchema(dataSchema);
37764
+ if (!this.context.storageManager) {
37765
+ if (this.context.validateStorageRequired) {
37766
+ this.context.validateStorageRequired();
37767
+ throw new Error("Storage validation failed");
37768
+ } else {
37769
+ throw new Error(
37770
+ "Storage manager not configured. Please provide storage providers in VanaConfig."
37771
+ );
37772
+ }
37773
+ }
37774
+ const schemaBlob = new Blob([JSON.stringify(schemaDefinition)], {
37775
+ type: "application/json"
37776
+ });
37777
+ const uploadResult = await this.context.storageManager.upload(
37778
+ schemaBlob,
37779
+ `${name.replace(/[^a-zA-Z0-9]/g, "_")}.json`,
37780
+ "ipfs"
37781
+ // Use IPFS for public schema storage
37782
+ );
37783
+ const chainId = this.context.walletClient.chain?.id;
37784
+ if (!chainId) {
37785
+ throw new Error("Chain ID not available");
37786
+ }
37787
+ const dataRefinerRegistryAddress = getContractAddress(
37788
+ chainId,
37789
+ "DataRefinerRegistry"
37790
+ );
37791
+ const dataRefinerRegistryAbi = getAbi("DataRefinerRegistry");
37792
+ const userAddress = await this.getUserAddress();
37793
+ const txHash = await this.context.walletClient.writeContract({
37794
+ address: dataRefinerRegistryAddress,
37795
+ abi: dataRefinerRegistryAbi,
37796
+ functionName: "addSchema",
37797
+ args: [name, type, uploadResult.url],
37798
+ account: this.context.walletClient.account || userAddress,
37799
+ chain: this.context.walletClient.chain || null
37800
+ });
37801
+ const receipt = await this.context.publicClient.waitForTransactionReceipt(
37802
+ {
37803
+ hash: txHash,
37804
+ timeout: 3e4
37805
+ // 30 seconds timeout
37806
+ }
37807
+ );
37808
+ let schemaId = 0;
37809
+ for (const log of receipt.logs) {
37810
+ try {
37811
+ const decoded = (0, import_viem3.decodeEventLog)({
37812
+ abi: dataRefinerRegistryAbi,
37813
+ data: log.data,
37814
+ topics: log.topics
37815
+ });
37816
+ if (decoded.eventName === "SchemaAdded") {
37817
+ schemaId = Number(decoded.args.schemaId);
37818
+ break;
37819
+ }
37820
+ } catch {
37821
+ }
37822
+ }
37823
+ return {
37824
+ schemaId,
37825
+ definitionUrl: uploadResult.url,
37826
+ transactionHash: txHash
37827
+ };
37828
+ } catch (error) {
37829
+ if (error instanceof SchemaValidationError) {
37830
+ throw error;
37831
+ }
37832
+ throw new Error(
37833
+ `Schema creation failed: ${error instanceof Error ? error.message : "Unknown error"}`
37834
+ );
37835
+ }
37836
+ }
37837
+ /**
37838
+ * Retrieves a schema by its ID.
37839
+ *
37840
+ * @param schemaId - The ID of the schema to retrieve
37841
+ * @returns Promise resolving to the schema object
37842
+ * @throws {Error} When the schema is not found or chain is unavailable
37843
+ * @example
37844
+ * ```typescript
37845
+ * const schema = await vana.schemas.get(1);
37846
+ * console.log(`Schema: ${schema.name} (${schema.type})`);
37847
+ * ```
37848
+ */
37849
+ async get(schemaId) {
37850
+ try {
37851
+ const chainId = this.context.walletClient.chain?.id;
37852
+ if (!chainId) {
37853
+ throw new Error("Chain ID not available");
37854
+ }
37855
+ const dataRefinerRegistryAddress = getContractAddress(
37856
+ chainId,
37857
+ "DataRefinerRegistry"
37858
+ );
37859
+ const dataRefinerRegistryAbi = getAbi("DataRefinerRegistry");
37860
+ const dataRefinerRegistry = (0, import_viem3.getContract)({
37861
+ address: dataRefinerRegistryAddress,
37862
+ abi: dataRefinerRegistryAbi,
37863
+ client: this.context.publicClient
37864
+ });
37865
+ const schemaData = await dataRefinerRegistry.read.schemas([
37866
+ BigInt(schemaId)
37867
+ ]);
37868
+ if (!schemaData) {
37869
+ throw new Error(`Schema with ID ${schemaId} not found`);
37870
+ }
37871
+ const schemaObj = schemaData;
37872
+ if (!schemaObj.name || !schemaObj.typ || !schemaObj.definitionUrl) {
37873
+ throw new Error("Incomplete schema data");
37874
+ }
37875
+ return {
37876
+ id: schemaId,
37877
+ name: schemaObj.name,
37878
+ type: schemaObj.typ,
37879
+ definitionUrl: schemaObj.definitionUrl
37880
+ };
37881
+ } catch (error) {
37882
+ throw new Error(
37883
+ `Failed to get schema: ${error instanceof Error ? error.message : "Unknown error"}`
37884
+ );
37885
+ }
37886
+ }
37887
+ /**
37888
+ * Gets the total number of schemas registered on the network.
37889
+ *
37890
+ * @returns Promise resolving to the total schema count
37891
+ * @throws {Error} When the count cannot be retrieved
37892
+ * @example
37893
+ * ```typescript
37894
+ * const count = await vana.schemas.count();
37895
+ * console.log(`Total schemas: ${count}`);
37896
+ * ```
37897
+ */
37898
+ async count() {
37899
+ try {
37900
+ const chainId = this.context.walletClient.chain?.id;
37901
+ if (!chainId) {
37902
+ throw new Error("Chain ID not available");
37903
+ }
37904
+ const dataRefinerRegistryAddress = getContractAddress(
37905
+ chainId,
37906
+ "DataRefinerRegistry"
37907
+ );
37908
+ const dataRefinerRegistryAbi = getAbi("DataRefinerRegistry");
37909
+ const dataRefinerRegistry = (0, import_viem3.getContract)({
37910
+ address: dataRefinerRegistryAddress,
37911
+ abi: dataRefinerRegistryAbi,
37912
+ client: this.context.publicClient
37913
+ });
37914
+ const count = await dataRefinerRegistry.read.schemasCount();
37915
+ return Number(count);
37916
+ } catch (error) {
37917
+ throw new Error(
37918
+ `Failed to get schemas count: ${error instanceof Error ? error.message : "Unknown error"}`
37919
+ );
37920
+ }
37921
+ }
37922
+ /**
37923
+ * Lists all schemas with pagination.
37924
+ *
37925
+ * @param options - Optional parameters for listing schemas
37926
+ * @param options.limit - Maximum number of schemas to return
37927
+ * @param options.offset - Number of schemas to skip
37928
+ * @returns Promise resolving to an array of schemas
37929
+ * @example
37930
+ * ```typescript
37931
+ * // Get all schemas
37932
+ * const schemas = await vana.schemas.list();
37933
+ *
37934
+ * // Get schemas with pagination
37935
+ * const schemas = await vana.schemas.list({ limit: 10, offset: 0 });
37936
+ * ```
37937
+ */
37938
+ async list(options = {}) {
37939
+ const { limit = 100, offset = 0 } = options;
37940
+ try {
37941
+ const totalCount = await this.count();
37942
+ const schemas = [];
37943
+ const start = offset;
37944
+ const end = Math.min(start + limit, totalCount);
37945
+ for (let i = start; i < end; i++) {
37946
+ try {
37947
+ const schema = await this.get(i + 1);
37948
+ schemas.push(schema);
37949
+ } catch (error) {
37950
+ console.warn(`Failed to retrieve schema ${i + 1}:`, error);
37951
+ }
37952
+ }
37953
+ return schemas;
37954
+ } catch (error) {
37955
+ throw new Error(
37956
+ `Failed to list schemas: ${error instanceof Error ? error.message : "Unknown error"}`
37957
+ );
37958
+ }
37959
+ }
37960
+ /**
37961
+ * Adds a schema using the legacy method (low-level API).
37962
+ *
37963
+ * @deprecated Use create() instead for the high-level API with automatic IPFS upload
37964
+ * @param params - Schema parameters including pre-generated definition URL
37965
+ * @returns Promise resolving to the add schema result
37966
+ */
37967
+ async addSchema(params) {
37968
+ try {
37969
+ const chainId = this.context.walletClient.chain?.id;
37970
+ if (!chainId) {
37971
+ throw new Error("Chain ID not available");
37972
+ }
37973
+ const dataRefinerRegistryAddress = getContractAddress(
37974
+ chainId,
37975
+ "DataRefinerRegistry"
37976
+ );
37977
+ const dataRefinerRegistryAbi = getAbi("DataRefinerRegistry");
37978
+ const userAddress = await this.getUserAddress();
37979
+ const txHash = await this.context.walletClient.writeContract({
37980
+ address: dataRefinerRegistryAddress,
37981
+ abi: dataRefinerRegistryAbi,
37982
+ functionName: "addSchema",
37983
+ args: [params.name, params.type, params.definitionUrl],
37984
+ account: this.context.walletClient.account || userAddress,
37985
+ chain: this.context.walletClient.chain || null
37986
+ });
37987
+ return {
37988
+ schemaId: 0,
37989
+ // TODO: Parse from transaction receipt
37990
+ transactionHash: txHash
37991
+ };
37992
+ } catch (error) {
37993
+ throw new Error(
37994
+ `Failed to add schema: ${error instanceof Error ? error.message : "Unknown error"}`
37995
+ );
37996
+ }
37997
+ }
37998
+ /**
37999
+ * Gets the user's wallet address.
38000
+ *
38001
+ * @private
38002
+ * @returns Promise resolving to the user's address
38003
+ */
38004
+ async getUserAddress() {
38005
+ if (!this.context.walletClient.account) {
38006
+ throw new Error("No wallet account connected");
38007
+ }
38008
+ if (typeof this.context.walletClient.account === "string") {
38009
+ return this.context.walletClient.account;
38010
+ }
38011
+ if (typeof this.context.walletClient.account === "object" && this.context.walletClient.account.address) {
38012
+ return this.context.walletClient.account.address;
38013
+ }
38014
+ throw new Error("Unable to determine wallet address");
38015
+ }
38016
+ };
38017
+
38018
+ // src/controllers/server.ts
38019
+ var ServerController = class {
38020
+ constructor(context) {
38021
+ this.context = context;
38022
+ }
38023
+ PERSONAL_SERVER_BASE_URL = process.env.NEXT_PUBLIC_PERSONAL_SERVER_BASE_URL;
38024
+ async getIdentity(request) {
38025
+ try {
38026
+ const response = await fetch(
38027
+ `${this.PERSONAL_SERVER_BASE_URL}/identity?address=${request.userAddress}`,
38028
+ {
38029
+ method: "GET",
38030
+ headers: {
38031
+ "Content-Type": "application/json"
38032
+ }
38033
+ }
38034
+ );
38035
+ console.debug("\u{1F50D} Debug - getIdentity response", response);
38036
+ if (!response.ok) {
38037
+ const errorText = await response.text();
38038
+ throw new NetworkError(
38039
+ `Local identity API request failed: ${response.status} ${response.statusText} - ${errorText}`
38040
+ );
38041
+ }
38042
+ const serverResponse = await response.json();
38043
+ return {
38044
+ kind: serverResponse.personal_server.kind,
38045
+ address: serverResponse.personal_server.address,
38046
+ public_key: serverResponse.personal_server.public_key,
38047
+ base_url: this.PERSONAL_SERVER_BASE_URL || "",
38048
+ name: "Hosted Vana Server"
38049
+ };
38050
+ } catch (error) {
38051
+ if (error instanceof NetworkError || error instanceof PersonalServerError) {
38052
+ throw error;
38053
+ }
38054
+ throw new PersonalServerError(
38055
+ `Failed to get personal server identity: ${error instanceof Error ? error.message : "Unknown error"}`
38056
+ );
38057
+ }
38058
+ }
38059
+ /**
38060
+ * Creates an operation via the personal server API.
38061
+ *
38062
+ * @remarks
38063
+ * This method submits a computation request to the personal server API.
38064
+ * The response includes the operation ID.
38065
+ * @param params - The request parameters object
38066
+ * @param params.permissionId - The permission ID authorizing this operation
38067
+ * @returns A Promise that resolves to an operation response with status and control URLs
38068
+ * @throws {PersonalServerError} When server request fails or parameters are invalid
38069
+ * @throws {NetworkError} When personal server API communication fails
38070
+ * @example
38071
+ * ```typescript
38072
+ * const response = await vana.server.createOperation({
38073
+ * permissionId: 123,
38074
+ * });
38075
+ *
38076
+ * console.log(`Operation created: ${response.id}`);
38077
+ * ```
38078
+ */
38079
+ async createOperation(params) {
38080
+ try {
38081
+ const requestData = {
38082
+ permission_id: params.permissionId
38083
+ };
38084
+ const requestJson = JSON.stringify(requestData);
38085
+ const signature = await this.createSignature(requestJson);
38086
+ const requestBody = {
38087
+ app_signature: signature,
38088
+ operation_request_json: requestJson
38089
+ };
38090
+ console.debug("\u{1F50D} Debug - createOperation requestBody", requestBody);
38091
+ const response = await this.makeRequest(requestBody);
38092
+ return response;
38093
+ } catch (error) {
38094
+ if (error instanceof Error) {
38095
+ if (error instanceof NetworkError || error instanceof SerializationError || error instanceof SignatureError || error instanceof PersonalServerError) {
38096
+ throw error;
38097
+ }
38098
+ throw new PersonalServerError(
38099
+ `Personal server operation creation failed: ${error.message}`,
38100
+ error
38101
+ );
38102
+ }
38103
+ throw new PersonalServerError(
38104
+ "Personal server operation creation failed with unknown error"
38105
+ );
38106
+ }
38107
+ }
38108
+ /**
38109
+ * Polls the status of a computation request for updates and results.
38110
+ *
38111
+ * @remarks
38112
+ * This method checks the current status of a computation request by querying
38113
+ * the personal server API using the provided operation ID. It returns the current
38114
+ * status, any available output, and error information. The method can be
38115
+ * called periodically until the operation completes or fails.
38116
+ *
38117
+ * Common status values include: `starting`, `processing`, `succeeded`, `failed`, `canceled`.
38118
+ * @param operationId - The operation ID returned from the initial request submission
38119
+ * @returns A Promise that resolves to the current operation response with status and results
38120
+ * @throws {NetworkError} When the polling request fails or returns invalid data
38121
+ * @example
38122
+ * ```typescript
38123
+ * // Poll until completion
38124
+ * let result = await vana.server.getOperation(response.id);
38125
+ *
38126
+ * while (result.status === "processing") {
38127
+ * await new Promise(resolve => setTimeout(resolve, 1000));
38128
+ * result = await vana.server.getOperation(response.id);
38129
+ * }
38130
+ *
38131
+ * if (result.status === "succeeded") {
38132
+ * console.log("Computation completed:", result.output);
38133
+ * }
38134
+ * ```
38135
+ */
38136
+ async getOperation(operationId) {
38137
+ try {
38138
+ console.debug("Polling Operation Status:", operationId);
38139
+ const response = await fetch(
38140
+ `${this.PERSONAL_SERVER_BASE_URL}/operations/${operationId}`,
38141
+ {
38142
+ method: "GET",
38143
+ headers: {
38144
+ "Content-Type": "application/json"
38145
+ }
38146
+ }
38147
+ );
38148
+ if (!response.ok) {
38149
+ const errorText = await response.text();
38150
+ console.debug("Polling Error Response:", {
38151
+ status: response.status,
38152
+ statusText: response.statusText,
38153
+ error: errorText
38154
+ });
38155
+ throw new NetworkError(
38156
+ `Status polling failed: ${response.status} ${response.statusText} - ${errorText}`
38157
+ );
38158
+ }
38159
+ const data = await response.json();
38160
+ console.debug("Polling Success Response:", data);
38161
+ return data;
38162
+ } catch (error) {
38163
+ if (error instanceof NetworkError) {
38164
+ throw error;
38165
+ }
38166
+ throw new NetworkError(
38167
+ `Failed to poll status: ${error instanceof Error ? error.message : "Unknown error"}`
38168
+ );
38169
+ }
38170
+ }
38171
+ async cancelOperation(operationId) {
38172
+ try {
38173
+ const response = await fetch(
38174
+ `${this.PERSONAL_SERVER_BASE_URL}/operations/${operationId}/cancel`,
38175
+ {
38176
+ method: "POST"
38177
+ }
38178
+ );
38179
+ if (!response.ok) {
38180
+ const errorText = await response.text();
38181
+ throw new PersonalServerError(
38182
+ `Failed to cancel operation: ${response.status} ${response.statusText} - ${errorText}`
38183
+ );
38184
+ }
38185
+ } catch (error) {
38186
+ if (error instanceof NetworkError) {
38187
+ throw error;
38188
+ }
38189
+ throw new NetworkError(
38190
+ `Failed to cancel operation: ${error instanceof Error ? error.message : "Unknown error"}`
38191
+ );
38192
+ }
38193
+ }
38194
+ /**
38195
+ * Makes the request to the personal server API.
38196
+ *
38197
+ * @param requestBody - The post request parameters to serialize
38198
+ * @returns JSON string representation of the request data
38199
+ */
38200
+ async makeRequest(requestBody) {
38201
+ try {
38202
+ console.debug("Personal Server Request:", {
38203
+ url: `${this.PERSONAL_SERVER_BASE_URL}/operations`,
38204
+ method: "POST",
38205
+ headers: {
38206
+ "Content-Type": "application/json"
38207
+ },
38208
+ body: requestBody
38209
+ });
38210
+ const response = await fetch(
38211
+ `${this.PERSONAL_SERVER_BASE_URL}/operations`,
38212
+ {
38213
+ method: "POST",
38214
+ headers: {
38215
+ "Content-Type": "application/json"
38216
+ },
38217
+ body: JSON.stringify(requestBody)
38218
+ }
38219
+ );
38220
+ if (!response.ok) {
38221
+ const errorText = await response.text();
38222
+ console.debug("Personal Server Error Response:", {
38223
+ status: response.status,
38224
+ statusText: response.statusText,
38225
+ error: errorText
38226
+ });
38227
+ throw new NetworkError(
38228
+ `Personal server API request failed: ${response.status} ${response.statusText} - ${errorText}`
38229
+ );
38230
+ }
38231
+ const data = await response.json();
38232
+ console.debug("Personal Server Success Response:", data);
38233
+ return data;
38234
+ } catch (error) {
38235
+ if (error instanceof NetworkError) {
38236
+ throw error;
38237
+ }
38238
+ throw new NetworkError(
38239
+ `Failed to make personal server API request: ${error instanceof Error ? error.message : "Unknown error"}`
38240
+ );
38241
+ }
38242
+ }
38243
+ /**
38244
+ * Creates a signature for the request JSON.
38245
+ *
38246
+ * @param requestJson - The JSON string to sign
38247
+ * @returns Promise resolving to the cryptographic signature
38248
+ */
38249
+ async createSignature(requestJson) {
38250
+ try {
38251
+ console.debug("\u{1F50D} Debug - createSignature", requestJson);
38252
+ const client = this.context.applicationClient || this.context.walletClient;
38253
+ const account = client.account;
38254
+ if (!account) {
38255
+ throw new SignatureError("No account available for signing");
38256
+ }
38257
+ if (account.type !== "local") {
38258
+ throw new SignatureError(
38259
+ "Only local accounts are supported for signing"
38260
+ );
38261
+ }
38262
+ console.debug("\u{1F50D} Debug - createSignature account", account);
38263
+ const signature = await account.signMessage({
38264
+ message: requestJson
38265
+ });
38266
+ return signature;
38267
+ } catch (error) {
38268
+ if (error instanceof Error && error.message.includes("User rejected")) {
38269
+ throw new SignatureError("User rejected the signature request");
38270
+ }
38271
+ throw new SignatureError(
38272
+ `Failed to create signature: ${error instanceof Error ? error.message : "Unknown error"}`
38273
+ );
38274
+ }
38275
+ }
38276
+ };
38277
+
38063
38278
  // src/contracts/contractController.ts
38064
- var import_viem5 = require("viem");
38279
+ var import_viem6 = require("viem");
38065
38280
 
38066
38281
  // src/core/client.ts
38067
- var import_viem4 = require("viem");
38282
+ var import_viem5 = require("viem");
38068
38283
 
38069
38284
  // src/config/chains.ts
38070
- var import_viem3 = require("viem");
38071
- var mokshaTestnet = (0, import_viem3.defineChain)({
38285
+ var import_viem4 = require("viem");
38286
+ var mokshaTestnet = (0, import_viem4.defineChain)({
38072
38287
  id: 14800,
38073
38288
  caipNetworkId: "eip155:14800",
38074
38289
  chainNamespace: "eip155",
@@ -38096,7 +38311,7 @@ var mokshaTestnet = (0, import_viem3.defineChain)({
38096
38311
  contracts: {},
38097
38312
  abis: {}
38098
38313
  });
38099
- var vanaMainnet = (0, import_viem3.defineChain)({
38314
+ var vanaMainnet = (0, import_viem4.defineChain)({
38100
38315
  id: 1480,
38101
38316
  caipNetworkId: "eip155:1480",
38102
38317
  chainNamespace: "eip155",
@@ -38138,9 +38353,9 @@ var createClient = (chainId = mokshaTestnet.id) => {
38138
38353
  if (!chain) {
38139
38354
  throw new Error(`Chain ${chainId} not found`);
38140
38355
  }
38141
- _client = (0, import_viem4.createPublicClient)({
38356
+ _client = (0, import_viem5.createPublicClient)({
38142
38357
  chain,
38143
- transport: (0, import_viem4.http)()
38358
+ transport: (0, import_viem5.http)()
38144
38359
  });
38145
38360
  }
38146
38361
  return _client;
@@ -38157,7 +38372,7 @@ function getContractController(contract, client = createClient()) {
38157
38372
  const cacheKey = createCacheKey(contract, chainId);
38158
38373
  let controller = contractCache.get(cacheKey);
38159
38374
  if (!controller) {
38160
- controller = (0, import_viem5.getContract)({
38375
+ controller = (0, import_viem6.getContract)({
38161
38376
  address: getContractAddress(chainId, contract),
38162
38377
  abi: getAbi(contract),
38163
38378
  client
@@ -39771,7 +39986,7 @@ var StorageManager = class {
39771
39986
  };
39772
39987
 
39773
39988
  // src/core.ts
39774
- var import_viem6 = require("viem");
39989
+ var import_viem7 = require("viem");
39775
39990
 
39776
39991
  // src/chains/definitions.ts
39777
39992
  var vanaMainnet2 = {
@@ -39814,7 +40029,7 @@ var moksha = {
39814
40029
  url: "https://moksha.vanascan.io"
39815
40030
  }
39816
40031
  },
39817
- subgraphUrl: "https://api.goldsky.com/api/public/project_cm168cz887zva010j39il7a6p/subgraphs/moksha/7.0.4/gn"
40032
+ subgraphUrl: "https://api.goldsky.com/api/public/project_cm168cz887zva010j39il7a6p/subgraphs/moksha/7.0.3/gn"
39818
40033
  };
39819
40034
  function getChainConfig(chainId) {
39820
40035
  switch (chainId) {
@@ -39826,16 +40041,60 @@ function getChainConfig(chainId) {
39826
40041
  return void 0;
39827
40042
  }
39828
40043
  }
40044
+ var mokshaTestnet2 = moksha;
39829
40045
  function getAllChains() {
39830
40046
  return [vanaMainnet2, moksha];
39831
40047
  }
39832
40048
 
39833
40049
  // src/core.ts
40050
+ var VanaCoreFactory = class {
40051
+ /**
40052
+ * Creates a VanaCore instance that enforces storage requirements at compile time.
40053
+ * Use this factory when you know you'll need storage-dependent operations.
40054
+ *
40055
+ * @param platform - The platform adapter for environment-specific operations
40056
+ * @param config - Configuration that includes required storage providers
40057
+ * @returns VanaCore instance with storage validation
40058
+ * @example
40059
+ * ```typescript
40060
+ * const vanaCore = VanaCoreFactory.createWithStorage(platformAdapter, {
40061
+ * walletClient: myWalletClient,
40062
+ * storage: {
40063
+ * providers: { ipfs: new IPFSStorage() },
40064
+ * defaultProvider: 'ipfs'
40065
+ * }
40066
+ * });
40067
+ * ```
40068
+ */
40069
+ static createWithStorage(platform, config) {
40070
+ const core = new VanaCore(platform, config);
40071
+ return core;
40072
+ }
40073
+ /**
40074
+ * Creates a VanaCore instance without storage requirements.
40075
+ * Storage-dependent operations will fail at runtime if not configured.
40076
+ *
40077
+ * @param platform - The platform adapter for environment-specific operations
40078
+ * @param config - Basic configuration without required storage
40079
+ * @returns VanaCore instance
40080
+ * @example
40081
+ * ```typescript
40082
+ * const vanaCore = VanaCoreFactory.create(platformAdapter, {
40083
+ * walletClient: myWalletClient
40084
+ * });
40085
+ * ```
40086
+ */
40087
+ static create(platform, config) {
40088
+ return new VanaCore(platform, config);
40089
+ }
40090
+ };
39834
40091
  var VanaCore = class {
39835
40092
  /** Manages gasless data access permissions and trusted server registry. */
39836
40093
  permissions;
39837
- /** Handles user data file operations and schema management. */
40094
+ /** Handles user data file operations. */
39838
40095
  data;
40096
+ /** Manages data schemas and refiners. */
40097
+ schemas;
39839
40098
  /** Provides personal server setup and trusted server interactions. */
39840
40099
  server;
39841
40100
  /** Offers low-level access to Vana protocol smart contracts. */
@@ -39844,12 +40103,18 @@ var VanaCore = class {
39844
40103
  platform;
39845
40104
  relayerCallbacks;
39846
40105
  storageManager;
40106
+ hasRequiredStorage;
40107
+ ipfsGateways;
39847
40108
  /**
39848
40109
  * Initializes a new VanaCore client instance with the provided configuration.
39849
40110
  *
39850
40111
  * @remarks
39851
40112
  * The constructor validates the configuration, initializes storage providers if configured,
39852
40113
  * creates wallet and public clients, and sets up all SDK controllers with shared context.
40114
+ *
40115
+ * IMPORTANT: This constructor will validate storage requirements at runtime to fail fast.
40116
+ * Methods that require storage will throw runtime errors if storage is not configured.
40117
+ *
39853
40118
  * @param platform - The platform adapter for environment-specific operations
39854
40119
  * @param config - The configuration object specifying wallet or chain settings
39855
40120
  * @throws {InvalidConfigurationError} When the configuration is invalid or incomplete
@@ -39865,6 +40130,8 @@ var VanaCore = class {
39865
40130
  this.platform = platform;
39866
40131
  this.validateConfig(config);
39867
40132
  this.relayerCallbacks = config.relayerCallbacks;
40133
+ this.ipfsGateways = config.ipfsGateways;
40134
+ this.hasRequiredStorage = hasStorageConfig(config);
39868
40135
  if (config.storage?.providers) {
39869
40136
  this.storageManager = new StorageManager();
39870
40137
  for (const [name, provider] of Object.entries(config.storage.providers)) {
@@ -39895,9 +40162,9 @@ var VanaCore = class {
39895
40162
  `Unsupported chain ID: ${config.chainId}`
39896
40163
  );
39897
40164
  }
39898
- walletClient = (0, import_viem6.createWalletClient)({
40165
+ walletClient = (0, import_viem7.createWalletClient)({
39899
40166
  chain,
39900
- transport: (0, import_viem6.http)(config.rpcUrl || chain.rpcUrls.default.http[0]),
40167
+ transport: (0, import_viem7.http)(config.rpcUrl || chain.rpcUrls.default.http[0]),
39901
40168
  account: config.account
39902
40169
  });
39903
40170
  } else {
@@ -39905,9 +40172,9 @@ var VanaCore = class {
39905
40172
  "Invalid configuration: must be either WalletConfig or ChainConfig"
39906
40173
  );
39907
40174
  }
39908
- const publicClient = (0, import_viem6.createPublicClient)({
40175
+ const publicClient = (0, import_viem7.createPublicClient)({
39909
40176
  chain: walletClient.chain,
39910
- transport: (0, import_viem6.http)()
40177
+ transport: (0, import_viem7.http)()
39911
40178
  });
39912
40179
  const chainConfig = getChainConfig(walletClient.chain.id);
39913
40180
  const subgraphUrl = config.subgraphUrl || chainConfig?.subgraphUrl;
@@ -39919,14 +40186,70 @@ var VanaCore = class {
39919
40186
  relayerCallbacks: this.relayerCallbacks,
39920
40187
  storageManager: this.storageManager,
39921
40188
  subgraphUrl,
39922
- platform: this.platform
40189
+ platform: this.platform,
39923
40190
  // Pass the platform adapter to controllers
40191
+ validateStorageRequired: this.validateStorageRequired.bind(this),
40192
+ hasStorage: this.hasStorage.bind(this),
40193
+ ipfsGateways: this.ipfsGateways
39924
40194
  };
39925
40195
  this.permissions = new PermissionsController(sharedContext);
39926
40196
  this.data = new DataController(sharedContext);
40197
+ this.schemas = new SchemaController(sharedContext);
39927
40198
  this.server = new ServerController(sharedContext);
39928
40199
  this.protocol = new ProtocolController(sharedContext);
39929
40200
  }
40201
+ /**
40202
+ * Validates that storage is available for storage-dependent operations.
40203
+ * This method enforces the fail-fast principle by checking storage availability
40204
+ * at method call time rather than during expensive operations.
40205
+ *
40206
+ * @throws {InvalidConfigurationError} When storage is required but not configured
40207
+ * @example
40208
+ * ```typescript
40209
+ * // This will throw if storage is not configured
40210
+ * vana.validateStorageRequired();
40211
+ * await vana.data.uploadFile(file); // Safe to proceed
40212
+ * ```
40213
+ */
40214
+ validateStorageRequired() {
40215
+ if (!this.hasRequiredStorage) {
40216
+ throw new InvalidConfigurationError(
40217
+ "Storage configuration is required for this operation. Please configure storage providers in VanaConfig.storage, provide a relayerCallbacks.storeGrantFile implementation, or pass pre-stored URLs to avoid this dependency. \n\nFor better type safety, consider using VanaCoreFactory.createWithStorage() with VanaConfigWithStorage to catch this error at compile time."
40218
+ );
40219
+ }
40220
+ }
40221
+ /**
40222
+ * Checks whether storage is configured without throwing an error.
40223
+ *
40224
+ * @returns True if storage is properly configured
40225
+ * @example
40226
+ * ```typescript
40227
+ * if (vana.hasStorage()) {
40228
+ * await vana.data.uploadFile(file);
40229
+ * } else {
40230
+ * console.warn('Storage not configured - using pre-stored URLs only');
40231
+ * }
40232
+ * ```
40233
+ */
40234
+ hasStorage() {
40235
+ return this.hasRequiredStorage;
40236
+ }
40237
+ /**
40238
+ * Type guard to check if this instance has storage enabled at compile time.
40239
+ * Use this when you need TypeScript to understand that storage is available.
40240
+ *
40241
+ * @returns True if storage is configured, with type narrowing
40242
+ * @example
40243
+ * ```typescript
40244
+ * if (vana.isStorageEnabled()) {
40245
+ * // TypeScript knows storage is available here
40246
+ * await vana.data.uploadFile(file);
40247
+ * }
40248
+ * ```
40249
+ */
40250
+ isStorageEnabled() {
40251
+ return this.hasRequiredStorage;
40252
+ }
39930
40253
  /**
39931
40254
  * Validates the provided configuration object against all requirements.
39932
40255
  *
@@ -40115,23 +40438,23 @@ var VanaCore = class {
40115
40438
  return this.platform;
40116
40439
  }
40117
40440
  /**
40118
- * Encrypts user data using the Vana protocol standard encryption.
40441
+ * Encrypts data using the Vana protocol standard encryption.
40119
40442
  * This method automatically uses the correct platform adapter for the current environment.
40120
40443
  *
40121
40444
  * @param data The data to encrypt (string or Blob)
40122
- * @param walletSignature The wallet signature to use as encryption key
40445
+ * @param key The key to use as encryption key
40123
40446
  * @returns The encrypted data as Blob
40124
40447
  * @example
40125
40448
  * ```typescript
40126
40449
  * const encryptionKey = await generateEncryptionKey(walletClient);
40127
- * const encrypted = await vana.encryptUserData("sensitive data", encryptionKey);
40450
+ * const encrypted = await vana.encryptBlob("sensitive data", encryptionKey);
40128
40451
  * ```
40129
40452
  */
40130
- async encryptUserData(data, walletSignature) {
40131
- return encryptUserData(data, walletSignature, this.platform);
40453
+ async encryptBlob(data, key) {
40454
+ return encryptBlobWithSignedKey(data, key, this.platform);
40132
40455
  }
40133
40456
  /**
40134
- * Decrypts user data using the Vana protocol standard decryption.
40457
+ * Decrypts data that was encrypted using the Vana protocol.
40135
40458
  * This method automatically uses the correct platform adapter for the current environment.
40136
40459
  *
40137
40460
  * @param encryptedData The encrypted data (string or Blob)
@@ -40140,25 +40463,29 @@ var VanaCore = class {
40140
40463
  * @example
40141
40464
  * ```typescript
40142
40465
  * const encryptionKey = await generateEncryptionKey(walletClient);
40143
- * const decrypted = await vana.decryptUserData(encryptedData, encryptionKey);
40466
+ * const decrypted = await vana.decryptBlob(encryptedData, encryptionKey);
40144
40467
  * const text = await decrypted.text();
40145
40468
  * ```
40146
40469
  */
40147
- async decryptUserData(encryptedData, walletSignature) {
40148
- return decryptUserData(encryptedData, walletSignature, this.platform);
40470
+ async decryptBlob(encryptedData, walletSignature) {
40471
+ return decryptBlobWithSignedKey(
40472
+ encryptedData,
40473
+ walletSignature,
40474
+ this.platform
40475
+ );
40149
40476
  }
40150
40477
  };
40151
40478
 
40152
40479
  // src/utils/formatters.ts
40153
- var import_viem7 = require("viem");
40480
+ var import_viem8 = require("viem");
40154
40481
  function formatNumber(value) {
40155
40482
  return Number(value);
40156
40483
  }
40157
40484
  function formatEth(wei, decimals = 4) {
40158
- return (0, import_viem7.formatEther)(BigInt(wei.toString())).slice(0, decimals + 2);
40485
+ return (0, import_viem8.formatEther)(BigInt(wei.toString())).slice(0, decimals + 2);
40159
40486
  }
40160
40487
  function formatToken(amount, decimals = 18, displayDecimals = 4) {
40161
- const value = (0, import_viem7.formatUnits)(BigInt(amount.toString()), decimals);
40488
+ const value = (0, import_viem8.formatUnits)(BigInt(amount.toString()), decimals);
40162
40489
  const parts = value.split(".");
40163
40490
  if (parts.length === 1) {
40164
40491
  return parts[0];
@@ -40176,7 +40503,7 @@ function createValidatedGrant(params) {
40176
40503
  try {
40177
40504
  validateGrant(grantFile, {
40178
40505
  schema: true,
40179
- grantee: params.to,
40506
+ grantee: params.grantee,
40180
40507
  operation: params.operation
40181
40508
  });
40182
40509
  } catch (error) {
@@ -40590,10 +40917,10 @@ var CircuitBreaker = class {
40590
40917
  };
40591
40918
 
40592
40919
  // src/server/handler.ts
40593
- var import_viem8 = require("viem");
40920
+ var import_viem9 = require("viem");
40594
40921
  async function handleRelayerRequest(sdk, payload) {
40595
40922
  const { typedData, signature, expectedUserAddress } = payload;
40596
- const signerAddress = await (0, import_viem8.recoverTypedDataAddress)({
40923
+ const signerAddress = await (0, import_viem9.recoverTypedDataAddress)({
40597
40924
  domain: typedData.domain,
40598
40925
  types: typedData.types,
40599
40926
  primaryType: typedData.primaryType,
@@ -41039,25 +41366,15 @@ var ApiClient = class {
41039
41366
  };
41040
41367
 
41041
41368
  // src/index.node.ts
41042
- var VanaNode = class extends VanaCore {
41043
- /**
41044
- * Creates a Vana SDK instance configured for Node.js environments.
41045
- *
41046
- * @param config - SDK configuration object (wallet client or chain config)
41047
- * @example
41048
- * ```typescript
41049
- * // With wallet client
41050
- * const vana = new Vana({ walletClient });
41051
- *
41052
- * // With chain configuration
41053
- * const vana = new Vana({ chainId: 14800, account });
41054
- * ```
41055
- */
41369
+ var VanaNodeImpl = class extends VanaCore {
41056
41370
  constructor(config) {
41057
41371
  super(new NodePlatformAdapter(), config);
41058
41372
  }
41059
41373
  };
41060
- var index_node_default = VanaNode;
41374
+ function Vana(config) {
41375
+ return new VanaNodeImpl(config);
41376
+ }
41377
+ var index_node_default = Vana;
41061
41378
  // Annotate the CommonJS export names for ESM import in node:
41062
41379
  0 && (module.exports = {
41063
41380
  ApiClient,
@@ -41094,6 +41411,7 @@ var index_node_default = VanaNode;
41094
41411
  RateLimiter,
41095
41412
  RelayerError,
41096
41413
  RetryUtility,
41414
+ SchemaController,
41097
41415
  SchemaValidationError,
41098
41416
  SchemaValidator,
41099
41417
  SerializationError,
@@ -41106,8 +41424,9 @@ var index_node_default = VanaNode;
41106
41424
  UserRejectedRequestError,
41107
41425
  Vana,
41108
41426
  VanaCore,
41427
+ VanaCoreFactory,
41109
41428
  VanaError,
41110
- VanaNode,
41429
+ VanaNodeImpl,
41111
41430
  __contractCache,
41112
41431
  chains,
41113
41432
  checkGrantAccess,
@@ -41122,12 +41441,12 @@ var index_node_default = VanaNode;
41122
41441
  createPlatformAdapterFor,
41123
41442
  createPlatformAdapterSafe,
41124
41443
  createValidatedGrant,
41125
- decryptUserData,
41444
+ decryptBlobWithSignedKey,
41126
41445
  decryptWithPrivateKey,
41127
41446
  decryptWithWalletPrivateKey,
41128
41447
  detectPlatform,
41448
+ encryptBlobWithSignedKey,
41129
41449
  encryptFileKey,
41130
- encryptUserData,
41131
41450
  encryptWithWalletPublicKey,
41132
41451
  extractIpfsHash,
41133
41452
  fetchAndValidateSchema,
@@ -41152,9 +41471,7 @@ var index_node_default = VanaNode;
41152
41471
  handleRelayerRequest,
41153
41472
  isAPIResponse,
41154
41473
  isGrantExpired,
41155
- isIdentityServerOutput,
41156
41474
  isIpfsUrl,
41157
- isPersonalServerOutput,
41158
41475
  isPlatformSupported,
41159
41476
  isReplicateAPIResponse,
41160
41477
  moksha,