@opendatalabs/vana-sdk 0.1.0-alpha.793d5ba → 0.1.0-alpha.8eb4e46

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.
@@ -141,65 +141,9 @@ var init_browser = __esm({
141
141
  try {
142
142
  const eccrypto = await import("eccrypto-js");
143
143
  const uncompressedKey = processWalletPublicKey(publicKey);
144
- const iv = Buffer.from([
145
- 1,
146
- 2,
147
- 3,
148
- 4,
149
- 5,
150
- 6,
151
- 7,
152
- 8,
153
- 9,
154
- 10,
155
- 11,
156
- 12,
157
- 13,
158
- 14,
159
- 15,
160
- 16
161
- ]);
162
- const ephemeralKey = Buffer.from([
163
- 17,
164
- 34,
165
- 51,
166
- 68,
167
- 85,
168
- 102,
169
- 119,
170
- 136,
171
- 153,
172
- 170,
173
- 187,
174
- 204,
175
- 221,
176
- 238,
177
- 255,
178
- 0,
179
- 16,
180
- 32,
181
- 48,
182
- 64,
183
- 80,
184
- 96,
185
- 112,
186
- 128,
187
- 144,
188
- 160,
189
- 176,
190
- 192,
191
- 208,
192
- 224,
193
- 240,
194
- 0
195
- ]);
196
144
  const encryptedBuffer = await eccrypto.encrypt(
197
145
  uncompressedKey,
198
- Buffer.from(data),
199
- {
200
- iv,
201
- ephemPrivateKey: ephemeralKey
202
- }
146
+ Buffer.from(data)
203
147
  );
204
148
  const result = Buffer.concat([
205
149
  encryptedBuffer.iv,
@@ -446,6 +390,9 @@ function isWalletConfig(config) {
446
390
  function isChainConfig(config) {
447
391
  return "chainId" in config && !("walletClient" in config);
448
392
  }
393
+ function hasStorageConfig(config) {
394
+ return config.storage?.providers !== void 0 && Object.keys(config.storage.providers).length > 0;
395
+ }
449
396
 
450
397
  // src/types/chains.ts
451
398
  function isVanaChainId(chainId) {
@@ -741,55 +688,6 @@ function isReplicateAPIResponse(value) {
741
688
  obj.status
742
689
  );
743
690
  }
744
- function isIdentityServerOutput(value) {
745
- console.debug("\u{1F50D} Type Guard: Checking value:", value);
746
- console.debug("\u{1F50D} Type Guard: Value type:", typeof value);
747
- if (typeof value !== "object" || value === null) {
748
- console.debug("\u{1F50D} Type Guard: Failed - not object or null");
749
- return false;
750
- }
751
- const obj = value;
752
- console.debug("\u{1F50D} Type Guard: Object keys:", Object.keys(obj));
753
- console.debug(
754
- "\u{1F50D} Type Guard: user_address:",
755
- obj.user_address,
756
- typeof obj.user_address
757
- );
758
- if (typeof obj.user_address !== "string") {
759
- console.debug("\u{1F50D} Type Guard: Failed - user_address not string");
760
- return false;
761
- }
762
- console.debug(
763
- "\u{1F50D} Type Guard: personal_server:",
764
- obj.personal_server,
765
- typeof obj.personal_server
766
- );
767
- if (typeof obj.personal_server !== "object" || obj.personal_server === null) {
768
- console.debug("\u{1F50D} Type Guard: Failed - personal_server not object or null");
769
- return false;
770
- }
771
- const personalServer = obj.personal_server;
772
- console.debug(
773
- "\u{1F50D} Type Guard: Personal server keys:",
774
- Object.keys(personalServer)
775
- );
776
- console.debug("\u{1F50D} Type Guard: address:", personalServer.address);
777
- console.debug("\u{1F50D} Type Guard: public_key:", personalServer.public_key);
778
- const hasAddress = "address" in personalServer;
779
- const hasPublicKey = "public_key" in personalServer;
780
- console.debug(
781
- "\u{1F50D} Type Guard: Has address:",
782
- hasAddress,
783
- "Has public_key:",
784
- hasPublicKey
785
- );
786
- return hasAddress && hasPublicKey;
787
- }
788
- function isPersonalServerOutput(value) {
789
- if (typeof value !== "object" || value === null) return false;
790
- const obj = value;
791
- return "user_address" in obj && "identity" in obj && typeof obj.user_address === "string" && typeof obj.identity === "object";
792
- }
793
691
  function isAPIResponse(value) {
794
692
  if (typeof value !== "object" || value === null) return false;
795
693
  const obj = value;
@@ -7820,6 +7718,12 @@ var DataRefinerRegistryABI = [
7820
7718
  name: "schemaId",
7821
7719
  type: "uint256"
7822
7720
  },
7721
+ {
7722
+ indexed: false,
7723
+ internalType: "string",
7724
+ name: "schemaDefinitionUrl",
7725
+ type: "string"
7726
+ },
7823
7727
  {
7824
7728
  indexed: false,
7825
7729
  internalType: "string",
@@ -7923,7 +7827,7 @@ var DataRefinerRegistryABI = [
7923
7827
  {
7924
7828
  indexed: false,
7925
7829
  internalType: "string",
7926
- name: "typ",
7830
+ name: "dialect",
7927
7831
  type: "string"
7928
7832
  },
7929
7833
  {
@@ -8019,6 +7923,40 @@ var DataRefinerRegistryABI = [
8019
7923
  stateMutability: "nonpayable",
8020
7924
  type: "function"
8021
7925
  },
7926
+ {
7927
+ inputs: [
7928
+ {
7929
+ internalType: "uint256",
7930
+ name: "dlpId",
7931
+ type: "uint256"
7932
+ },
7933
+ {
7934
+ internalType: "string",
7935
+ name: "name",
7936
+ type: "string"
7937
+ },
7938
+ {
7939
+ internalType: "string",
7940
+ name: "schemaDefinitionUrl",
7941
+ type: "string"
7942
+ },
7943
+ {
7944
+ internalType: "string",
7945
+ name: "refinementInstructionUrl",
7946
+ type: "string"
7947
+ }
7948
+ ],
7949
+ name: "addRefiner",
7950
+ outputs: [
7951
+ {
7952
+ internalType: "uint256",
7953
+ name: "",
7954
+ type: "uint256"
7955
+ }
7956
+ ],
7957
+ stateMutability: "nonpayable",
7958
+ type: "function"
7959
+ },
8022
7960
  {
8023
7961
  inputs: [
8024
7962
  {
@@ -8042,7 +7980,7 @@ var DataRefinerRegistryABI = [
8042
7980
  type: "string"
8043
7981
  }
8044
7982
  ],
8045
- name: "addRefiner",
7983
+ name: "addRefinerWithSchemaId",
8046
7984
  outputs: [
8047
7985
  {
8048
7986
  internalType: "uint256",
@@ -8062,7 +8000,7 @@ var DataRefinerRegistryABI = [
8062
8000
  },
8063
8001
  {
8064
8002
  internalType: "string",
8065
- name: "typ",
8003
+ name: "dialect",
8066
8004
  type: "string"
8067
8005
  },
8068
8006
  {
@@ -8425,7 +8363,7 @@ var DataRefinerRegistryABI = [
8425
8363
  },
8426
8364
  {
8427
8365
  internalType: "string",
8428
- name: "typ",
8366
+ name: "dialect",
8429
8367
  type: "string"
8430
8368
  },
8431
8369
  {
@@ -33342,7 +33280,7 @@ function getAbi(contract) {
33342
33280
  import { keccak256, toHex } from "viem";
33343
33281
  function createGrantFile(params) {
33344
33282
  const grantFile = {
33345
- grantee: params.to,
33283
+ grantee: params.grantee,
33346
33284
  operation: params.operation,
33347
33285
  parameters: params.parameters
33348
33286
  };
@@ -33762,7 +33700,7 @@ var PermissionsController = class {
33762
33700
  * @example
33763
33701
  * ```typescript
33764
33702
  * const txHash = await vana.permissions.grant({
33765
- * to: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
33703
+ * grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
33766
33704
  * operation: "llm_inference",
33767
33705
  * parameters: {
33768
33706
  * model: "gpt-4",
@@ -33791,7 +33729,7 @@ var PermissionsController = class {
33791
33729
  * @example
33792
33730
  * ```typescript
33793
33731
  * const { preview, confirm } = await vana.permissions.prepareGrant({
33794
- * to: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
33732
+ * grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
33795
33733
  * operation: "llm_inference",
33796
33734
  * files: [1, 2, 3],
33797
33735
  * parameters: { model: "gpt-4", prompt: "Analyze my social media data" }
@@ -33840,9 +33778,13 @@ var PermissionsController = class {
33840
33778
  console.debug("\u{1F50D} Debug - Grant URL from params:", grantUrl);
33841
33779
  if (!grantUrl) {
33842
33780
  if (!this.context.relayerCallbacks?.storeGrantFile && !this.context.storageManager) {
33843
- throw new Error(
33844
- "No storage available. Provide a grantUrl, configure relayerCallbacks.storeGrantFile, or storageManager."
33845
- );
33781
+ if (this.context.validateStorageRequired) {
33782
+ this.context.validateStorageRequired();
33783
+ } else {
33784
+ throw new Error(
33785
+ "No storage available. Provide a grantUrl, configure relayerCallbacks.storeGrantFile, or storageManager."
33786
+ );
33787
+ }
33846
33788
  }
33847
33789
  if (this.context.relayerCallbacks?.storeGrantFile) {
33848
33790
  grantUrl = await this.context.relayerCallbacks.storeGrantFile(grantFile);
@@ -33866,7 +33808,7 @@ var PermissionsController = class {
33866
33808
  grantUrl
33867
33809
  );
33868
33810
  const typedData = await this.composePermissionGrantMessage({
33869
- to: params.to,
33811
+ grantee: params.grantee,
33870
33812
  operation: params.operation,
33871
33813
  // Placeholder - real data is in IPFS
33872
33814
  files: params.files,
@@ -33913,7 +33855,7 @@ var PermissionsController = class {
33913
33855
  * @example
33914
33856
  * ```typescript
33915
33857
  * const { typedData, signature } = await vana.permissions.createAndSign({
33916
- * to: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
33858
+ * grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
33917
33859
  * operation: "data_analysis",
33918
33860
  * parameters: { analysisType: "sentiment" },
33919
33861
  * });
@@ -33929,9 +33871,13 @@ var PermissionsController = class {
33929
33871
  console.debug("\u{1F50D} Debug - Grant URL from params:", grantUrl);
33930
33872
  if (!grantUrl) {
33931
33873
  if (!this.context.relayerCallbacks?.storeGrantFile && !this.context.storageManager) {
33932
- throw new Error(
33933
- "No storage available. Provide a grantUrl, configure relayerCallbacks.storeGrantFile, or storageManager."
33934
- );
33874
+ if (this.context.validateStorageRequired) {
33875
+ this.context.validateStorageRequired();
33876
+ } else {
33877
+ throw new Error(
33878
+ "No storage available. Provide a grantUrl, configure relayerCallbacks.storeGrantFile, or storageManager."
33879
+ );
33880
+ }
33935
33881
  }
33936
33882
  if (this.context.relayerCallbacks?.storeGrantFile) {
33937
33883
  grantUrl = await this.context.relayerCallbacks.storeGrantFile(grantFile);
@@ -33955,7 +33901,7 @@ var PermissionsController = class {
33955
33901
  grantUrl
33956
33902
  );
33957
33903
  const typedData = await this.composePermissionGrantMessage({
33958
- to: params.to,
33904
+ grantee: params.grantee,
33959
33905
  operation: params.operation,
33960
33906
  // Placeholder - real data is in IPFS
33961
33907
  files: params.files,
@@ -34306,7 +34252,7 @@ var PermissionsController = class {
34306
34252
  * Composes the EIP-712 typed data for PermissionGrant (new simplified format).
34307
34253
  *
34308
34254
  * @param params - The parameters for composing the permission grant message
34309
- * @param params.to - The recipient address for the permission grant
34255
+ * @param params.grantee - The recipient address for the permission grant
34310
34256
  * @param params.operation - The type of operation being granted permission for
34311
34257
  * @param params.files - Array of file IDs that the permission applies to
34312
34258
  * @param params.grantUrl - URL where the grant details are stored
@@ -34399,36 +34345,45 @@ var PermissionsController = class {
34399
34345
  return addresses[0];
34400
34346
  }
34401
34347
  /**
34402
- * Retrieves all permissions granted by the current user using subgraph queries.
34348
+ * Gets on-chain permission grant data without expensive off-chain resolution.
34403
34349
  *
34404
34350
  * @remarks
34405
- * This method queries the Vana subgraph to find permissions directly granted by the user
34406
- * using the Permission entity. It efficiently handles millions of permissions by leveraging
34407
- * indexed subgraph data instead of scanning contract logs. The method fetches complete
34408
- * grant files from IPFS to provide detailed permission information including operation
34409
- * parameters and grantee details.
34410
- * @param params - Optional query parameters
34411
- * @param params.limit - Maximum number of permissions to return (default: 50)
34412
- * @param params.subgraphUrl - Optional subgraph URL to override the default endpoint
34413
- * @returns A Promise that resolves to an array of `GrantedPermission` objects
34414
- * @throws {BlockchainError} When subgraph is unavailable or returns invalid data
34351
+ * This method provides a fast, performance-focused way to retrieve permission grants
34352
+ * by querying only the subgraph without making expensive IPFS or individual contract calls.
34353
+ * It eliminates the N+1 query problem of the legacy `getUserPermissions()` method.
34354
+ *
34355
+ * The returned data contains all on-chain information but does NOT include resolved
34356
+ * operation details, parameters, or file IDs. Use `retrieveGrantFile()` separately
34357
+ * for specific grants when detailed data is needed.
34358
+ *
34359
+ * **Performance**: Completes in ~100-500ms regardless of permission count.
34360
+ * **Reliability**: Single point of failure (subgraph) with clear RPC fallback path.
34361
+ *
34362
+ * @param options - Options for retrieving permissions (limit, subgraph URL)
34363
+ * @returns A Promise that resolves to an array of `OnChainPermissionGrant` objects
34364
+ * @throws {BlockchainError} When subgraph query fails
34365
+ * @throws {NetworkError} When network requests fail
34415
34366
  * @example
34416
34367
  * ```typescript
34417
- * // Get all permissions granted by current user
34418
- * const permissions = await vana.permissions.getUserPermissions();
34368
+ * // Fast: Get all on-chain permission data
34369
+ * const grants = await vana.permissions.getUserPermissionGrantsOnChain({ limit: 20 });
34419
34370
  *
34420
- * permissions.forEach(permission => {
34421
- * console.log(`Granted ${permission.operation} to ${permission.grantee}`);
34371
+ * // Display in UI immediately
34372
+ * grants.forEach(grant => {
34373
+ * console.log(`Permission ${grant.id}: ${grant.grantUrl}`);
34422
34374
  * });
34423
34375
  *
34424
- * // Limit results
34425
- * const recent = await vana.permissions.getUserPermissions({ limit: 10 });
34376
+ * // Lazy load detailed data for specific permission when user clicks
34377
+ * const grantFile = await retrieveGrantFile(grants[0].grantUrl);
34378
+ * console.log(`Operation: ${grantFile.operation}`);
34379
+ * console.log(`Parameters:`, grantFile.parameters);
34426
34380
  * ```
34427
34381
  */
34428
- async getUserPermissions(params) {
34382
+ async getUserPermissionGrantsOnChain(options = {}) {
34383
+ const { limit = 50, subgraphUrl } = options;
34429
34384
  try {
34430
34385
  const userAddress = await this.getUserAddress();
34431
- const graphqlEndpoint = params?.subgraphUrl || this.context.subgraphUrl;
34386
+ const graphqlEndpoint = subgraphUrl || this.context.subgraphUrl;
34432
34387
  if (!graphqlEndpoint) {
34433
34388
  throw new BlockchainError(
34434
34389
  "subgraphUrl is required. Please provide a valid subgraph endpoint or configure it in Vana constructor."
@@ -34450,16 +34405,6 @@ var PermissionsController = class {
34450
34405
  }
34451
34406
  }
34452
34407
  `;
34453
- console.info("Query:", query);
34454
- console.info(
34455
- "Body:",
34456
- JSON.stringify({
34457
- query,
34458
- variables: {
34459
- userId: userAddress.toLowerCase()
34460
- }
34461
- })
34462
- );
34463
34408
  const response = await fetch(graphqlEndpoint, {
34464
34409
  method: "POST",
34465
34410
  headers: {
@@ -34478,7 +34423,6 @@ var PermissionsController = class {
34478
34423
  );
34479
34424
  }
34480
34425
  const result = await response.json();
34481
- console.info("Result:", result);
34482
34426
  if (result.errors) {
34483
34427
  throw new BlockchainError(
34484
34428
  `Subgraph errors: ${result.errors.map((e) => e.message).join(", ")}`
@@ -34486,64 +34430,32 @@ var PermissionsController = class {
34486
34430
  }
34487
34431
  const userData = result.data?.user;
34488
34432
  if (!userData || !userData.permissions?.length) {
34489
- console.warn("No permissions found for user:", userAddress);
34490
34433
  return [];
34491
34434
  }
34492
- const userPermissions = [];
34493
- const limit = params?.limit || 50;
34494
- const permissionsToProcess = userData.permissions.slice(0, limit);
34495
- for (const permission of permissionsToProcess) {
34496
- try {
34497
- let operation;
34498
- let files = [];
34499
- let parameters;
34500
- let granteeAddress;
34501
- try {
34502
- const grantFile = await retrieveGrantFile(permission.grant);
34503
- operation = grantFile.operation;
34504
- parameters = grantFile.parameters;
34505
- granteeAddress = grantFile.grantee;
34506
- } catch {
34507
- }
34508
- try {
34509
- const fileIds = await this.getPermissionFileIds(
34510
- BigInt(permission.id)
34511
- );
34512
- files = fileIds.map((id) => Number(id));
34513
- } catch {
34514
- }
34515
- userPermissions.push({
34516
- id: BigInt(permission.id),
34517
- files,
34518
- operation: operation || "",
34519
- parameters: parameters || {},
34520
- grant: permission.grant,
34521
- grantor: userAddress.toLowerCase(),
34522
- // Current user is the grantor
34523
- grantee: granteeAddress || userAddress,
34524
- // Application that received permission
34525
- active: true,
34526
- // Default to active if not specified
34527
- grantedAt: Number(permission.addedAtBlock),
34528
- nonce: Number(permission.nonce)
34529
- });
34530
- } catch (error) {
34531
- console.error(
34532
- "SDK Error: Failed to process permission:",
34533
- permission.id,
34534
- error
34535
- );
34536
- }
34537
- }
34538
- return userPermissions.sort((a, b) => {
34435
+ const onChainGrants = userData.permissions.slice(0, limit).map((permission) => ({
34436
+ id: BigInt(permission.id),
34437
+ grantUrl: permission.grant,
34438
+ grantSignature: permission.grantSignature,
34439
+ grantHash: permission.grantHash,
34440
+ nonce: BigInt(permission.nonce),
34441
+ addedAtBlock: BigInt(permission.addedAtBlock),
34442
+ addedAtTimestamp: BigInt(permission.addedAtTimestamp || "0"),
34443
+ transactionHash: permission.transactionHash || "",
34444
+ grantor: userAddress,
34445
+ active: true
34446
+ // TODO: Add revocation status from subgraph when available
34447
+ }));
34448
+ return onChainGrants.sort((a, b) => {
34539
34449
  if (a.id < b.id) return 1;
34540
34450
  if (a.id > b.id) return -1;
34541
34451
  return 0;
34542
34452
  });
34543
34453
  } catch (error) {
34544
- console.error("Failed to fetch user permissions:", error);
34454
+ if (error instanceof BlockchainError || error instanceof NetworkError) {
34455
+ throw error;
34456
+ }
34545
34457
  throw new BlockchainError(
34546
- `Failed to fetch user permissions: ${error instanceof Error ? error.message : "Unknown error"}`
34458
+ `Failed to fetch user permission grants: ${error instanceof Error ? error.message : "Unknown error"}`
34547
34459
  );
34548
34460
  }
34549
34461
  }
@@ -35305,32 +35217,19 @@ async function decryptWithPrivateKey(encryptedData, privateKey, platformAdapter)
35305
35217
  throw new Error(`Failed to decrypt with private key: ${error}`);
35306
35218
  }
35307
35219
  }
35308
- async function encryptUserData(data, walletSignature, platformAdapter) {
35220
+ async function encryptBlobWithSignedKey(data, key, platformAdapter) {
35309
35221
  try {
35310
35222
  const dataBuffer = data instanceof Blob ? await data.arrayBuffer() : new TextEncoder().encode(data);
35311
35223
  const dataArray = new Uint8Array(dataBuffer);
35312
35224
  const encrypted = await platformAdapter.crypto.encryptWithPassword(
35313
35225
  dataArray,
35314
- walletSignature
35226
+ key
35315
35227
  );
35316
35228
  return new Blob([encrypted], {
35317
35229
  type: "application/octet-stream"
35318
35230
  });
35319
35231
  } catch (error) {
35320
- throw new Error(`Failed to encrypt user data: ${error}`);
35321
- }
35322
- }
35323
- async function decryptUserData(encryptedData, walletSignature, platformAdapter) {
35324
- try {
35325
- const encryptedBuffer = encryptedData instanceof Blob ? await encryptedData.arrayBuffer() : new TextEncoder().encode(encryptedData);
35326
- const encryptedArray = new Uint8Array(encryptedBuffer);
35327
- const decrypted = await platformAdapter.crypto.decryptWithPassword(
35328
- encryptedArray,
35329
- walletSignature
35330
- );
35331
- return new Blob([decrypted], { type: "text/plain" });
35332
- } catch (error) {
35333
- throw new Error(`Failed to decrypt user data: ${error}`);
35232
+ throw new Error(`Failed to encrypt data: ${error}`);
35334
35233
  }
35335
35234
  }
35336
35235
  async function generateEncryptionKeyPair(platformAdapter) {
@@ -35347,60 +35246,25 @@ async function generatePGPKeyPair(platformAdapter, options) {
35347
35246
  throw new Error(`Failed to generate PGP key pair: ${error}`);
35348
35247
  }
35349
35248
  }
35249
+ async function decryptBlobWithSignedKey(encryptedData, key, platformAdapter) {
35250
+ try {
35251
+ const encryptedBuffer = encryptedData instanceof Blob ? await encryptedData.arrayBuffer() : new TextEncoder().encode(encryptedData);
35252
+ const encryptedArray = new Uint8Array(encryptedBuffer);
35253
+ const decrypted = await platformAdapter.crypto.decryptWithPassword(
35254
+ encryptedArray,
35255
+ key
35256
+ );
35257
+ return new Blob([decrypted], { type: "text/plain" });
35258
+ } catch (error) {
35259
+ throw new Error(`Failed to decrypt data: ${error}`);
35260
+ }
35261
+ }
35350
35262
 
35351
35263
  // src/controllers/data.ts
35352
35264
  var DataController = class {
35353
35265
  constructor(context) {
35354
35266
  this.context = context;
35355
35267
  }
35356
- /**
35357
- * Uploads user data with automatic encryption and blockchain registration.
35358
- *
35359
- * @remarks
35360
- * This is the primary method for uploading user data to the Vana network. It handles
35361
- * the complete workflow including content normalization, schema validation, encryption,
35362
- * storage upload, permission granting, and blockchain registration.
35363
- *
35364
- * The method automatically:
35365
- * - Normalizes input content to a Blob
35366
- * - Validates data against schema if provided
35367
- * - Generates encryption keys and encrypts the data
35368
- * - Uploads to the configured storage provider
35369
- * - Grants permissions to specified applications
35370
- * - Registers the file on the blockchain
35371
- *
35372
- * @param params - Upload parameters including content, filename, schema, and permissions
35373
- * @returns Promise resolving to upload results with file ID and transaction hash
35374
- * @throws {Error} When wallet is not connected or storage is not configured
35375
- * @throws {SchemaValidationError} When schema validation fails
35376
- * @throws {Error} When upload or blockchain registration fails
35377
- * @example
35378
- * ```typescript
35379
- * // Basic file upload
35380
- * const result = await vana.data.upload({
35381
- * content: "My personal data",
35382
- * filename: "diary.txt"
35383
- * });
35384
- *
35385
- * // Upload with schema validation
35386
- * const result = await vana.data.upload({
35387
- * content: { name: "John", age: 30 },
35388
- * filename: "profile.json",
35389
- * schemaId: 1
35390
- * });
35391
- *
35392
- * // Upload with permissions
35393
- * const result = await vana.data.upload({
35394
- * content: "Data for AI analysis",
35395
- * filename: "analysis.txt",
35396
- * permissions: [{
35397
- * to: "0x1234...",
35398
- * operation: "llm_inference",
35399
- * parameters: { model: "gpt-4" }
35400
- * }]
35401
- * });
35402
- * ```
35403
- */
35404
35268
  async upload(params) {
35405
35269
  const {
35406
35270
  content,
@@ -35408,7 +35272,8 @@ var DataController = class {
35408
35272
  schemaId,
35409
35273
  permissions = [],
35410
35274
  encrypt: encrypt2 = true,
35411
- providerName
35275
+ providerName,
35276
+ owner
35412
35277
  } = params;
35413
35278
  try {
35414
35279
  let blob;
@@ -35465,84 +35330,88 @@ var DataController = class {
35465
35330
  this.context.walletClient,
35466
35331
  DEFAULT_ENCRYPTION_SEED
35467
35332
  );
35468
- finalBlob = await encryptUserData(
35333
+ finalBlob = await encryptBlobWithSignedKey(
35469
35334
  blob,
35470
35335
  encryptionKey,
35471
35336
  this.context.platform
35472
35337
  );
35473
35338
  }
35474
35339
  if (!this.context.storageManager) {
35475
- throw new Error(
35476
- "Storage manager not configured. Please provide storage providers in VanaConfig."
35477
- );
35340
+ if (this.context.validateStorageRequired) {
35341
+ this.context.validateStorageRequired();
35342
+ throw new Error("Storage validation failed");
35343
+ } else {
35344
+ throw new Error(
35345
+ "Storage manager not configured. Please provide storage providers in VanaConfig."
35346
+ );
35347
+ }
35478
35348
  }
35479
35349
  const uploadResult = await this.context.storageManager.upload(
35480
35350
  finalBlob,
35481
35351
  filename,
35482
35352
  providerName
35483
35353
  );
35484
- let blockchainResult;
35485
- const userAddress = await this.getUserAddress();
35486
- if (permissions.length > 0) {
35487
- throw new Error(
35488
- "Upload with permissions is not yet implemented in the high-level API"
35354
+ const userAddress = owner || await this.getUserAddress();
35355
+ let encryptedPermissions = [];
35356
+ if (permissions.length > 0 && encrypt2) {
35357
+ const userEncryptionKey = await generateEncryptionKey(
35358
+ this.context.walletClient,
35359
+ DEFAULT_ENCRYPTION_SEED
35489
35360
  );
35490
- } else {
35491
- const chainId = this.context.walletClient.chain?.id;
35492
- if (!chainId) {
35493
- throw new Error("Chain ID not available");
35494
- }
35495
- const dataRegistryAddress = getContractAddress(chainId, "DataRegistry");
35496
- const dataRegistryAbi = getAbi("DataRegistry");
35497
- let txHash;
35498
- if (schemaId !== void 0) {
35499
- txHash = await this.context.walletClient.writeContract({
35500
- address: dataRegistryAddress,
35501
- abi: dataRegistryAbi,
35502
- functionName: "addFileWithSchema",
35503
- args: [uploadResult.url, BigInt(schemaId)],
35504
- account: this.context.walletClient.account || userAddress,
35505
- chain: this.context.walletClient.chain || null
35506
- });
35507
- } else {
35508
- txHash = await this.context.walletClient.writeContract({
35509
- address: dataRegistryAddress,
35510
- abi: dataRegistryAbi,
35511
- functionName: "addFile",
35512
- args: [uploadResult.url],
35513
- account: this.context.walletClient.account || userAddress,
35514
- chain: this.context.walletClient.chain || null
35515
- });
35516
- }
35517
- const receipt = await this.context.publicClient.waitForTransactionReceipt({
35518
- hash: txHash,
35519
- timeout: 3e4
35520
- // 30 seconds timeout
35521
- });
35522
- let fileId = 0;
35523
- for (const log of receipt.logs) {
35524
- try {
35525
- const decoded = decodeEventLog({
35526
- abi: dataRegistryAbi,
35527
- data: log.data,
35528
- topics: log.topics
35529
- });
35530
- if (decoded.eventName === "FileAdded") {
35531
- fileId = Number(decoded.args.fileId);
35532
- break;
35361
+ encryptedPermissions = await Promise.all(
35362
+ permissions.map(async (permission) => {
35363
+ const publicKey = permission.publicKey;
35364
+ if (!publicKey) {
35365
+ throw new Error(
35366
+ `Public key required for permission to ${permission.grantee} when encryption is enabled`
35367
+ );
35533
35368
  }
35534
- } catch {
35369
+ const encryptedKey = await encryptWithWalletPublicKey(
35370
+ userEncryptionKey,
35371
+ publicKey,
35372
+ this.context.platform
35373
+ );
35374
+ return {
35375
+ account: permission.grantee,
35376
+ key: encryptedKey
35377
+ };
35378
+ })
35379
+ );
35380
+ }
35381
+ let result;
35382
+ if (this.context.relayerCallbacks?.submitFileAdditionComplete) {
35383
+ result = await this.context.relayerCallbacks.submitFileAdditionComplete(
35384
+ {
35385
+ url: uploadResult.url,
35386
+ userAddress,
35387
+ permissions: encryptedPermissions,
35388
+ schemaId: schemaId || 0,
35389
+ ownerAddress: owner
35535
35390
  }
35391
+ );
35392
+ } else if (this.context.relayerCallbacks?.submitFileAddition) {
35393
+ const needsComplexRegistration = schemaId !== void 0 || encryptedPermissions.length > 0;
35394
+ if (needsComplexRegistration) {
35395
+ throw new Error(
35396
+ "The configured relay callback does not support schemas or permissions. Please update your relay server implementation to provide the `submitFileAdditionComplete` callback."
35397
+ );
35536
35398
  }
35537
- blockchainResult = {
35538
- fileId,
35539
- transactionHash: txHash
35540
- };
35399
+ result = await this.context.relayerCallbacks.submitFileAddition(
35400
+ uploadResult.url,
35401
+ userAddress
35402
+ );
35403
+ } else {
35404
+ result = await this.addFileWithPermissionsAndSchema(
35405
+ uploadResult.url,
35406
+ userAddress,
35407
+ encryptedPermissions,
35408
+ schemaId || 0
35409
+ );
35541
35410
  }
35542
35411
  return {
35543
- fileId: blockchainResult.fileId,
35412
+ fileId: result.fileId,
35544
35413
  url: uploadResult.url,
35545
- transactionHash: blockchainResult.transactionHash,
35414
+ transactionHash: result.transactionHash,
35546
35415
  size: uploadResult.size,
35547
35416
  isValid,
35548
35417
  validationErrors: validationErrors.length > 0 ? validationErrors : void 0
@@ -35553,6 +35422,142 @@ var DataController = class {
35553
35422
  );
35554
35423
  }
35555
35424
  }
35425
+ /**
35426
+ * Decrypts a file owned by the user using their wallet signature.
35427
+ *
35428
+ * @remarks
35429
+ * This is the high-level convenience method for decrypting user files, serving as the
35430
+ * symmetrical counterpart to the `upload` method. It handles the complete decryption
35431
+ * workflow including key generation, URL protocol detection, content fetching, and
35432
+ * decryption.
35433
+ *
35434
+ * The method automatically:
35435
+ * - Generates the decryption key from the user's wallet signature
35436
+ * - Determines the appropriate fetch method based on the file URL protocol
35437
+ * - Fetches the encrypted content from IPFS or standard HTTP URLs
35438
+ * - Decrypts the content using the generated key
35439
+ *
35440
+ * For IPFS URLs, the method uses gateway fallback for improved reliability. For
35441
+ * standard HTTP URLs, it uses a simple fetch. If you need custom authentication
35442
+ * headers or specific gateway configurations, use the low-level primitives directly.
35443
+ *
35444
+ * @param file - The user file to decrypt (typically from getUserFiles)
35445
+ * @param encryptionSeed - Optional custom encryption seed (defaults to Vana standard)
35446
+ * @returns Promise resolving to the decrypted file content as a Blob
35447
+ * @throws {Error} When the wallet is not connected
35448
+ * @throws {Error} When fetching the encrypted content fails
35449
+ * @throws {Error} When decryption fails (wrong key or corrupted data)
35450
+ * @example
35451
+ * ```typescript
35452
+ * // Basic file decryption
35453
+ * const files = await vana.data.getUserFiles({ owner: userAddress });
35454
+ * const decryptedBlob = await vana.data.decryptFile(files[0]);
35455
+ *
35456
+ * // Convert to text
35457
+ * const text = await decryptedBlob.text();
35458
+ * console.log('Decrypted content:', text);
35459
+ *
35460
+ * // Convert to JSON
35461
+ * const json = JSON.parse(await decryptedBlob.text());
35462
+ * console.log('Decrypted data:', json);
35463
+ *
35464
+ * // With custom encryption seed
35465
+ * const decryptedBlob = await vana.data.decryptFile(
35466
+ * files[0],
35467
+ * "My custom encryption seed"
35468
+ * );
35469
+ *
35470
+ * // Save to file (in Node.js)
35471
+ * const buffer = await decryptedBlob.arrayBuffer();
35472
+ * fs.writeFileSync('decrypted-file.txt', Buffer.from(buffer));
35473
+ * ```
35474
+ */
35475
+ async decryptFile(file, encryptionSeed) {
35476
+ try {
35477
+ const encryptionKey = await generateEncryptionKey(
35478
+ this.context.walletClient,
35479
+ encryptionSeed || DEFAULT_ENCRYPTION_SEED
35480
+ );
35481
+ let encryptedBlob;
35482
+ try {
35483
+ if (file.url.startsWith("ipfs://")) {
35484
+ encryptedBlob = await this.fetchFromIPFS(file.url);
35485
+ } else {
35486
+ encryptedBlob = await this.fetch(file.url);
35487
+ }
35488
+ } catch (fetchError) {
35489
+ const errorMessage = fetchError instanceof Error ? fetchError.message : "Unknown error";
35490
+ if (errorMessage.includes("Failed to fetch IPFS content") && errorMessage.includes("from all gateways")) {
35491
+ throw new Error(
35492
+ "Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
35493
+ );
35494
+ } else if (errorMessage.includes("Empty response")) {
35495
+ throw new Error("File is empty or could not be retrieved");
35496
+ } else if (errorMessage.includes("Network error:") || errorMessage.includes("Failed to fetch")) {
35497
+ throw new Error(
35498
+ "Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
35499
+ );
35500
+ } else if (errorMessage.includes("HTTP error!")) {
35501
+ const statusMatch = errorMessage.match(/status: (\d+)/);
35502
+ const status = statusMatch ? statusMatch[1] : "unknown";
35503
+ if (status === "500") {
35504
+ throw new Error(
35505
+ "Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
35506
+ );
35507
+ } else if (status === "403") {
35508
+ throw new Error(
35509
+ "Access denied. You may not have permission to access this file"
35510
+ );
35511
+ } else if (status === "404") {
35512
+ throw new Error(
35513
+ "File not found: The encrypted file is no longer available at the stored URL."
35514
+ );
35515
+ } else {
35516
+ throw new Error(
35517
+ "Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
35518
+ );
35519
+ }
35520
+ }
35521
+ throw fetchError;
35522
+ }
35523
+ if (encryptedBlob.size === 0) {
35524
+ throw new Error("File is empty or could not be retrieved");
35525
+ }
35526
+ let decryptedBlob;
35527
+ try {
35528
+ decryptedBlob = await decryptBlobWithSignedKey(
35529
+ encryptedBlob,
35530
+ encryptionKey,
35531
+ this.context.platform
35532
+ );
35533
+ } catch (decryptError) {
35534
+ const errorMessage = decryptError instanceof Error ? decryptError.message : "Unknown error";
35535
+ if (errorMessage.includes("not a valid OpenPGP message")) {
35536
+ throw new Error(
35537
+ "Invalid file format: This file doesn't appear to be encrypted with the Vana protocol"
35538
+ );
35539
+ } else if (errorMessage.includes("Session key decryption failed")) {
35540
+ throw new Error("Wrong encryption key");
35541
+ } else if (errorMessage.includes("Error decrypting message")) {
35542
+ throw new Error("Wrong encryption key");
35543
+ } else if (errorMessage.includes("File not found")) {
35544
+ throw new Error(
35545
+ "File not found: The encrypted file is no longer available"
35546
+ );
35547
+ } else {
35548
+ throw decryptError;
35549
+ }
35550
+ }
35551
+ return decryptedBlob;
35552
+ } catch (error) {
35553
+ 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"))) {
35554
+ throw error;
35555
+ }
35556
+ throw new Error(
35557
+ `Failed to decrypt file: ${error instanceof Error ? error.message : "Unknown error"}`
35558
+ );
35559
+ }
35560
+ }
35556
35561
  /**
35557
35562
  * Retrieves all data files owned by a specific user address.
35558
35563
  *
@@ -36303,81 +36308,6 @@ var DataController = class {
36303
36308
  );
36304
36309
  }
36305
36310
  }
36306
- /**
36307
- * Decrypts a file that was encrypted using the Vana protocol.
36308
- *
36309
- * @param file - The UserFile object containing the file URL and metadata
36310
- * @param encryptionSeed - Optional custom encryption seed (defaults to Vana standard)
36311
- * @returns Promise resolving to the decrypted file as a Blob
36312
- *
36313
- * This method handles the complete flow of:
36314
- * 1. Generating the encryption key from the user's wallet signature
36315
- * 2. Fetching the encrypted file from the stored URL
36316
- * 3. Decrypting the file using the canonical Vana decryption method
36317
- */
36318
- async decryptFile(file, encryptionSeed = DEFAULT_ENCRYPTION_SEED) {
36319
- try {
36320
- const encryptionKey = await generateEncryptionKey(
36321
- this.context.walletClient,
36322
- encryptionSeed
36323
- );
36324
- const fetchUrl = this.convertToDownloadUrl(file.url);
36325
- console.debug(
36326
- `Fetching encrypted file from URL: ${fetchUrl} (original: ${file.url})`
36327
- );
36328
- const response = await fetch(fetchUrl);
36329
- if (!response.ok) {
36330
- if (response.status === 404) {
36331
- throw new Error(
36332
- "File not found. The encrypted file may have been moved or deleted."
36333
- );
36334
- } else if (response.status === 403) {
36335
- throw new Error(
36336
- "Access denied. You may not have permission to access this file."
36337
- );
36338
- } else {
36339
- throw new Error(
36340
- `Network error: ${response.status} ${response.statusText}`
36341
- );
36342
- }
36343
- }
36344
- const encryptedBlob = await response.blob();
36345
- console.debug(
36346
- `Retrieved blob of size: ${encryptedBlob.size} bytes, type: ${encryptedBlob.type}`
36347
- );
36348
- if (encryptedBlob.size === 0) {
36349
- throw new Error("File is empty or could not be retrieved.");
36350
- }
36351
- const decryptedBlob = await decryptUserData(
36352
- encryptedBlob,
36353
- encryptionKey,
36354
- this.context.platform
36355
- );
36356
- return decryptedBlob;
36357
- } catch (error) {
36358
- console.error("Failed to decrypt file:", error);
36359
- if (error instanceof Error) {
36360
- if (error.message.includes("Session key decryption failed") || error.message.includes("Error decrypting message")) {
36361
- throw new Error(
36362
- "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."
36363
- );
36364
- } else if (error.message.includes("Failed to fetch") || error.message.includes("Network error")) {
36365
- throw new Error(
36366
- "Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
36367
- );
36368
- } else if (error.message.includes("File not found")) {
36369
- throw new Error(
36370
- "File not found: The encrypted file is no longer available at the stored URL."
36371
- );
36372
- } else if (error.message.includes("not a valid OpenPGP message") || error.message.includes("does not conform to a valid OpenPGP format")) {
36373
- throw new Error(
36374
- "Invalid file format: This file doesn't appear to be encrypted with the Vana protocol."
36375
- );
36376
- }
36377
- }
36378
- throw error;
36379
- }
36380
- }
36381
36311
  /**
36382
36312
  * Registers a file URL directly on the blockchain with a schema ID.
36383
36313
  *
@@ -36438,26 +36368,6 @@ var DataController = class {
36438
36368
  );
36439
36369
  }
36440
36370
  }
36441
- /**
36442
- * Converts IPFS and Google Drive URLs to direct download URLs for fetching.
36443
- *
36444
- * @param url - The URL to convert to a direct download URL
36445
- * @returns The converted direct download URL or the original URL if not a special URL
36446
- */
36447
- convertToDownloadUrl(url) {
36448
- if (url.startsWith("ipfs://")) {
36449
- const hash = url.replace("ipfs://", "");
36450
- return `https://ipfs.io/ipfs/${hash}`;
36451
- }
36452
- if (url.includes("drive.google.com/file/d/")) {
36453
- const fileIdMatch = url.match(/\/file\/d\/([a-zA-Z0-9-_]+)/);
36454
- if (fileIdMatch) {
36455
- const fileId = fileIdMatch[1];
36456
- return `https://drive.google.com/uc?id=${fileId}&export=download`;
36457
- }
36458
- }
36459
- return url;
36460
- }
36461
36371
  /**
36462
36372
  * Gets the user's address from the wallet client.
36463
36373
  *
@@ -36532,6 +36442,66 @@ var DataController = class {
36532
36442
  );
36533
36443
  }
36534
36444
  }
36445
+ /**
36446
+ * Adds a file to the registry with permissions and schema.
36447
+ * This combines the functionality of addFileWithPermissions and schema validation.
36448
+ *
36449
+ * @param url - The URL of the file to register
36450
+ * @param ownerAddress - The address of the file owner
36451
+ * @param permissions - Array of permissions to grant (account and encrypted key)
36452
+ * @param schemaId - The schema ID to associate with the file (0 for no schema)
36453
+ * @returns Promise resolving to object with fileId and transactionHash
36454
+ */
36455
+ async addFileWithPermissionsAndSchema(url, ownerAddress, permissions = [], schemaId = 0) {
36456
+ try {
36457
+ const chainId = this.context.walletClient.chain?.id;
36458
+ if (!chainId) {
36459
+ throw new Error("Chain ID not available");
36460
+ }
36461
+ const dataRegistryAddress = getContractAddress(chainId, "DataRegistry");
36462
+ const dataRegistryAbi = getAbi("DataRegistry");
36463
+ const txHash = await this.context.walletClient.writeContract({
36464
+ address: dataRegistryAddress,
36465
+ abi: dataRegistryAbi,
36466
+ functionName: "addFileWithPermissionsAndSchema",
36467
+ args: [url, ownerAddress, permissions, BigInt(schemaId)],
36468
+ account: this.context.walletClient.account || ownerAddress,
36469
+ chain: this.context.walletClient.chain || null
36470
+ });
36471
+ const receipt = await this.context.publicClient.waitForTransactionReceipt(
36472
+ {
36473
+ hash: txHash,
36474
+ timeout: 3e4
36475
+ // 30 seconds timeout
36476
+ }
36477
+ );
36478
+ let fileId = 0;
36479
+ for (const log of receipt.logs) {
36480
+ try {
36481
+ const decoded = decodeEventLog({
36482
+ abi: dataRegistryAbi,
36483
+ data: log.data,
36484
+ topics: log.topics
36485
+ });
36486
+ if (decoded.eventName === "FileAdded") {
36487
+ fileId = Number(decoded.args.fileId);
36488
+ break;
36489
+ }
36490
+ } catch {
36491
+ continue;
36492
+ }
36493
+ }
36494
+ return {
36495
+ fileId,
36496
+ transactionHash: txHash
36497
+ };
36498
+ } catch (error) {
36499
+ console.error("Failed to add file with permissions and schema:", error);
36500
+ throw new Error(
36501
+ `Failed to add file with permissions and schema: ${error instanceof Error ? error.message : "Unknown error"}`
36502
+ );
36503
+ }
36504
+ }
36535
36505
  /**
36536
36506
  * Adds a new schema to the DataRefinerRegistry.
36537
36507
  *
@@ -36686,7 +36656,7 @@ var DataController = class {
36686
36656
  const txHash = await this.context.walletClient.writeContract({
36687
36657
  address: dataRefinerRegistryAddress,
36688
36658
  abi: dataRefinerRegistryAbi,
36689
- functionName: "addRefiner",
36659
+ functionName: "addRefinerWithSchemaId",
36690
36660
  args: [
36691
36661
  BigInt(params.dlpId),
36692
36662
  params.name,
@@ -36891,7 +36861,7 @@ var DataController = class {
36891
36861
  this.context.walletClient,
36892
36862
  DEFAULT_ENCRYPTION_SEED
36893
36863
  );
36894
- const encryptedData = await encryptUserData(
36864
+ const encryptedData = await encryptBlobWithSignedKey(
36895
36865
  data,
36896
36866
  userEncryptionKey,
36897
36867
  this.context.platform
@@ -37055,12 +37025,13 @@ var DataController = class {
37055
37025
  privateKey,
37056
37026
  this.context.platform
37057
37027
  );
37058
- const response = await fetch(this.convertToDownloadUrl(file.url));
37059
- if (!response.ok) {
37060
- throw new Error(`Failed to download file: ${response.statusText}`);
37028
+ let encryptedData;
37029
+ if (file.url.startsWith("ipfs://")) {
37030
+ encryptedData = await this.fetchFromIPFS(file.url);
37031
+ } else {
37032
+ encryptedData = await this.fetch(file.url);
37061
37033
  }
37062
- const encryptedData = await response.blob();
37063
- const decryptedData = await decryptUserData(
37034
+ const decryptedData = await decryptBlobWithSignedKey(
37064
37035
  encryptedData,
37065
37036
  userEncryptionKey,
37066
37037
  this.context.platform
@@ -37073,6 +37044,162 @@ var DataController = class {
37073
37044
  );
37074
37045
  }
37075
37046
  }
37047
+ /**
37048
+ * Simple network-agnostic fetch utility for retrieving file content.
37049
+ *
37050
+ * @remarks
37051
+ * This is a thin wrapper around the global fetch API that returns the response as a Blob.
37052
+ * It provides a consistent interface for fetching encrypted content before decryption.
37053
+ * For IPFS URLs, consider using fetchFromIPFS for better reliability.
37054
+ *
37055
+ * @param url - The URL to fetch content from
37056
+ * @returns Promise resolving to the fetched content as a Blob
37057
+ * @throws {Error} When the fetch fails or returns a non-ok response
37058
+ *
37059
+ * @example
37060
+ * ```typescript
37061
+ * // Fetch and decrypt a file
37062
+ * const encryptionKey = await generateEncryptionKey(walletClient);
37063
+ * const encryptedBlob = await vana.data.fetch(file.url);
37064
+ * const decryptedBlob = await decryptBlob(encryptedBlob, encryptionKey, platform);
37065
+ *
37066
+ * // With custom headers for authentication
37067
+ * const response = await fetch(file.url, {
37068
+ * headers: { 'Authorization': 'Bearer token' }
37069
+ * });
37070
+ * const encryptedBlob = await response.blob();
37071
+ * ```
37072
+ */
37073
+ async fetch(url) {
37074
+ try {
37075
+ const response = await fetch(url);
37076
+ if (!response.ok) {
37077
+ throw new Error(
37078
+ `HTTP error! status: ${response.status} ${response.statusText}`
37079
+ );
37080
+ }
37081
+ const blob = await response.blob();
37082
+ if (blob.size === 0) {
37083
+ throw new Error("Empty response");
37084
+ }
37085
+ return blob;
37086
+ } catch (error) {
37087
+ if (error instanceof TypeError && error.message.includes("fetch")) {
37088
+ throw new Error(
37089
+ `Network error: Failed to fetch from ${url}. The URL may be invalid or the server may not be accessible.`
37090
+ );
37091
+ }
37092
+ throw error;
37093
+ }
37094
+ }
37095
+ /**
37096
+ * Specialized IPFS fetcher with gateway fallback mechanism.
37097
+ *
37098
+ * @remarks
37099
+ * This method provides robust IPFS content fetching by trying multiple gateways
37100
+ * in sequence until one succeeds. It supports both ipfs:// URLs and raw CIDs.
37101
+ *
37102
+ * The default gateway list includes public gateways, but you should provide
37103
+ * your own gateways for production use to ensure reliability and privacy.
37104
+ *
37105
+ * @param url - The IPFS URL (ipfs://...) or CID to fetch
37106
+ * @param options - Optional configuration
37107
+ * @param options.gateways - Array of IPFS gateway URLs to try (must end with /)
37108
+ * @returns Promise resolving to the fetched content as a Blob
37109
+ * @throws {Error} When all gateways fail to fetch the content
37110
+ *
37111
+ * @example
37112
+ * ```typescript
37113
+ * // Fetch from IPFS with custom gateways
37114
+ * const encryptedBlob = await vana.data.fetchFromIPFS(file.url, {
37115
+ * gateways: [
37116
+ * 'https://my-private-gateway.com/ipfs/',
37117
+ * 'https://dweb.link/ipfs/',
37118
+ * 'https://ipfs.io/ipfs/'
37119
+ * ]
37120
+ * });
37121
+ *
37122
+ * // Decrypt the fetched content
37123
+ * const encryptionKey = await generateEncryptionKey(walletClient);
37124
+ * const decryptedBlob = await decryptBlob(encryptedBlob, encryptionKey, platform);
37125
+ *
37126
+ * // With raw CID
37127
+ * const blob = await vana.data.fetchFromIPFS('QmXxx...', {
37128
+ * gateways: ['https://ipfs.io/ipfs/']
37129
+ * });
37130
+ * ```
37131
+ */
37132
+ async fetchFromIPFS(url, options) {
37133
+ const defaultGateways = [
37134
+ "https://dweb.link/ipfs/",
37135
+ "https://ipfs.io/ipfs/"
37136
+ ];
37137
+ const gateways = options?.gateways || this.context.ipfsGateways || defaultGateways;
37138
+ let cid;
37139
+ if (url.startsWith("ipfs://")) {
37140
+ cid = url.replace("ipfs://", "");
37141
+ } else if (url.startsWith("Qm") || url.startsWith("bafy")) {
37142
+ cid = url;
37143
+ } else {
37144
+ throw new Error(
37145
+ `Invalid IPFS URL format. Expected ipfs://... or a raw CID, got: ${url}`
37146
+ );
37147
+ }
37148
+ const errors = [];
37149
+ for (let i = 0; i < gateways.length; i++) {
37150
+ const gateway = gateways[i];
37151
+ const isLastGateway = i === gateways.length - 1;
37152
+ const gatewayUrl = gateway.endsWith("/") ? `${gateway}${cid}` : `${gateway}/${cid}`;
37153
+ try {
37154
+ console.debug(`Trying IPFS gateway: ${gatewayUrl}`);
37155
+ const response = await fetch(gatewayUrl);
37156
+ if (response.ok) {
37157
+ const blob = await response.blob();
37158
+ if (blob.size > 0) {
37159
+ console.debug(`Successfully fetched from gateway: ${gateway}`);
37160
+ return blob;
37161
+ } else {
37162
+ if (isLastGateway) {
37163
+ throw new Error("Empty response");
37164
+ }
37165
+ errors.push({
37166
+ gateway,
37167
+ error: "Empty response"
37168
+ });
37169
+ }
37170
+ } else {
37171
+ if (isLastGateway) {
37172
+ if (response.status === 403) {
37173
+ throw new Error(`HTTP error! status: 403 ${response.statusText}`);
37174
+ } else if (response.status === 404) {
37175
+ throw new Error(`HTTP error! status: 404 ${response.statusText}`);
37176
+ } else {
37177
+ throw new Error(
37178
+ `HTTP error! status: ${response.status} ${response.statusText}`
37179
+ );
37180
+ }
37181
+ }
37182
+ errors.push({
37183
+ gateway,
37184
+ error: `HTTP ${response.status} ${response.statusText}`
37185
+ });
37186
+ }
37187
+ } catch (error) {
37188
+ if (isLastGateway && error instanceof Error && (error.message.includes("Empty response") || error.message.includes("HTTP error!"))) {
37189
+ throw error;
37190
+ }
37191
+ errors.push({
37192
+ gateway,
37193
+ error: error instanceof Error ? error.message : "Unknown error"
37194
+ });
37195
+ }
37196
+ }
37197
+ const errorDetails = errors.map((e) => `${e.gateway}: ${e.error}`).join("\n ");
37198
+ throw new Error(
37199
+ `Failed to fetch IPFS content ${cid} from all gateways:
37200
+ ${errorDetails}`
37201
+ );
37202
+ }
37076
37203
  /**
37077
37204
  * Validates a data schema against the Vana meta-schema.
37078
37205
  *
@@ -37261,9 +37388,14 @@ var SchemaController = class {
37261
37388
  };
37262
37389
  validateDataSchema(dataSchema);
37263
37390
  if (!this.context.storageManager) {
37264
- throw new Error(
37265
- "Storage manager not configured. Please provide storage providers in VanaConfig."
37266
- );
37391
+ if (this.context.validateStorageRequired) {
37392
+ this.context.validateStorageRequired();
37393
+ throw new Error("Storage validation failed");
37394
+ } else {
37395
+ throw new Error(
37396
+ "Storage manager not configured. Please provide storage providers in VanaConfig."
37397
+ );
37398
+ }
37267
37399
  }
37268
37400
  const schemaBlob = new Blob([JSON.stringify(schemaDefinition)], {
37269
37401
  type: "application/json"
@@ -37535,6 +37667,7 @@ var ServerController = class {
37535
37667
  }
37536
37668
  const serverResponse = await response.json();
37537
37669
  return {
37670
+ kind: serverResponse.personal_server.kind,
37538
37671
  address: serverResponse.personal_server.address,
37539
37672
  public_key: serverResponse.personal_server.public_key,
37540
37673
  base_url: this.PERSONAL_SERVER_BASE_URL || "",
@@ -39148,175 +39281,151 @@ var PinataStorage = class {
39148
39281
  }
39149
39282
  };
39150
39283
 
39151
- // src/storage/providers/server-proxy.ts
39152
- var ServerProxyStorage = class {
39153
- constructor(config) {
39154
- this.config = config;
39155
- if (!config.uploadUrl) {
39156
- throw new StorageError(
39157
- "Upload URL is required",
39158
- "MISSING_UPLOAD_URL",
39159
- "server-proxy"
39160
- );
39161
- }
39162
- if (!config.downloadUrl) {
39163
- throw new StorageError(
39164
- "Download URL is required",
39165
- "MISSING_DOWNLOAD_URL",
39166
- "server-proxy"
39284
+ // src/storage/providers/callback-storage.ts
39285
+ var CallbackStorage = class {
39286
+ constructor(callbacks) {
39287
+ this.callbacks = callbacks;
39288
+ if (!callbacks.upload || !callbacks.download) {
39289
+ throw new Error(
39290
+ "CallbackStorage requires both upload and download callbacks"
39167
39291
  );
39168
39292
  }
39169
39293
  }
39170
39294
  /**
39171
- * Uploads a file through your server endpoint
39172
- *
39173
- * @remarks
39174
- * This method sends the file to your configured upload endpoint via FormData.
39175
- * Your server is responsible for handling the actual storage implementation
39176
- * and must return a JSON response with `success: true` and an `identifier` field.
39177
- *
39178
- * @param file - The file to upload
39179
- * @param filename - Optional custom filename
39180
- * @returns Promise that resolves to the server-provided identifier
39181
- * @throws {StorageError} When the upload fails or server returns an error
39295
+ * Upload a file using the provided callback
39182
39296
  *
39183
- * @example
39184
- * ```typescript
39185
- * const identifier = await serverStorage.upload(fileBlob, { name: "report.pdf" });
39186
- * console.log("File uploaded with identifier:", identifier);
39187
- * ```
39297
+ * @param file - The blob to upload
39298
+ * @param filename - Optional filename for the upload
39299
+ * @returns The upload result with URL and metadata
39188
39300
  */
39189
39301
  async upload(file, filename) {
39190
39302
  try {
39191
- const formData = new FormData();
39192
- formData.append("file", file);
39193
- if (filename) {
39194
- formData.append("name", filename);
39195
- }
39196
- const response = await fetch(this.config.uploadUrl, {
39197
- method: "POST",
39198
- body: formData
39199
- });
39200
- if (!response.ok) {
39201
- const _errorText = await response.text();
39202
- throw new StorageError(
39203
- `Server upload failed: ${response.status} ${response.statusText}`,
39204
- "UPLOAD_FAILED",
39205
- "server-proxy"
39206
- );
39207
- }
39208
- const result = await response.json();
39209
- if (!result.success) {
39303
+ const result = await this.callbacks.upload(file, filename);
39304
+ if (!result.url || result.url.trim() === "") {
39210
39305
  throw new StorageError(
39211
- `Upload failed: ${result.error || "Unknown server error"}`,
39212
- "UPLOAD_FAILED",
39213
- "server-proxy"
39214
- );
39215
- }
39216
- if (!result.identifier) {
39217
- throw new StorageError(
39218
- "Server upload succeeded but no identifier returned",
39219
- "NO_IDENTIFIER_RETURNED",
39220
- "server-proxy"
39306
+ "Upload callback returned invalid result: missing or empty url",
39307
+ "INVALID_UPLOAD_RESULT",
39308
+ "callback-storage"
39221
39309
  );
39222
39310
  }
39223
- return {
39224
- url: result.url || result.identifier,
39225
- size: file.size,
39226
- contentType: file.type || "application/octet-stream"
39227
- };
39311
+ return result;
39228
39312
  } catch (error) {
39229
39313
  if (error instanceof StorageError) {
39230
39314
  throw error;
39231
39315
  }
39232
39316
  throw new StorageError(
39233
- `Server proxy upload error: ${error instanceof Error ? error.message : "Unknown error"}`,
39317
+ `Upload failed: ${error instanceof Error ? error.message : String(error)}`,
39234
39318
  "UPLOAD_ERROR",
39235
- "server-proxy"
39319
+ "callback-storage",
39320
+ { cause: error instanceof Error ? error : void 0 }
39236
39321
  );
39237
39322
  }
39238
39323
  }
39239
39324
  /**
39240
- * Downloads a file through your server endpoint
39241
- *
39242
- * @remarks
39243
- * This method sends the identifier to your configured download endpoint via POST request.
39244
- * Your server is responsible for retrieving the file from your storage backend
39245
- * and returning the file content as a blob response.
39325
+ * Download a file using the provided callback
39246
39326
  *
39247
- * @param url - The server-provided URL or identifier from upload
39248
- * @returns Promise that resolves to the downloaded file content
39249
- * @throws {StorageError} When the download fails or file is not found
39250
- *
39251
- * @example
39252
- * ```typescript
39253
- * const fileBlob = await serverStorage.download("file-123");
39254
- * const url = URL.createObjectURL(fileBlob);
39255
- * ```
39327
+ * @param url - The URL or identifier to download
39328
+ * @returns The downloaded blob
39256
39329
  */
39257
39330
  async download(url) {
39258
39331
  try {
39259
- const identifier = this.extractIdentifierFromUrl(url);
39260
- const response = await fetch(this.config.downloadUrl, {
39261
- method: "POST",
39262
- headers: {
39263
- "Content-Type": "application/json"
39264
- },
39265
- body: JSON.stringify({ identifier })
39266
- });
39267
- if (!response.ok) {
39268
- const _errorText = await response.text();
39332
+ const identifier = this.callbacks.extractIdentifier ? this.callbacks.extractIdentifier(url) : url;
39333
+ const blob = await this.callbacks.download(identifier);
39334
+ if (!(blob instanceof Blob)) {
39269
39335
  throw new StorageError(
39270
- `Server download failed: ${response.status} ${response.statusText}`,
39271
- "DOWNLOAD_FAILED",
39272
- "server-proxy"
39336
+ "Download callback returned invalid result: expected Blob",
39337
+ "INVALID_DOWNLOAD_RESULT",
39338
+ "callback-storage"
39273
39339
  );
39274
39340
  }
39275
- return await response.blob();
39341
+ return blob;
39276
39342
  } catch (error) {
39277
39343
  if (error instanceof StorageError) {
39278
39344
  throw error;
39279
39345
  }
39280
39346
  throw new StorageError(
39281
- `Server proxy download error: ${error instanceof Error ? error.message : "Unknown error"}`,
39347
+ `Download failed: ${error instanceof Error ? error.message : String(error)}`,
39282
39348
  "DOWNLOAD_ERROR",
39283
- "server-proxy"
39349
+ "callback-storage",
39350
+ { cause: error instanceof Error ? error : void 0 }
39284
39351
  );
39285
39352
  }
39286
39353
  }
39287
- async list(_options) {
39288
- throw new StorageError(
39289
- "List operation is not supported by server proxy storage",
39290
- "LIST_NOT_SUPPORTED",
39291
- "server-proxy"
39292
- );
39293
- }
39294
- async delete(_url) {
39295
- throw new StorageError(
39296
- "Delete operation is not supported by server proxy storage",
39297
- "DELETE_NOT_SUPPORTED",
39298
- "server-proxy"
39299
- );
39354
+ /**
39355
+ * List files using the provided callback (if available)
39356
+ *
39357
+ * @param options - Optional list options including filters and pagination
39358
+ * @returns Array of storage files
39359
+ */
39360
+ async list(options) {
39361
+ if (!this.callbacks.list) {
39362
+ throw new StorageError(
39363
+ "List operation not supported - no list callback provided",
39364
+ "NOT_SUPPORTED",
39365
+ "callback-storage"
39366
+ );
39367
+ }
39368
+ try {
39369
+ const result = await this.callbacks.list(options?.namePattern, options);
39370
+ return result.items.map((item, index) => ({
39371
+ id: item.identifier,
39372
+ name: item.identifier.split("/").pop() || `file-${index}`,
39373
+ url: item.identifier,
39374
+ size: item.size || 0,
39375
+ contentType: "application/octet-stream",
39376
+ createdAt: item.lastModified || /* @__PURE__ */ new Date(),
39377
+ metadata: item.metadata
39378
+ }));
39379
+ } catch (error) {
39380
+ throw new StorageError(
39381
+ `List failed: ${error instanceof Error ? error.message : String(error)}`,
39382
+ "LIST_ERROR",
39383
+ "callback-storage",
39384
+ { cause: error instanceof Error ? error : void 0 }
39385
+ );
39386
+ }
39300
39387
  }
39301
39388
  /**
39302
- * Extract identifier from URL or return as-is
39389
+ * Delete a file using the provided callback (if available)
39303
39390
  *
39304
- * @param url - URL or identifier string
39305
- * @returns identifier string
39391
+ * @param url - The URL or identifier to delete
39392
+ * @returns True if deletion succeeded
39306
39393
  */
39307
- extractIdentifierFromUrl(url) {
39308
- return url;
39394
+ async delete(url) {
39395
+ if (!this.callbacks.delete) {
39396
+ throw new StorageError(
39397
+ "Delete operation not supported - no delete callback provided",
39398
+ "NOT_SUPPORTED",
39399
+ "callback-storage"
39400
+ );
39401
+ }
39402
+ try {
39403
+ const identifier = this.callbacks.extractIdentifier ? this.callbacks.extractIdentifier(url) : url;
39404
+ return await this.callbacks.delete(identifier);
39405
+ } catch (error) {
39406
+ throw new StorageError(
39407
+ `Delete failed: ${error instanceof Error ? error.message : String(error)}`,
39408
+ "DELETE_ERROR",
39409
+ "callback-storage",
39410
+ { cause: error instanceof Error ? error : void 0 }
39411
+ );
39412
+ }
39309
39413
  }
39414
+ /**
39415
+ * Get provider configuration
39416
+ *
39417
+ * @returns Provider configuration metadata
39418
+ */
39310
39419
  getConfig() {
39311
39420
  return {
39312
- name: "Server Proxy",
39313
- type: "server-proxy",
39421
+ name: "callback-storage",
39422
+ type: "callback",
39314
39423
  requiresAuth: false,
39315
39424
  features: {
39316
39425
  upload: true,
39317
39426
  download: true,
39318
- list: false,
39319
- delete: false
39427
+ list: !!this.callbacks.list,
39428
+ delete: !!this.callbacks.delete
39320
39429
  }
39321
39430
  };
39322
39431
  }
@@ -39548,6 +39657,47 @@ function getAllChains() {
39548
39657
  }
39549
39658
 
39550
39659
  // src/core.ts
39660
+ var VanaCoreFactory = class {
39661
+ /**
39662
+ * Creates a VanaCore instance that enforces storage requirements at compile time.
39663
+ * Use this factory when you know you'll need storage-dependent operations.
39664
+ *
39665
+ * @param platform - The platform adapter for environment-specific operations
39666
+ * @param config - Configuration that includes required storage providers
39667
+ * @returns VanaCore instance with storage validation
39668
+ * @example
39669
+ * ```typescript
39670
+ * const vanaCore = VanaCoreFactory.createWithStorage(platformAdapter, {
39671
+ * walletClient: myWalletClient,
39672
+ * storage: {
39673
+ * providers: { ipfs: new IPFSStorage() },
39674
+ * defaultProvider: 'ipfs'
39675
+ * }
39676
+ * });
39677
+ * ```
39678
+ */
39679
+ static createWithStorage(platform, config) {
39680
+ const core = new VanaCore(platform, config);
39681
+ return core;
39682
+ }
39683
+ /**
39684
+ * Creates a VanaCore instance without storage requirements.
39685
+ * Storage-dependent operations will fail at runtime if not configured.
39686
+ *
39687
+ * @param platform - The platform adapter for environment-specific operations
39688
+ * @param config - Basic configuration without required storage
39689
+ * @returns VanaCore instance
39690
+ * @example
39691
+ * ```typescript
39692
+ * const vanaCore = VanaCoreFactory.create(platformAdapter, {
39693
+ * walletClient: myWalletClient
39694
+ * });
39695
+ * ```
39696
+ */
39697
+ static create(platform, config) {
39698
+ return new VanaCore(platform, config);
39699
+ }
39700
+ };
39551
39701
  var VanaCore = class {
39552
39702
  /**
39553
39703
  * Initializes a new VanaCore client instance with the provided configuration.
@@ -39555,6 +39705,10 @@ var VanaCore = class {
39555
39705
  * @remarks
39556
39706
  * The constructor validates the configuration, initializes storage providers if configured,
39557
39707
  * creates wallet and public clients, and sets up all SDK controllers with shared context.
39708
+ *
39709
+ * IMPORTANT: This constructor will validate storage requirements at runtime to fail fast.
39710
+ * Methods that require storage will throw runtime errors if storage is not configured.
39711
+ *
39558
39712
  * @param platform - The platform adapter for environment-specific operations
39559
39713
  * @param config - The configuration object specifying wallet or chain settings
39560
39714
  * @throws {InvalidConfigurationError} When the configuration is invalid or incomplete
@@ -39581,9 +39735,13 @@ var VanaCore = class {
39581
39735
  __publicField(this, "platform");
39582
39736
  __publicField(this, "relayerCallbacks");
39583
39737
  __publicField(this, "storageManager");
39738
+ __publicField(this, "hasRequiredStorage");
39739
+ __publicField(this, "ipfsGateways");
39584
39740
  this.platform = platform;
39585
39741
  this.validateConfig(config);
39586
39742
  this.relayerCallbacks = config.relayerCallbacks;
39743
+ this.ipfsGateways = config.ipfsGateways;
39744
+ this.hasRequiredStorage = hasStorageConfig(config);
39587
39745
  if (config.storage?.providers) {
39588
39746
  this.storageManager = new StorageManager();
39589
39747
  for (const [name, provider] of Object.entries(config.storage.providers)) {
@@ -39638,8 +39796,11 @@ var VanaCore = class {
39638
39796
  relayerCallbacks: this.relayerCallbacks,
39639
39797
  storageManager: this.storageManager,
39640
39798
  subgraphUrl,
39641
- platform: this.platform
39799
+ platform: this.platform,
39642
39800
  // Pass the platform adapter to controllers
39801
+ validateStorageRequired: this.validateStorageRequired.bind(this),
39802
+ hasStorage: this.hasStorage.bind(this),
39803
+ ipfsGateways: this.ipfsGateways
39643
39804
  };
39644
39805
  this.permissions = new PermissionsController(sharedContext);
39645
39806
  this.data = new DataController(sharedContext);
@@ -39647,6 +39808,58 @@ var VanaCore = class {
39647
39808
  this.server = new ServerController(sharedContext);
39648
39809
  this.protocol = new ProtocolController(sharedContext);
39649
39810
  }
39811
+ /**
39812
+ * Validates that storage is available for storage-dependent operations.
39813
+ * This method enforces the fail-fast principle by checking storage availability
39814
+ * at method call time rather than during expensive operations.
39815
+ *
39816
+ * @throws {InvalidConfigurationError} When storage is required but not configured
39817
+ * @example
39818
+ * ```typescript
39819
+ * // This will throw if storage is not configured
39820
+ * vana.validateStorageRequired();
39821
+ * await vana.data.uploadFile(file); // Safe to proceed
39822
+ * ```
39823
+ */
39824
+ validateStorageRequired() {
39825
+ if (!this.hasRequiredStorage) {
39826
+ throw new InvalidConfigurationError(
39827
+ "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."
39828
+ );
39829
+ }
39830
+ }
39831
+ /**
39832
+ * Checks whether storage is configured without throwing an error.
39833
+ *
39834
+ * @returns True if storage is properly configured
39835
+ * @example
39836
+ * ```typescript
39837
+ * if (vana.hasStorage()) {
39838
+ * await vana.data.uploadFile(file);
39839
+ * } else {
39840
+ * console.warn('Storage not configured - using pre-stored URLs only');
39841
+ * }
39842
+ * ```
39843
+ */
39844
+ hasStorage() {
39845
+ return this.hasRequiredStorage;
39846
+ }
39847
+ /**
39848
+ * Type guard to check if this instance has storage enabled at compile time.
39849
+ * Use this when you need TypeScript to understand that storage is available.
39850
+ *
39851
+ * @returns True if storage is configured, with type narrowing
39852
+ * @example
39853
+ * ```typescript
39854
+ * if (vana.isStorageEnabled()) {
39855
+ * // TypeScript knows storage is available here
39856
+ * await vana.data.uploadFile(file);
39857
+ * }
39858
+ * ```
39859
+ */
39860
+ isStorageEnabled() {
39861
+ return this.hasRequiredStorage;
39862
+ }
39650
39863
  /**
39651
39864
  * Validates the provided configuration object against all requirements.
39652
39865
  *
@@ -39835,23 +40048,23 @@ var VanaCore = class {
39835
40048
  return this.platform;
39836
40049
  }
39837
40050
  /**
39838
- * Encrypts user data using the Vana protocol standard encryption.
40051
+ * Encrypts data using the Vana protocol standard encryption.
39839
40052
  * This method automatically uses the correct platform adapter for the current environment.
39840
40053
  *
39841
40054
  * @param data The data to encrypt (string or Blob)
39842
- * @param walletSignature The wallet signature to use as encryption key
40055
+ * @param key The key to use as encryption key
39843
40056
  * @returns The encrypted data as Blob
39844
40057
  * @example
39845
40058
  * ```typescript
39846
40059
  * const encryptionKey = await generateEncryptionKey(walletClient);
39847
- * const encrypted = await vana.encryptUserData("sensitive data", encryptionKey);
40060
+ * const encrypted = await vana.encryptBlob("sensitive data", encryptionKey);
39848
40061
  * ```
39849
40062
  */
39850
- async encryptUserData(data, walletSignature) {
39851
- return encryptUserData(data, walletSignature, this.platform);
40063
+ async encryptBlob(data, key) {
40064
+ return encryptBlobWithSignedKey(data, key, this.platform);
39852
40065
  }
39853
40066
  /**
39854
- * Decrypts user data using the Vana protocol standard decryption.
40067
+ * Decrypts data that was encrypted using the Vana protocol.
39855
40068
  * This method automatically uses the correct platform adapter for the current environment.
39856
40069
  *
39857
40070
  * @param encryptedData The encrypted data (string or Blob)
@@ -39860,12 +40073,16 @@ var VanaCore = class {
39860
40073
  * @example
39861
40074
  * ```typescript
39862
40075
  * const encryptionKey = await generateEncryptionKey(walletClient);
39863
- * const decrypted = await vana.decryptUserData(encryptedData, encryptionKey);
40076
+ * const decrypted = await vana.decryptBlob(encryptedData, encryptionKey);
39864
40077
  * const text = await decrypted.text();
39865
40078
  * ```
39866
40079
  */
39867
- async decryptUserData(encryptedData, walletSignature) {
39868
- return decryptUserData(encryptedData, walletSignature, this.platform);
40080
+ async decryptBlob(encryptedData, walletSignature) {
40081
+ return decryptBlobWithSignedKey(
40082
+ encryptedData,
40083
+ walletSignature,
40084
+ this.platform
40085
+ );
39869
40086
  }
39870
40087
  };
39871
40088
 
@@ -39896,7 +40113,7 @@ function createValidatedGrant(params) {
39896
40113
  try {
39897
40114
  validateGrant(grantFile, {
39898
40115
  schema: true,
39899
- grantee: params.to,
40116
+ grantee: params.grantee,
39900
40117
  operation: params.operation
39901
40118
  });
39902
40119
  } catch (error) {
@@ -40653,31 +40870,22 @@ var ApiClient = class {
40653
40870
  };
40654
40871
 
40655
40872
  // src/index.browser.ts
40656
- var VanaBrowser = class extends VanaCore {
40657
- /**
40658
- * Creates a Vana SDK instance configured for browser environments.
40659
- *
40660
- * @param config - SDK configuration object (wallet client or chain config)
40661
- * @example
40662
- * ```typescript
40663
- * // With wallet client
40664
- * const vana = new Vana({ walletClient });
40665
- *
40666
- * // With chain configuration
40667
- * const vana = new Vana({ chainId: 14800, account });
40668
- * ```
40669
- */
40873
+ var VanaBrowserImpl = class extends VanaCore {
40670
40874
  constructor(config) {
40671
40875
  super(new BrowserPlatformAdapter(), config);
40672
40876
  }
40673
40877
  };
40674
- var index_browser_default = VanaBrowser;
40878
+ function Vana(config) {
40879
+ return new VanaBrowserImpl(config);
40880
+ }
40881
+ var index_browser_default = Vana;
40675
40882
  export {
40676
40883
  ApiClient,
40677
40884
  AsyncQueue,
40678
40885
  BaseController,
40679
40886
  BlockchainError,
40680
40887
  BrowserPlatformAdapter,
40888
+ CallbackStorage,
40681
40889
  CircuitBreaker,
40682
40890
  ContractFactory,
40683
40891
  ContractNotFoundError,
@@ -40710,14 +40918,15 @@ export {
40710
40918
  SchemaValidator,
40711
40919
  SerializationError,
40712
40920
  ServerController,
40713
- ServerProxyStorage,
40714
40921
  ServerUrlMismatchError,
40715
40922
  SignatureError,
40716
40923
  StorageError,
40717
40924
  StorageManager,
40718
40925
  UserRejectedRequestError,
40719
- VanaBrowser as Vana,
40720
- VanaBrowser,
40926
+ Vana,
40927
+ VanaBrowserImpl,
40928
+ VanaCore,
40929
+ VanaCoreFactory,
40721
40930
  VanaError,
40722
40931
  __contractCache,
40723
40932
  chains,
@@ -40730,13 +40939,13 @@ export {
40730
40939
  createGrantFile,
40731
40940
  createPlatformAdapterSafe,
40732
40941
  createValidatedGrant,
40733
- decryptUserData,
40942
+ decryptBlobWithSignedKey,
40734
40943
  decryptWithPrivateKey,
40735
40944
  decryptWithWalletPrivateKey,
40736
40945
  index_browser_default as default,
40737
40946
  detectPlatform,
40947
+ encryptBlobWithSignedKey,
40738
40948
  encryptFileKey,
40739
- encryptUserData,
40740
40949
  encryptWithWalletPublicKey,
40741
40950
  extractIpfsHash,
40742
40951
  fetchAndValidateSchema,
@@ -40760,9 +40969,7 @@ export {
40760
40969
  getPlatformCapabilities,
40761
40970
  isAPIResponse,
40762
40971
  isGrantExpired,
40763
- isIdentityServerOutput,
40764
40972
  isIpfsUrl,
40765
- isPersonalServerOutput,
40766
40973
  isPlatformSupported,
40767
40974
  isReplicateAPIResponse,
40768
40975
  moksha,