@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.
- package/README.md +189 -425
- package/dist/chains.browser.cjs +4 -1
- package/dist/chains.browser.cjs.map +1 -1
- package/dist/chains.browser.d.cts +2 -1
- package/dist/chains.browser.d.ts +2 -1
- package/dist/chains.browser.js +3 -1
- package/dist/chains.browser.js.map +1 -1
- package/dist/chains.cjs +4 -1
- package/dist/chains.cjs.map +1 -1
- package/dist/chains.d.cts +1 -1
- package/dist/chains.d.ts +1 -1
- package/dist/chains.js +3 -1
- package/dist/chains.js.map +1 -1
- package/dist/chains.node.cjs +4 -1
- package/dist/chains.node.cjs.map +1 -1
- package/dist/chains.node.d.cts +1 -1
- package/dist/chains.node.d.ts +1 -1
- package/dist/chains.node.js +3 -1
- package/dist/chains.node.js.map +1 -1
- package/dist/index.browser.d.ts +3883 -2669
- package/dist/index.browser.js +1412 -1094
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.cts +1 -31277
- package/dist/index.node.cjs +1448 -1131
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.d.cts +3885 -2671
- package/dist/index.node.d.ts +3885 -2671
- package/dist/index.node.js +1427 -1110
- package/dist/index.node.js.map +1 -1
- package/dist/platform.browser.js +32 -181
- package/dist/platform.browser.js.map +1 -1
- package/dist/platform.cjs +47 -197
- package/dist/platform.cjs.map +1 -1
- package/dist/platform.js +47 -197
- package/dist/platform.js.map +1 -1
- package/dist/platform.node.cjs +47 -197
- package/dist/platform.node.cjs.map +1 -1
- package/dist/platform.node.js +47 -197
- package/dist/platform.node.js.map +1 -1
- package/package.json +24 -18
package/dist/index.browser.js
CHANGED
|
@@ -29,11 +29,6 @@ function parseEncryptedDataBuffer(encryptedBuffer) {
|
|
|
29
29
|
mac: encryptedBuffer.slice(-32)
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
|
-
function uint8ArrayToHex(array) {
|
|
33
|
-
return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join(
|
|
34
|
-
""
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
32
|
var init_crypto_utils = __esm({
|
|
38
33
|
"src/platform/shared/crypto-utils.ts"() {
|
|
39
34
|
"use strict";
|
|
@@ -84,147 +79,59 @@ var init_error_utils = __esm({
|
|
|
84
79
|
|
|
85
80
|
// src/platform/browser.ts
|
|
86
81
|
import * as openpgp from "openpgp";
|
|
87
|
-
|
|
88
|
-
const result = new Uint8Array(hex.length / 2);
|
|
89
|
-
for (let i = 0; i < hex.length; i += 2) {
|
|
90
|
-
result[i / 2] = parseInt(hex.substr(i, 2), 16);
|
|
91
|
-
}
|
|
92
|
-
return result;
|
|
93
|
-
}
|
|
94
|
-
var BrowserECDH, BrowserCryptoAdapter, BrowserPGPAdapter, BrowserHttpAdapter, BrowserPlatformAdapter, browserPlatformAdapter;
|
|
82
|
+
var BrowserCryptoAdapter, BrowserPGPAdapter, BrowserHttpAdapter, BrowserPlatformAdapter, browserPlatformAdapter;
|
|
95
83
|
var init_browser = __esm({
|
|
96
84
|
"src/platform/browser.ts"() {
|
|
97
85
|
"use strict";
|
|
98
86
|
init_crypto_utils();
|
|
99
87
|
init_pgp_utils();
|
|
100
88
|
init_error_utils();
|
|
101
|
-
BrowserECDH = class {
|
|
102
|
-
async generateKeyPair() {
|
|
103
|
-
const keyPair = await crypto.subtle.generateKey(
|
|
104
|
-
{
|
|
105
|
-
name: "ECDH",
|
|
106
|
-
namedCurve: "P-256"
|
|
107
|
-
},
|
|
108
|
-
true,
|
|
109
|
-
["deriveKey", "deriveBits"]
|
|
110
|
-
);
|
|
111
|
-
return keyPair;
|
|
112
|
-
}
|
|
113
|
-
async encrypt(publicKeyHex, message) {
|
|
114
|
-
const ephemeralKeyPair = await this.generateKeyPair();
|
|
115
|
-
const publicKeyData = hexToUint8Array(publicKeyHex);
|
|
116
|
-
const importedPublicKey = await crypto.subtle.importKey(
|
|
117
|
-
"raw",
|
|
118
|
-
publicKeyData,
|
|
119
|
-
{ name: "ECDH", namedCurve: "P-256" },
|
|
120
|
-
false,
|
|
121
|
-
[]
|
|
122
|
-
);
|
|
123
|
-
const sharedKey = await crypto.subtle.deriveKey(
|
|
124
|
-
{ name: "ECDH", public: importedPublicKey },
|
|
125
|
-
ephemeralKeyPair.privateKey,
|
|
126
|
-
{ name: "AES-GCM", length: 256 },
|
|
127
|
-
false,
|
|
128
|
-
["encrypt"]
|
|
129
|
-
);
|
|
130
|
-
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
131
|
-
const encoder = new TextEncoder();
|
|
132
|
-
const data = encoder.encode(message);
|
|
133
|
-
const encrypted = await crypto.subtle.encrypt(
|
|
134
|
-
{ name: "AES-GCM", iv },
|
|
135
|
-
sharedKey,
|
|
136
|
-
data
|
|
137
|
-
);
|
|
138
|
-
const ephemeralPublicKeyData = await crypto.subtle.exportKey(
|
|
139
|
-
"raw",
|
|
140
|
-
ephemeralKeyPair.publicKey
|
|
141
|
-
);
|
|
142
|
-
return JSON.stringify({
|
|
143
|
-
encrypted: Array.from(new Uint8Array(encrypted)),
|
|
144
|
-
iv: Array.from(iv),
|
|
145
|
-
ephemeralPublicKey: Array.from(new Uint8Array(ephemeralPublicKeyData)),
|
|
146
|
-
publicKey: publicKeyHex
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
async decrypt(privateKeyHex, encryptedData) {
|
|
150
|
-
try {
|
|
151
|
-
const data = JSON.parse(encryptedData);
|
|
152
|
-
if (!data.encrypted || !data.iv || !data.ephemeralPublicKey) {
|
|
153
|
-
throw new Error("Invalid encrypted data format");
|
|
154
|
-
}
|
|
155
|
-
const privateKeyData = hexToUint8Array(privateKeyHex);
|
|
156
|
-
const importedPrivateKey = await crypto.subtle.importKey(
|
|
157
|
-
"pkcs8",
|
|
158
|
-
privateKeyData,
|
|
159
|
-
{ name: "ECDH", namedCurve: "P-256" },
|
|
160
|
-
false,
|
|
161
|
-
["deriveKey"]
|
|
162
|
-
);
|
|
163
|
-
const ephemeralPublicKey = await crypto.subtle.importKey(
|
|
164
|
-
"raw",
|
|
165
|
-
new Uint8Array(data.ephemeralPublicKey),
|
|
166
|
-
{ name: "ECDH", namedCurve: "P-256" },
|
|
167
|
-
false,
|
|
168
|
-
[]
|
|
169
|
-
);
|
|
170
|
-
const sharedKey = await crypto.subtle.deriveKey(
|
|
171
|
-
{ name: "ECDH", public: ephemeralPublicKey },
|
|
172
|
-
importedPrivateKey,
|
|
173
|
-
{ name: "AES-GCM", length: 256 },
|
|
174
|
-
false,
|
|
175
|
-
["decrypt"]
|
|
176
|
-
);
|
|
177
|
-
const decrypted = await crypto.subtle.decrypt(
|
|
178
|
-
{ name: "AES-GCM", iv: new Uint8Array(data.iv) },
|
|
179
|
-
sharedKey,
|
|
180
|
-
new Uint8Array(data.encrypted)
|
|
181
|
-
);
|
|
182
|
-
return new TextDecoder().decode(decrypted);
|
|
183
|
-
} catch (error) {
|
|
184
|
-
throw new Error(
|
|
185
|
-
`Decryption failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
89
|
BrowserCryptoAdapter = class {
|
|
191
90
|
async encryptWithPublicKey(data, publicKeyHex) {
|
|
192
91
|
try {
|
|
193
|
-
const
|
|
194
|
-
|
|
92
|
+
const eccrypto = await import("eccrypto-js");
|
|
93
|
+
const publicKeyBuffer = Buffer.from(publicKeyHex, "hex");
|
|
94
|
+
const encrypted = await eccrypto.encrypt(
|
|
95
|
+
publicKeyBuffer,
|
|
96
|
+
Buffer.from(data, "utf8")
|
|
97
|
+
);
|
|
98
|
+
const result = Buffer.concat([
|
|
99
|
+
encrypted.iv,
|
|
100
|
+
encrypted.ephemPublicKey,
|
|
101
|
+
encrypted.ciphertext,
|
|
102
|
+
encrypted.mac
|
|
103
|
+
]);
|
|
104
|
+
return result.toString("hex");
|
|
195
105
|
} catch (error) {
|
|
196
106
|
throw new Error(`Encryption failed: ${error}`);
|
|
197
107
|
}
|
|
198
108
|
}
|
|
199
109
|
async decryptWithPrivateKey(encryptedData, privateKeyHex) {
|
|
200
110
|
try {
|
|
201
|
-
const
|
|
202
|
-
|
|
111
|
+
const eccrypto = await import("eccrypto-js");
|
|
112
|
+
const privateKeyBuffer = processWalletPrivateKey(privateKeyHex);
|
|
113
|
+
const encryptedBuffer = Buffer.from(encryptedData, "hex");
|
|
114
|
+
const { iv, ephemPublicKey, ciphertext, mac } = parseEncryptedDataBuffer(encryptedBuffer);
|
|
115
|
+
const encryptedObj = { iv, ephemPublicKey, ciphertext, mac };
|
|
116
|
+
const decryptedBuffer = await eccrypto.decrypt(
|
|
117
|
+
privateKeyBuffer,
|
|
118
|
+
encryptedObj
|
|
119
|
+
);
|
|
120
|
+
return decryptedBuffer.toString("utf8");
|
|
203
121
|
} catch (error) {
|
|
204
122
|
throw new Error(`Decryption failed: ${error}`);
|
|
205
123
|
}
|
|
206
124
|
}
|
|
207
125
|
async generateKeyPair() {
|
|
208
126
|
try {
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
true,
|
|
215
|
-
["deriveKey", "deriveBits"]
|
|
216
|
-
);
|
|
217
|
-
const publicKeyBuffer = await crypto.subtle.exportKey(
|
|
218
|
-
"raw",
|
|
219
|
-
keyPair.publicKey
|
|
220
|
-
);
|
|
221
|
-
const privateKeyBuffer = await crypto.subtle.exportKey(
|
|
222
|
-
"pkcs8",
|
|
223
|
-
keyPair.privateKey
|
|
224
|
-
);
|
|
127
|
+
const eccrypto = await import("eccrypto-js");
|
|
128
|
+
const privateKeyBytes = new Uint8Array(32);
|
|
129
|
+
crypto.getRandomValues(privateKeyBytes);
|
|
130
|
+
const privateKey = Buffer.from(privateKeyBytes);
|
|
131
|
+
const publicKey = eccrypto.getPublicCompressed(privateKey);
|
|
225
132
|
return {
|
|
226
|
-
|
|
227
|
-
|
|
133
|
+
privateKey: privateKey.toString("hex"),
|
|
134
|
+
publicKey: publicKey.toString("hex")
|
|
228
135
|
};
|
|
229
136
|
} catch (error) {
|
|
230
137
|
throw wrapCryptoError("key generation", error);
|
|
@@ -234,65 +141,9 @@ var init_browser = __esm({
|
|
|
234
141
|
try {
|
|
235
142
|
const eccrypto = await import("eccrypto-js");
|
|
236
143
|
const uncompressedKey = processWalletPublicKey(publicKey);
|
|
237
|
-
const iv = Buffer.from([
|
|
238
|
-
1,
|
|
239
|
-
2,
|
|
240
|
-
3,
|
|
241
|
-
4,
|
|
242
|
-
5,
|
|
243
|
-
6,
|
|
244
|
-
7,
|
|
245
|
-
8,
|
|
246
|
-
9,
|
|
247
|
-
10,
|
|
248
|
-
11,
|
|
249
|
-
12,
|
|
250
|
-
13,
|
|
251
|
-
14,
|
|
252
|
-
15,
|
|
253
|
-
16
|
|
254
|
-
]);
|
|
255
|
-
const ephemeralKey = Buffer.from([
|
|
256
|
-
17,
|
|
257
|
-
34,
|
|
258
|
-
51,
|
|
259
|
-
68,
|
|
260
|
-
85,
|
|
261
|
-
102,
|
|
262
|
-
119,
|
|
263
|
-
136,
|
|
264
|
-
153,
|
|
265
|
-
170,
|
|
266
|
-
187,
|
|
267
|
-
204,
|
|
268
|
-
221,
|
|
269
|
-
238,
|
|
270
|
-
255,
|
|
271
|
-
0,
|
|
272
|
-
16,
|
|
273
|
-
32,
|
|
274
|
-
48,
|
|
275
|
-
64,
|
|
276
|
-
80,
|
|
277
|
-
96,
|
|
278
|
-
112,
|
|
279
|
-
128,
|
|
280
|
-
144,
|
|
281
|
-
160,
|
|
282
|
-
176,
|
|
283
|
-
192,
|
|
284
|
-
208,
|
|
285
|
-
224,
|
|
286
|
-
240,
|
|
287
|
-
0
|
|
288
|
-
]);
|
|
289
144
|
const encryptedBuffer = await eccrypto.encrypt(
|
|
290
145
|
uncompressedKey,
|
|
291
|
-
Buffer.from(data)
|
|
292
|
-
{
|
|
293
|
-
iv,
|
|
294
|
-
ephemPrivateKey: ephemeralKey
|
|
295
|
-
}
|
|
146
|
+
Buffer.from(data)
|
|
296
147
|
);
|
|
297
148
|
const result = Buffer.concat([
|
|
298
149
|
encryptedBuffer.iv,
|
|
@@ -539,6 +390,9 @@ function isWalletConfig(config) {
|
|
|
539
390
|
function isChainConfig(config) {
|
|
540
391
|
return "chainId" in config && !("walletClient" in config);
|
|
541
392
|
}
|
|
393
|
+
function hasStorageConfig(config) {
|
|
394
|
+
return config.storage?.providers !== void 0 && Object.keys(config.storage.providers).length > 0;
|
|
395
|
+
}
|
|
542
396
|
|
|
543
397
|
// src/types/chains.ts
|
|
544
398
|
function isVanaChainId(chainId) {
|
|
@@ -562,8 +416,8 @@ var StorageError = class extends Error {
|
|
|
562
416
|
import Ajv from "ajv";
|
|
563
417
|
import addFormats from "ajv-formats";
|
|
564
418
|
|
|
565
|
-
// src/schemas/
|
|
566
|
-
var
|
|
419
|
+
// src/schemas/dataSchema.schema.json
|
|
420
|
+
var dataSchema_schema_default = {
|
|
567
421
|
$id: "https://vana.org/data-schema.json",
|
|
568
422
|
$schema: "http://json-schema.org/draft-07/schema#",
|
|
569
423
|
title: "Schema",
|
|
@@ -635,7 +489,7 @@ var SchemaValidator = class {
|
|
|
635
489
|
strict: false
|
|
636
490
|
});
|
|
637
491
|
addFormats(this.ajv);
|
|
638
|
-
this.dataSchemaValidator = this.ajv.compile(
|
|
492
|
+
this.dataSchemaValidator = this.ajv.compile(dataSchema_schema_default);
|
|
639
493
|
}
|
|
640
494
|
/**
|
|
641
495
|
* Validates a data schema against the Vana meta-schema
|
|
@@ -834,55 +688,6 @@ function isReplicateAPIResponse(value) {
|
|
|
834
688
|
obj.status
|
|
835
689
|
);
|
|
836
690
|
}
|
|
837
|
-
function isIdentityServerOutput(value) {
|
|
838
|
-
console.debug("\u{1F50D} Type Guard: Checking value:", value);
|
|
839
|
-
console.debug("\u{1F50D} Type Guard: Value type:", typeof value);
|
|
840
|
-
if (typeof value !== "object" || value === null) {
|
|
841
|
-
console.debug("\u{1F50D} Type Guard: Failed - not object or null");
|
|
842
|
-
return false;
|
|
843
|
-
}
|
|
844
|
-
const obj = value;
|
|
845
|
-
console.debug("\u{1F50D} Type Guard: Object keys:", Object.keys(obj));
|
|
846
|
-
console.debug(
|
|
847
|
-
"\u{1F50D} Type Guard: user_address:",
|
|
848
|
-
obj.user_address,
|
|
849
|
-
typeof obj.user_address
|
|
850
|
-
);
|
|
851
|
-
if (typeof obj.user_address !== "string") {
|
|
852
|
-
console.debug("\u{1F50D} Type Guard: Failed - user_address not string");
|
|
853
|
-
return false;
|
|
854
|
-
}
|
|
855
|
-
console.debug(
|
|
856
|
-
"\u{1F50D} Type Guard: personal_server:",
|
|
857
|
-
obj.personal_server,
|
|
858
|
-
typeof obj.personal_server
|
|
859
|
-
);
|
|
860
|
-
if (typeof obj.personal_server !== "object" || obj.personal_server === null) {
|
|
861
|
-
console.debug("\u{1F50D} Type Guard: Failed - personal_server not object or null");
|
|
862
|
-
return false;
|
|
863
|
-
}
|
|
864
|
-
const personalServer = obj.personal_server;
|
|
865
|
-
console.debug(
|
|
866
|
-
"\u{1F50D} Type Guard: Personal server keys:",
|
|
867
|
-
Object.keys(personalServer)
|
|
868
|
-
);
|
|
869
|
-
console.debug("\u{1F50D} Type Guard: address:", personalServer.address);
|
|
870
|
-
console.debug("\u{1F50D} Type Guard: public_key:", personalServer.public_key);
|
|
871
|
-
const hasAddress = "address" in personalServer;
|
|
872
|
-
const hasPublicKey = "public_key" in personalServer;
|
|
873
|
-
console.debug(
|
|
874
|
-
"\u{1F50D} Type Guard: Has address:",
|
|
875
|
-
hasAddress,
|
|
876
|
-
"Has public_key:",
|
|
877
|
-
hasPublicKey
|
|
878
|
-
);
|
|
879
|
-
return hasAddress && hasPublicKey;
|
|
880
|
-
}
|
|
881
|
-
function isPersonalServerOutput(value) {
|
|
882
|
-
if (typeof value !== "object" || value === null) return false;
|
|
883
|
-
const obj = value;
|
|
884
|
-
return "user_address" in obj && "identity" in obj && typeof obj.user_address === "string" && typeof obj.identity === "object";
|
|
885
|
-
}
|
|
886
691
|
function isAPIResponse(value) {
|
|
887
692
|
if (typeof value !== "object" || value === null) return false;
|
|
888
693
|
const obj = value;
|
|
@@ -7913,6 +7718,12 @@ var DataRefinerRegistryABI = [
|
|
|
7913
7718
|
name: "schemaId",
|
|
7914
7719
|
type: "uint256"
|
|
7915
7720
|
},
|
|
7721
|
+
{
|
|
7722
|
+
indexed: false,
|
|
7723
|
+
internalType: "string",
|
|
7724
|
+
name: "schemaDefinitionUrl",
|
|
7725
|
+
type: "string"
|
|
7726
|
+
},
|
|
7916
7727
|
{
|
|
7917
7728
|
indexed: false,
|
|
7918
7729
|
internalType: "string",
|
|
@@ -8016,7 +7827,7 @@ var DataRefinerRegistryABI = [
|
|
|
8016
7827
|
{
|
|
8017
7828
|
indexed: false,
|
|
8018
7829
|
internalType: "string",
|
|
8019
|
-
name: "
|
|
7830
|
+
name: "dialect",
|
|
8020
7831
|
type: "string"
|
|
8021
7832
|
},
|
|
8022
7833
|
{
|
|
@@ -8112,6 +7923,40 @@ var DataRefinerRegistryABI = [
|
|
|
8112
7923
|
stateMutability: "nonpayable",
|
|
8113
7924
|
type: "function"
|
|
8114
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
|
+
},
|
|
8115
7960
|
{
|
|
8116
7961
|
inputs: [
|
|
8117
7962
|
{
|
|
@@ -8135,7 +7980,7 @@ var DataRefinerRegistryABI = [
|
|
|
8135
7980
|
type: "string"
|
|
8136
7981
|
}
|
|
8137
7982
|
],
|
|
8138
|
-
name: "
|
|
7983
|
+
name: "addRefinerWithSchemaId",
|
|
8139
7984
|
outputs: [
|
|
8140
7985
|
{
|
|
8141
7986
|
internalType: "uint256",
|
|
@@ -8155,7 +8000,7 @@ var DataRefinerRegistryABI = [
|
|
|
8155
8000
|
},
|
|
8156
8001
|
{
|
|
8157
8002
|
internalType: "string",
|
|
8158
|
-
name: "
|
|
8003
|
+
name: "dialect",
|
|
8159
8004
|
type: "string"
|
|
8160
8005
|
},
|
|
8161
8006
|
{
|
|
@@ -8518,7 +8363,7 @@ var DataRefinerRegistryABI = [
|
|
|
8518
8363
|
},
|
|
8519
8364
|
{
|
|
8520
8365
|
internalType: "string",
|
|
8521
|
-
name: "
|
|
8366
|
+
name: "dialect",
|
|
8522
8367
|
type: "string"
|
|
8523
8368
|
},
|
|
8524
8369
|
{
|
|
@@ -33435,7 +33280,7 @@ function getAbi(contract) {
|
|
|
33435
33280
|
import { keccak256, toHex } from "viem";
|
|
33436
33281
|
function createGrantFile(params) {
|
|
33437
33282
|
const grantFile = {
|
|
33438
|
-
grantee: params.
|
|
33283
|
+
grantee: params.grantee,
|
|
33439
33284
|
operation: params.operation,
|
|
33440
33285
|
parameters: params.parameters
|
|
33441
33286
|
};
|
|
@@ -33855,7 +33700,7 @@ var PermissionsController = class {
|
|
|
33855
33700
|
* @example
|
|
33856
33701
|
* ```typescript
|
|
33857
33702
|
* const txHash = await vana.permissions.grant({
|
|
33858
|
-
*
|
|
33703
|
+
* grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
|
|
33859
33704
|
* operation: "llm_inference",
|
|
33860
33705
|
* parameters: {
|
|
33861
33706
|
* model: "gpt-4",
|
|
@@ -33884,7 +33729,7 @@ var PermissionsController = class {
|
|
|
33884
33729
|
* @example
|
|
33885
33730
|
* ```typescript
|
|
33886
33731
|
* const { preview, confirm } = await vana.permissions.prepareGrant({
|
|
33887
|
-
*
|
|
33732
|
+
* grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
|
|
33888
33733
|
* operation: "llm_inference",
|
|
33889
33734
|
* files: [1, 2, 3],
|
|
33890
33735
|
* parameters: { model: "gpt-4", prompt: "Analyze my social media data" }
|
|
@@ -33933,9 +33778,13 @@ var PermissionsController = class {
|
|
|
33933
33778
|
console.debug("\u{1F50D} Debug - Grant URL from params:", grantUrl);
|
|
33934
33779
|
if (!grantUrl) {
|
|
33935
33780
|
if (!this.context.relayerCallbacks?.storeGrantFile && !this.context.storageManager) {
|
|
33936
|
-
|
|
33937
|
-
|
|
33938
|
-
|
|
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
|
+
}
|
|
33939
33788
|
}
|
|
33940
33789
|
if (this.context.relayerCallbacks?.storeGrantFile) {
|
|
33941
33790
|
grantUrl = await this.context.relayerCallbacks.storeGrantFile(grantFile);
|
|
@@ -33959,7 +33808,7 @@ var PermissionsController = class {
|
|
|
33959
33808
|
grantUrl
|
|
33960
33809
|
);
|
|
33961
33810
|
const typedData = await this.composePermissionGrantMessage({
|
|
33962
|
-
|
|
33811
|
+
grantee: params.grantee,
|
|
33963
33812
|
operation: params.operation,
|
|
33964
33813
|
// Placeholder - real data is in IPFS
|
|
33965
33814
|
files: params.files,
|
|
@@ -34006,7 +33855,7 @@ var PermissionsController = class {
|
|
|
34006
33855
|
* @example
|
|
34007
33856
|
* ```typescript
|
|
34008
33857
|
* const { typedData, signature } = await vana.permissions.createAndSign({
|
|
34009
|
-
*
|
|
33858
|
+
* grantee: "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36",
|
|
34010
33859
|
* operation: "data_analysis",
|
|
34011
33860
|
* parameters: { analysisType: "sentiment" },
|
|
34012
33861
|
* });
|
|
@@ -34022,9 +33871,13 @@ var PermissionsController = class {
|
|
|
34022
33871
|
console.debug("\u{1F50D} Debug - Grant URL from params:", grantUrl);
|
|
34023
33872
|
if (!grantUrl) {
|
|
34024
33873
|
if (!this.context.relayerCallbacks?.storeGrantFile && !this.context.storageManager) {
|
|
34025
|
-
|
|
34026
|
-
|
|
34027
|
-
|
|
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
|
+
}
|
|
34028
33881
|
}
|
|
34029
33882
|
if (this.context.relayerCallbacks?.storeGrantFile) {
|
|
34030
33883
|
grantUrl = await this.context.relayerCallbacks.storeGrantFile(grantFile);
|
|
@@ -34048,7 +33901,7 @@ var PermissionsController = class {
|
|
|
34048
33901
|
grantUrl
|
|
34049
33902
|
);
|
|
34050
33903
|
const typedData = await this.composePermissionGrantMessage({
|
|
34051
|
-
|
|
33904
|
+
grantee: params.grantee,
|
|
34052
33905
|
operation: params.operation,
|
|
34053
33906
|
// Placeholder - real data is in IPFS
|
|
34054
33907
|
files: params.files,
|
|
@@ -34399,7 +34252,7 @@ var PermissionsController = class {
|
|
|
34399
34252
|
* Composes the EIP-712 typed data for PermissionGrant (new simplified format).
|
|
34400
34253
|
*
|
|
34401
34254
|
* @param params - The parameters for composing the permission grant message
|
|
34402
|
-
* @param params.
|
|
34255
|
+
* @param params.grantee - The recipient address for the permission grant
|
|
34403
34256
|
* @param params.operation - The type of operation being granted permission for
|
|
34404
34257
|
* @param params.files - Array of file IDs that the permission applies to
|
|
34405
34258
|
* @param params.grantUrl - URL where the grant details are stored
|
|
@@ -34492,36 +34345,45 @@ var PermissionsController = class {
|
|
|
34492
34345
|
return addresses[0];
|
|
34493
34346
|
}
|
|
34494
34347
|
/**
|
|
34495
|
-
*
|
|
34348
|
+
* Gets on-chain permission grant data without expensive off-chain resolution.
|
|
34496
34349
|
*
|
|
34497
34350
|
* @remarks
|
|
34498
|
-
* This method
|
|
34499
|
-
*
|
|
34500
|
-
*
|
|
34501
|
-
*
|
|
34502
|
-
*
|
|
34503
|
-
*
|
|
34504
|
-
*
|
|
34505
|
-
*
|
|
34506
|
-
*
|
|
34507
|
-
*
|
|
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
|
|
34508
34366
|
* @example
|
|
34509
34367
|
* ```typescript
|
|
34510
|
-
* // Get all
|
|
34511
|
-
* const
|
|
34368
|
+
* // Fast: Get all on-chain permission data
|
|
34369
|
+
* const grants = await vana.permissions.getUserPermissionGrantsOnChain({ limit: 20 });
|
|
34512
34370
|
*
|
|
34513
|
-
*
|
|
34514
|
-
*
|
|
34371
|
+
* // Display in UI immediately
|
|
34372
|
+
* grants.forEach(grant => {
|
|
34373
|
+
* console.log(`Permission ${grant.id}: ${grant.grantUrl}`);
|
|
34515
34374
|
* });
|
|
34516
34375
|
*
|
|
34517
|
-
* //
|
|
34518
|
-
* const
|
|
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);
|
|
34519
34380
|
* ```
|
|
34520
34381
|
*/
|
|
34521
|
-
async
|
|
34382
|
+
async getUserPermissionGrantsOnChain(options = {}) {
|
|
34383
|
+
const { limit = 50, subgraphUrl } = options;
|
|
34522
34384
|
try {
|
|
34523
34385
|
const userAddress = await this.getUserAddress();
|
|
34524
|
-
const graphqlEndpoint =
|
|
34386
|
+
const graphqlEndpoint = subgraphUrl || this.context.subgraphUrl;
|
|
34525
34387
|
if (!graphqlEndpoint) {
|
|
34526
34388
|
throw new BlockchainError(
|
|
34527
34389
|
"subgraphUrl is required. Please provide a valid subgraph endpoint or configure it in Vana constructor."
|
|
@@ -34543,16 +34405,6 @@ var PermissionsController = class {
|
|
|
34543
34405
|
}
|
|
34544
34406
|
}
|
|
34545
34407
|
`;
|
|
34546
|
-
console.info("Query:", query);
|
|
34547
|
-
console.info(
|
|
34548
|
-
"Body:",
|
|
34549
|
-
JSON.stringify({
|
|
34550
|
-
query,
|
|
34551
|
-
variables: {
|
|
34552
|
-
userId: userAddress.toLowerCase()
|
|
34553
|
-
}
|
|
34554
|
-
})
|
|
34555
|
-
);
|
|
34556
34408
|
const response = await fetch(graphqlEndpoint, {
|
|
34557
34409
|
method: "POST",
|
|
34558
34410
|
headers: {
|
|
@@ -34571,7 +34423,6 @@ var PermissionsController = class {
|
|
|
34571
34423
|
);
|
|
34572
34424
|
}
|
|
34573
34425
|
const result = await response.json();
|
|
34574
|
-
console.info("Result:", result);
|
|
34575
34426
|
if (result.errors) {
|
|
34576
34427
|
throw new BlockchainError(
|
|
34577
34428
|
`Subgraph errors: ${result.errors.map((e) => e.message).join(", ")}`
|
|
@@ -34579,64 +34430,32 @@ var PermissionsController = class {
|
|
|
34579
34430
|
}
|
|
34580
34431
|
const userData = result.data?.user;
|
|
34581
34432
|
if (!userData || !userData.permissions?.length) {
|
|
34582
|
-
console.warn("No permissions found for user:", userAddress);
|
|
34583
34433
|
return [];
|
|
34584
34434
|
}
|
|
34585
|
-
const
|
|
34586
|
-
|
|
34587
|
-
|
|
34588
|
-
|
|
34589
|
-
|
|
34590
|
-
|
|
34591
|
-
|
|
34592
|
-
|
|
34593
|
-
|
|
34594
|
-
|
|
34595
|
-
|
|
34596
|
-
|
|
34597
|
-
|
|
34598
|
-
|
|
34599
|
-
} catch {
|
|
34600
|
-
}
|
|
34601
|
-
try {
|
|
34602
|
-
const fileIds = await this.getPermissionFileIds(
|
|
34603
|
-
BigInt(permission.id)
|
|
34604
|
-
);
|
|
34605
|
-
files = fileIds.map((id) => Number(id));
|
|
34606
|
-
} catch {
|
|
34607
|
-
}
|
|
34608
|
-
userPermissions.push({
|
|
34609
|
-
id: BigInt(permission.id),
|
|
34610
|
-
files,
|
|
34611
|
-
operation: operation || "",
|
|
34612
|
-
parameters: parameters || {},
|
|
34613
|
-
grant: permission.grant,
|
|
34614
|
-
grantor: userAddress.toLowerCase(),
|
|
34615
|
-
// Current user is the grantor
|
|
34616
|
-
grantee: granteeAddress || userAddress,
|
|
34617
|
-
// Application that received permission
|
|
34618
|
-
active: true,
|
|
34619
|
-
// Default to active if not specified
|
|
34620
|
-
grantedAt: Number(permission.addedAtBlock),
|
|
34621
|
-
nonce: Number(permission.nonce)
|
|
34622
|
-
});
|
|
34623
|
-
} catch (error) {
|
|
34624
|
-
console.error(
|
|
34625
|
-
"SDK Error: Failed to process permission:",
|
|
34626
|
-
permission.id,
|
|
34627
|
-
error
|
|
34628
|
-
);
|
|
34629
|
-
}
|
|
34630
|
-
}
|
|
34631
|
-
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) => {
|
|
34632
34449
|
if (a.id < b.id) return 1;
|
|
34633
34450
|
if (a.id > b.id) return -1;
|
|
34634
34451
|
return 0;
|
|
34635
34452
|
});
|
|
34636
34453
|
} catch (error) {
|
|
34637
|
-
|
|
34454
|
+
if (error instanceof BlockchainError || error instanceof NetworkError) {
|
|
34455
|
+
throw error;
|
|
34456
|
+
}
|
|
34638
34457
|
throw new BlockchainError(
|
|
34639
|
-
`Failed to fetch user
|
|
34458
|
+
`Failed to fetch user permission grants: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
34640
34459
|
);
|
|
34641
34460
|
}
|
|
34642
34461
|
}
|
|
@@ -35334,590 +35153,6 @@ var PermissionsController = class {
|
|
|
35334
35153
|
// src/controllers/data.ts
|
|
35335
35154
|
import { getContract, decodeEventLog } from "viem";
|
|
35336
35155
|
|
|
35337
|
-
// src/controllers/server.ts
|
|
35338
|
-
var ServerController = class {
|
|
35339
|
-
constructor(context) {
|
|
35340
|
-
this.context = context;
|
|
35341
|
-
__publicField(this, "REPLICATE_API_URL", "https://api.replicate.com/v1/predictions");
|
|
35342
|
-
__publicField(this, "PERSONAL_SERVER_VERSION", "vana-com/personal-server:6dae0fead4017557a2e6bd020643359ac1769228a9cfe3bd2365eeeb9711610e");
|
|
35343
|
-
__publicField(this, "IDENTITY_SERVER_VERSION", "vana-com/identity-server:8e357fbeb87c0558b545809cabd0ef2f311082d8ce1f12b93cb8ad2f38cfbfd2");
|
|
35344
|
-
}
|
|
35345
|
-
/**
|
|
35346
|
-
* Posts a computation request to a user's personal server.
|
|
35347
|
-
*
|
|
35348
|
-
* @remarks
|
|
35349
|
-
* This method submits a computation request to the specified user's personal server
|
|
35350
|
-
* via the Replicate API. It creates a signed request with the user address and
|
|
35351
|
-
* permission ID, then submits it for processing. The response includes URLs for
|
|
35352
|
-
* polling results and canceling the computation if needed.
|
|
35353
|
-
*
|
|
35354
|
-
* The method requires a valid Replicate API token and uses the application's
|
|
35355
|
-
* wallet client for request signing to ensure authenticity.
|
|
35356
|
-
* @param params - The request parameters object
|
|
35357
|
-
* @param params.permissionId - The permission ID authorizing this computation
|
|
35358
|
-
* @returns A Promise that resolves to a prediction response with status and control URLs
|
|
35359
|
-
* @throws {PersonalServerError} When server request fails or parameters are invalid
|
|
35360
|
-
* @throws {SignatureError} When request signing fails
|
|
35361
|
-
* @throws {NetworkError} When Replicate API communication fails
|
|
35362
|
-
* @example
|
|
35363
|
-
* ```typescript
|
|
35364
|
-
* const response = await vana.server.postRequest({
|
|
35365
|
-
* permissionId: 123,
|
|
35366
|
-
* });
|
|
35367
|
-
*
|
|
35368
|
-
* console.log(`Request submitted: ${response.id}`);
|
|
35369
|
-
* console.log(`Poll for results: ${response.urls.get}`);
|
|
35370
|
-
* ```
|
|
35371
|
-
*/
|
|
35372
|
-
async postRequest(params) {
|
|
35373
|
-
try {
|
|
35374
|
-
this.validatePostRequestParams(params);
|
|
35375
|
-
const requestJson = this.createRequestJson(params);
|
|
35376
|
-
const signature = await this.createSignature(requestJson);
|
|
35377
|
-
const replicateInput = {
|
|
35378
|
-
replicate_api_token: this.getReplicateApiToken(),
|
|
35379
|
-
signature,
|
|
35380
|
-
request_json: requestJson
|
|
35381
|
-
};
|
|
35382
|
-
console.debug("\u{1F50D} Debug - replicateInput", replicateInput);
|
|
35383
|
-
const response = await this.makeReplicateRequest(replicateInput);
|
|
35384
|
-
return response;
|
|
35385
|
-
} catch (error) {
|
|
35386
|
-
if (error instanceof Error) {
|
|
35387
|
-
if (error instanceof NetworkError || error instanceof SerializationError || error instanceof SignatureError || error instanceof PersonalServerError) {
|
|
35388
|
-
throw error;
|
|
35389
|
-
}
|
|
35390
|
-
throw new PersonalServerError(
|
|
35391
|
-
`Personal server request failed: ${error.message}`,
|
|
35392
|
-
error
|
|
35393
|
-
);
|
|
35394
|
-
}
|
|
35395
|
-
throw new PersonalServerError(
|
|
35396
|
-
"Personal server request failed with unknown error"
|
|
35397
|
-
);
|
|
35398
|
-
}
|
|
35399
|
-
}
|
|
35400
|
-
/**
|
|
35401
|
-
* Initializes the personal server and fetches user identity.
|
|
35402
|
-
*
|
|
35403
|
-
* @param params - The request parameters containing user address
|
|
35404
|
-
* @returns Promise resolving to the user's identity information
|
|
35405
|
-
*/
|
|
35406
|
-
async initPersonalServer(params) {
|
|
35407
|
-
try {
|
|
35408
|
-
this.validateInitPersonalServerParams(params);
|
|
35409
|
-
const response = await this.makePersonalServerRequest(params);
|
|
35410
|
-
const result = await this.pollPersonalServerResult(response);
|
|
35411
|
-
return result;
|
|
35412
|
-
} catch (error) {
|
|
35413
|
-
if (error instanceof Error) {
|
|
35414
|
-
if (error instanceof NetworkError || error instanceof SerializationError || error instanceof PersonalServerError) {
|
|
35415
|
-
throw error;
|
|
35416
|
-
}
|
|
35417
|
-
throw new PersonalServerError(
|
|
35418
|
-
`Personal server initialization failed: ${error.message}`,
|
|
35419
|
-
error
|
|
35420
|
-
);
|
|
35421
|
-
}
|
|
35422
|
-
throw new PersonalServerError(
|
|
35423
|
-
"Personal server initialization failed with unknown error"
|
|
35424
|
-
);
|
|
35425
|
-
}
|
|
35426
|
-
}
|
|
35427
|
-
/**
|
|
35428
|
-
* Retrieves the public key for a user's personal server via the Identity Server.
|
|
35429
|
-
*
|
|
35430
|
-
* @remarks
|
|
35431
|
-
* This method uses the Identity Server to deterministically derive the personal server's
|
|
35432
|
-
* public key from the user's EVM address. This enables anyone to encrypt data for a
|
|
35433
|
-
* specific user's server without requiring that server to be online. The Identity Server
|
|
35434
|
-
* provides a reliable way to obtain encryption keys for secure data sharing across the
|
|
35435
|
-
* Vana network.
|
|
35436
|
-
*
|
|
35437
|
-
* The derived public key is deterministic and consistent, allowing for predictable
|
|
35438
|
-
* encryption workflows in decentralized applications.
|
|
35439
|
-
* @param userAddress - The user's EVM address to derive the server public key for
|
|
35440
|
-
* @returns A Promise that resolves to the server's public key as a hex string
|
|
35441
|
-
* @throws {PersonalServerError} When user address is invalid or server lookup fails
|
|
35442
|
-
* @throws {NetworkError} When Identity Server API request fails
|
|
35443
|
-
* @example
|
|
35444
|
-
* ```typescript
|
|
35445
|
-
* // Get public key for encrypting data to a user's server
|
|
35446
|
-
* const publicKey = await vana.server.getTrustedServerPublicKey(
|
|
35447
|
-
* "0x742d35Cc6558Fd4D9e9E0E888F0462ef6919Bd36"
|
|
35448
|
-
* );
|
|
35449
|
-
*
|
|
35450
|
-
* // Use the public key for encryption
|
|
35451
|
-
* const encryptedData = await encryptForServer(data, publicKey);
|
|
35452
|
-
* ```
|
|
35453
|
-
*/
|
|
35454
|
-
async getTrustedServerPublicKey(userAddress) {
|
|
35455
|
-
try {
|
|
35456
|
-
if (!userAddress || typeof userAddress !== "string") {
|
|
35457
|
-
throw new PersonalServerError(
|
|
35458
|
-
"User address is required and must be a valid string"
|
|
35459
|
-
);
|
|
35460
|
-
}
|
|
35461
|
-
if (!userAddress.startsWith("0x") || userAddress.length !== 42) {
|
|
35462
|
-
throw new PersonalServerError(
|
|
35463
|
-
"User address must be a valid EVM address"
|
|
35464
|
-
);
|
|
35465
|
-
}
|
|
35466
|
-
const requestBody = {
|
|
35467
|
-
version: this.IDENTITY_SERVER_VERSION,
|
|
35468
|
-
input: {
|
|
35469
|
-
user_address: userAddress
|
|
35470
|
-
}
|
|
35471
|
-
};
|
|
35472
|
-
console.debug("Identity Server Request for Public Key:", {
|
|
35473
|
-
url: this.REPLICATE_API_URL,
|
|
35474
|
-
method: "POST",
|
|
35475
|
-
body: requestBody
|
|
35476
|
-
});
|
|
35477
|
-
const response = await fetch(this.REPLICATE_API_URL, {
|
|
35478
|
-
method: "POST",
|
|
35479
|
-
headers: {
|
|
35480
|
-
Authorization: `Token ${this.getReplicateApiToken()}`,
|
|
35481
|
-
"Content-Type": "application/json"
|
|
35482
|
-
},
|
|
35483
|
-
body: JSON.stringify(requestBody)
|
|
35484
|
-
});
|
|
35485
|
-
if (!response.ok) {
|
|
35486
|
-
const errorText = await response.text();
|
|
35487
|
-
console.debug("Identity Server Error Response:", {
|
|
35488
|
-
status: response.status,
|
|
35489
|
-
statusText: response.statusText,
|
|
35490
|
-
error: errorText
|
|
35491
|
-
});
|
|
35492
|
-
throw new NetworkError(
|
|
35493
|
-
`Identity Server API request failed: ${response.status} ${response.statusText} - ${errorText}`
|
|
35494
|
-
);
|
|
35495
|
-
}
|
|
35496
|
-
const data = await response.json();
|
|
35497
|
-
console.debug("Identity Server Success Response:", data);
|
|
35498
|
-
const result = await this.pollIdentityServerResult({
|
|
35499
|
-
id: data.id,
|
|
35500
|
-
status: data.status,
|
|
35501
|
-
urls: {
|
|
35502
|
-
get: data.urls?.get || "",
|
|
35503
|
-
cancel: data.urls?.cancel || ""
|
|
35504
|
-
},
|
|
35505
|
-
input: data.input,
|
|
35506
|
-
output: data.output,
|
|
35507
|
-
error: data.error
|
|
35508
|
-
});
|
|
35509
|
-
return result;
|
|
35510
|
-
} catch (error) {
|
|
35511
|
-
if (error instanceof Error) {
|
|
35512
|
-
if (error instanceof NetworkError || error instanceof PersonalServerError) {
|
|
35513
|
-
throw error;
|
|
35514
|
-
}
|
|
35515
|
-
throw new PersonalServerError(
|
|
35516
|
-
`Failed to get trusted server public key: ${error.message}`,
|
|
35517
|
-
error
|
|
35518
|
-
);
|
|
35519
|
-
}
|
|
35520
|
-
throw new PersonalServerError(
|
|
35521
|
-
"Failed to get trusted server public key with unknown error"
|
|
35522
|
-
);
|
|
35523
|
-
}
|
|
35524
|
-
}
|
|
35525
|
-
/**
|
|
35526
|
-
* Polls the status of a computation request for updates and results.
|
|
35527
|
-
*
|
|
35528
|
-
* @remarks
|
|
35529
|
-
* This method checks the current status of a computation request by querying
|
|
35530
|
-
* the Replicate API using the provided polling URL. It returns the current
|
|
35531
|
-
* status, any available output, and error information. The method should be
|
|
35532
|
-
* called periodically until the computation completes or fails.
|
|
35533
|
-
*
|
|
35534
|
-
* Common status values include: `starting`, `processing`, `succeeded`, `failed`, `canceled`.
|
|
35535
|
-
* @param getUrl - The polling URL returned from the initial request submission
|
|
35536
|
-
* @returns A Promise that resolves to the current prediction response with status and results
|
|
35537
|
-
* @throws {NetworkError} When the polling request fails or returns invalid data
|
|
35538
|
-
* @example
|
|
35539
|
-
* ```typescript
|
|
35540
|
-
* // Poll until completion
|
|
35541
|
-
* let result = await vana.server.pollStatus(response.urls.get);
|
|
35542
|
-
*
|
|
35543
|
-
* while (result.status === "processing") {
|
|
35544
|
-
* await new Promise(resolve => setTimeout(resolve, 1000));
|
|
35545
|
-
* result = await vana.server.pollStatus(response.urls.get);
|
|
35546
|
-
* }
|
|
35547
|
-
*
|
|
35548
|
-
* if (result.status === "succeeded") {
|
|
35549
|
-
* console.log("Computation completed:", result.output);
|
|
35550
|
-
* }
|
|
35551
|
-
* ```
|
|
35552
|
-
*/
|
|
35553
|
-
async pollStatus(getUrl) {
|
|
35554
|
-
try {
|
|
35555
|
-
console.debug("Polling Replicate Status:", getUrl);
|
|
35556
|
-
const response = await fetch(getUrl, {
|
|
35557
|
-
method: "GET",
|
|
35558
|
-
headers: {
|
|
35559
|
-
Authorization: `Token ${this.getReplicateApiToken()}`,
|
|
35560
|
-
"Content-Type": "application/json"
|
|
35561
|
-
}
|
|
35562
|
-
});
|
|
35563
|
-
if (!response.ok) {
|
|
35564
|
-
const errorText = await response.text();
|
|
35565
|
-
console.debug("Polling Error Response:", {
|
|
35566
|
-
status: response.status,
|
|
35567
|
-
statusText: response.statusText,
|
|
35568
|
-
error: errorText
|
|
35569
|
-
});
|
|
35570
|
-
throw new NetworkError(
|
|
35571
|
-
`Status polling failed: ${response.status} ${response.statusText} - ${errorText}`
|
|
35572
|
-
);
|
|
35573
|
-
}
|
|
35574
|
-
const data = await response.json();
|
|
35575
|
-
console.debug("Polling Success Response:", data);
|
|
35576
|
-
return {
|
|
35577
|
-
id: data.id,
|
|
35578
|
-
status: data.status,
|
|
35579
|
-
urls: {
|
|
35580
|
-
get: data.urls?.get || getUrl,
|
|
35581
|
-
cancel: data.urls?.cancel || ""
|
|
35582
|
-
},
|
|
35583
|
-
input: data.input,
|
|
35584
|
-
output: data.output,
|
|
35585
|
-
error: data.error
|
|
35586
|
-
};
|
|
35587
|
-
} catch (error) {
|
|
35588
|
-
if (error instanceof NetworkError) {
|
|
35589
|
-
throw error;
|
|
35590
|
-
}
|
|
35591
|
-
throw new NetworkError(
|
|
35592
|
-
`Failed to poll status: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
35593
|
-
);
|
|
35594
|
-
}
|
|
35595
|
-
}
|
|
35596
|
-
/**
|
|
35597
|
-
* Validates the post request parameters.
|
|
35598
|
-
*
|
|
35599
|
-
* @param params - The post request parameters to validate
|
|
35600
|
-
*/
|
|
35601
|
-
validatePostRequestParams(params) {
|
|
35602
|
-
if (typeof params.permissionId !== "number" || params.permissionId <= 0) {
|
|
35603
|
-
throw new PersonalServerError(
|
|
35604
|
-
"Permission ID is required and must be a valid positive number"
|
|
35605
|
-
);
|
|
35606
|
-
}
|
|
35607
|
-
}
|
|
35608
|
-
/**
|
|
35609
|
-
* Validates the init personal server parameters.
|
|
35610
|
-
*
|
|
35611
|
-
* @param params - The initialization parameters to validate
|
|
35612
|
-
*/
|
|
35613
|
-
validateInitPersonalServerParams(params) {
|
|
35614
|
-
if (!params.userAddress || typeof params.userAddress !== "string") {
|
|
35615
|
-
throw new PersonalServerError(
|
|
35616
|
-
"User address is required and must be a valid string"
|
|
35617
|
-
);
|
|
35618
|
-
}
|
|
35619
|
-
if (!params.userAddress.startsWith("0x") || params.userAddress.length !== 42) {
|
|
35620
|
-
throw new PersonalServerError(
|
|
35621
|
-
"User address must be a valid Vana address"
|
|
35622
|
-
);
|
|
35623
|
-
}
|
|
35624
|
-
}
|
|
35625
|
-
/**
|
|
35626
|
-
* Creates the request JSON string for the personal server.
|
|
35627
|
-
*
|
|
35628
|
-
* @param params - The post request parameters to serialize
|
|
35629
|
-
* @returns JSON string representation of the request data
|
|
35630
|
-
*/
|
|
35631
|
-
createRequestJson(params) {
|
|
35632
|
-
try {
|
|
35633
|
-
const requestData = {
|
|
35634
|
-
permission_id: params.permissionId
|
|
35635
|
-
};
|
|
35636
|
-
return JSON.stringify(requestData);
|
|
35637
|
-
} catch (error) {
|
|
35638
|
-
throw new SerializationError(
|
|
35639
|
-
`Failed to create request JSON: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
35640
|
-
);
|
|
35641
|
-
}
|
|
35642
|
-
}
|
|
35643
|
-
/**
|
|
35644
|
-
* Creates a signature for the request JSON.
|
|
35645
|
-
*
|
|
35646
|
-
* @param requestJson - The JSON string to sign
|
|
35647
|
-
* @returns Promise resolving to the cryptographic signature
|
|
35648
|
-
*/
|
|
35649
|
-
async createSignature(requestJson) {
|
|
35650
|
-
try {
|
|
35651
|
-
console.debug("\u{1F50D} Debug - createSignature", requestJson);
|
|
35652
|
-
const client = this.context.applicationClient || this.context.walletClient;
|
|
35653
|
-
const account = client.account;
|
|
35654
|
-
if (!account) {
|
|
35655
|
-
throw new SignatureError("No account available for signing");
|
|
35656
|
-
}
|
|
35657
|
-
if (account.type !== "local") {
|
|
35658
|
-
throw new SignatureError(
|
|
35659
|
-
"Only local accounts are supported for signing"
|
|
35660
|
-
);
|
|
35661
|
-
}
|
|
35662
|
-
const signature = await account.signMessage({
|
|
35663
|
-
message: requestJson
|
|
35664
|
-
});
|
|
35665
|
-
return signature;
|
|
35666
|
-
} catch (error) {
|
|
35667
|
-
if (error instanceof Error && error.message.includes("User rejected")) {
|
|
35668
|
-
throw new SignatureError("User rejected the signature request");
|
|
35669
|
-
}
|
|
35670
|
-
throw new SignatureError(
|
|
35671
|
-
`Failed to create signature: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
35672
|
-
);
|
|
35673
|
-
}
|
|
35674
|
-
}
|
|
35675
|
-
/**
|
|
35676
|
-
* Gets the Replicate API token from environment.
|
|
35677
|
-
*
|
|
35678
|
-
* @returns The Replicate API token from environment variables
|
|
35679
|
-
*/
|
|
35680
|
-
getReplicateApiToken() {
|
|
35681
|
-
const token = process.env.REPLICATE_API_TOKEN || process.env.NEXT_PUBLIC_REPLICATE_API_TOKEN;
|
|
35682
|
-
if (!token) {
|
|
35683
|
-
throw new PersonalServerError(
|
|
35684
|
-
"REPLICATE_API_TOKEN environment variable is required"
|
|
35685
|
-
);
|
|
35686
|
-
}
|
|
35687
|
-
return token;
|
|
35688
|
-
}
|
|
35689
|
-
/**
|
|
35690
|
-
* Makes the request to the Replicate API.
|
|
35691
|
-
*
|
|
35692
|
-
* @param input - The input parameters for the Replicate API request
|
|
35693
|
-
* @returns Promise resolving to the Replicate prediction response
|
|
35694
|
-
*/
|
|
35695
|
-
async makeReplicateRequest(input) {
|
|
35696
|
-
try {
|
|
35697
|
-
const requestBody = {
|
|
35698
|
-
version: this.PERSONAL_SERVER_VERSION,
|
|
35699
|
-
input
|
|
35700
|
-
};
|
|
35701
|
-
console.debug("Replicate Request:", {
|
|
35702
|
-
url: this.REPLICATE_API_URL,
|
|
35703
|
-
method: "POST",
|
|
35704
|
-
headers: {
|
|
35705
|
-
Authorization: `Token ${this.getReplicateApiToken().substring(0, 10)}...`,
|
|
35706
|
-
"Content-Type": "application/json"
|
|
35707
|
-
},
|
|
35708
|
-
body: requestBody
|
|
35709
|
-
});
|
|
35710
|
-
const response = await fetch(this.REPLICATE_API_URL, {
|
|
35711
|
-
method: "POST",
|
|
35712
|
-
headers: {
|
|
35713
|
-
Authorization: `Token ${this.getReplicateApiToken()}`,
|
|
35714
|
-
"Content-Type": "application/json"
|
|
35715
|
-
},
|
|
35716
|
-
body: JSON.stringify(requestBody)
|
|
35717
|
-
});
|
|
35718
|
-
if (!response.ok) {
|
|
35719
|
-
const errorText = await response.text();
|
|
35720
|
-
console.debug("Replicate Error Response:", {
|
|
35721
|
-
status: response.status,
|
|
35722
|
-
statusText: response.statusText,
|
|
35723
|
-
error: errorText
|
|
35724
|
-
});
|
|
35725
|
-
throw new NetworkError(
|
|
35726
|
-
`Replicate API request failed: ${response.status} ${response.statusText} - ${errorText}`
|
|
35727
|
-
);
|
|
35728
|
-
}
|
|
35729
|
-
const data = await response.json();
|
|
35730
|
-
console.debug("Replicate Success Response:", data);
|
|
35731
|
-
return {
|
|
35732
|
-
id: data.id,
|
|
35733
|
-
status: data.status,
|
|
35734
|
-
urls: {
|
|
35735
|
-
get: data.urls?.get || "",
|
|
35736
|
-
cancel: data.urls?.cancel || ""
|
|
35737
|
-
},
|
|
35738
|
-
input: data.input,
|
|
35739
|
-
output: data.output,
|
|
35740
|
-
error: data.error
|
|
35741
|
-
};
|
|
35742
|
-
} catch (error) {
|
|
35743
|
-
if (error instanceof NetworkError) {
|
|
35744
|
-
throw error;
|
|
35745
|
-
}
|
|
35746
|
-
throw new NetworkError(
|
|
35747
|
-
`Failed to make Replicate API request: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
35748
|
-
);
|
|
35749
|
-
}
|
|
35750
|
-
}
|
|
35751
|
-
/**
|
|
35752
|
-
* Makes the request to the personal server.
|
|
35753
|
-
*
|
|
35754
|
-
* @param params - The initialization parameters for the personal server
|
|
35755
|
-
* @returns Promise resolving to the Replicate prediction response
|
|
35756
|
-
*/
|
|
35757
|
-
async makePersonalServerRequest(params) {
|
|
35758
|
-
try {
|
|
35759
|
-
const requestBody = {
|
|
35760
|
-
version: this.IDENTITY_SERVER_VERSION,
|
|
35761
|
-
input: {
|
|
35762
|
-
user_address: params.userAddress
|
|
35763
|
-
}
|
|
35764
|
-
};
|
|
35765
|
-
console.debug("Personal Server Request:", {
|
|
35766
|
-
url: this.REPLICATE_API_URL,
|
|
35767
|
-
method: "POST",
|
|
35768
|
-
headers: {
|
|
35769
|
-
Authorization: `Token ${this.getReplicateApiToken().substring(0, 10)}...`,
|
|
35770
|
-
"Content-Type": "application/json"
|
|
35771
|
-
},
|
|
35772
|
-
body: requestBody
|
|
35773
|
-
});
|
|
35774
|
-
const response = await fetch(this.REPLICATE_API_URL, {
|
|
35775
|
-
method: "POST",
|
|
35776
|
-
headers: {
|
|
35777
|
-
Authorization: `Token ${this.getReplicateApiToken()}`,
|
|
35778
|
-
"Content-Type": "application/json"
|
|
35779
|
-
},
|
|
35780
|
-
body: JSON.stringify(requestBody)
|
|
35781
|
-
});
|
|
35782
|
-
if (!response.ok) {
|
|
35783
|
-
const errorText = await response.text();
|
|
35784
|
-
console.debug("Personal Server Error Response:", {
|
|
35785
|
-
status: response.status,
|
|
35786
|
-
statusText: response.statusText,
|
|
35787
|
-
error: errorText
|
|
35788
|
-
});
|
|
35789
|
-
throw new NetworkError(
|
|
35790
|
-
`Personal Server API request failed: ${response.status} ${response.statusText} - ${errorText}`
|
|
35791
|
-
);
|
|
35792
|
-
}
|
|
35793
|
-
const data = await response.json();
|
|
35794
|
-
console.debug("Personal Server Success Response:", data);
|
|
35795
|
-
return {
|
|
35796
|
-
id: data.id,
|
|
35797
|
-
status: data.status,
|
|
35798
|
-
urls: {
|
|
35799
|
-
get: data.urls?.get || "",
|
|
35800
|
-
cancel: data.urls?.cancel || ""
|
|
35801
|
-
},
|
|
35802
|
-
input: data.input,
|
|
35803
|
-
output: data.output,
|
|
35804
|
-
error: data.error
|
|
35805
|
-
};
|
|
35806
|
-
} catch (error) {
|
|
35807
|
-
if (error instanceof NetworkError) {
|
|
35808
|
-
throw error;
|
|
35809
|
-
}
|
|
35810
|
-
throw new NetworkError(
|
|
35811
|
-
`Failed to make Personal Server API request: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
35812
|
-
);
|
|
35813
|
-
}
|
|
35814
|
-
}
|
|
35815
|
-
/**
|
|
35816
|
-
* Polls the identity server result until completion and extracts the public key.
|
|
35817
|
-
*
|
|
35818
|
-
* @param initialResponse - The initial response from the identity server
|
|
35819
|
-
* @returns Promise resolving to the extracted public key
|
|
35820
|
-
*/
|
|
35821
|
-
async pollIdentityServerResult(initialResponse) {
|
|
35822
|
-
const maxAttempts = 60;
|
|
35823
|
-
let attempts = 0;
|
|
35824
|
-
let currentResponse = initialResponse;
|
|
35825
|
-
while (attempts < maxAttempts) {
|
|
35826
|
-
if (currentResponse.status === "succeeded") {
|
|
35827
|
-
const output = currentResponse.output;
|
|
35828
|
-
let parsedOutput;
|
|
35829
|
-
if (typeof output === "string") {
|
|
35830
|
-
try {
|
|
35831
|
-
parsedOutput = JSON.parse(output);
|
|
35832
|
-
} catch {
|
|
35833
|
-
throw new PersonalServerError(
|
|
35834
|
-
"Failed to parse Identity Server response as JSON"
|
|
35835
|
-
);
|
|
35836
|
-
}
|
|
35837
|
-
} else {
|
|
35838
|
-
parsedOutput = output;
|
|
35839
|
-
}
|
|
35840
|
-
const personalServer = parsedOutput.personal_server;
|
|
35841
|
-
if (!personalServer || !personalServer.public_key) {
|
|
35842
|
-
throw new PersonalServerError(
|
|
35843
|
-
"Identity Server response missing personal_server.public_key"
|
|
35844
|
-
);
|
|
35845
|
-
}
|
|
35846
|
-
return personalServer.public_key;
|
|
35847
|
-
} else if (currentResponse.status === "failed") {
|
|
35848
|
-
throw new PersonalServerError(
|
|
35849
|
-
`Identity Server request failed: ${currentResponse.error || "Unknown error"}`
|
|
35850
|
-
);
|
|
35851
|
-
} else if (currentResponse.status === "canceled") {
|
|
35852
|
-
throw new PersonalServerError("Identity Server request was canceled");
|
|
35853
|
-
}
|
|
35854
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
35855
|
-
attempts++;
|
|
35856
|
-
if (currentResponse.urls.get) {
|
|
35857
|
-
currentResponse = await this.pollStatus(currentResponse.urls.get);
|
|
35858
|
-
}
|
|
35859
|
-
}
|
|
35860
|
-
throw new PersonalServerError(
|
|
35861
|
-
"Identity Server request timed out after 60 seconds"
|
|
35862
|
-
);
|
|
35863
|
-
}
|
|
35864
|
-
/**
|
|
35865
|
-
* Polls the personal server result until completion.
|
|
35866
|
-
*
|
|
35867
|
-
* @param initialResponse - The initial response from the personal server
|
|
35868
|
-
* @returns Promise resolving to the personal server response data
|
|
35869
|
-
*/
|
|
35870
|
-
async pollPersonalServerResult(initialResponse) {
|
|
35871
|
-
const maxAttempts = 60;
|
|
35872
|
-
let attempts = 0;
|
|
35873
|
-
let currentResponse = initialResponse;
|
|
35874
|
-
while (attempts < maxAttempts) {
|
|
35875
|
-
if (currentResponse.status === "succeeded") {
|
|
35876
|
-
const output = currentResponse.output;
|
|
35877
|
-
let parsedOutput;
|
|
35878
|
-
if (typeof output === "string") {
|
|
35879
|
-
try {
|
|
35880
|
-
parsedOutput = JSON.parse(output);
|
|
35881
|
-
} catch {
|
|
35882
|
-
parsedOutput = output;
|
|
35883
|
-
}
|
|
35884
|
-
} else {
|
|
35885
|
-
parsedOutput = output;
|
|
35886
|
-
}
|
|
35887
|
-
const personalServer = parsedOutput.personal_server;
|
|
35888
|
-
const derivedAddress = personalServer?.address;
|
|
35889
|
-
const publicKey = personalServer?.public_key;
|
|
35890
|
-
return {
|
|
35891
|
-
userAddress: parsedOutput.user_address,
|
|
35892
|
-
identity: {
|
|
35893
|
-
metadata: {
|
|
35894
|
-
derivedAddress,
|
|
35895
|
-
publicKey
|
|
35896
|
-
}
|
|
35897
|
-
},
|
|
35898
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
35899
|
-
};
|
|
35900
|
-
} else if (currentResponse.status === "failed") {
|
|
35901
|
-
throw new PersonalServerError(
|
|
35902
|
-
`Personal server initialization failed: ${currentResponse.error || "Unknown error"}`
|
|
35903
|
-
);
|
|
35904
|
-
} else if (currentResponse.status === "canceled") {
|
|
35905
|
-
throw new PersonalServerError(
|
|
35906
|
-
"Personal server initialization was canceled"
|
|
35907
|
-
);
|
|
35908
|
-
}
|
|
35909
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
35910
|
-
attempts++;
|
|
35911
|
-
if (currentResponse.urls.get) {
|
|
35912
|
-
currentResponse = await this.pollStatus(currentResponse.urls.get);
|
|
35913
|
-
}
|
|
35914
|
-
}
|
|
35915
|
-
throw new PersonalServerError(
|
|
35916
|
-
"Personal server initialization timed out after 60 seconds"
|
|
35917
|
-
);
|
|
35918
|
-
}
|
|
35919
|
-
};
|
|
35920
|
-
|
|
35921
35156
|
// src/utils/encryption.ts
|
|
35922
35157
|
var DEFAULT_ENCRYPTION_SEED = "Please sign to retrieve your encryption key";
|
|
35923
35158
|
async function generateEncryptionKey(wallet, seed = DEFAULT_ENCRYPTION_SEED) {
|
|
@@ -35982,32 +35217,19 @@ async function decryptWithPrivateKey(encryptedData, privateKey, platformAdapter)
|
|
|
35982
35217
|
throw new Error(`Failed to decrypt with private key: ${error}`);
|
|
35983
35218
|
}
|
|
35984
35219
|
}
|
|
35985
|
-
async function
|
|
35220
|
+
async function encryptBlobWithSignedKey(data, key, platformAdapter) {
|
|
35986
35221
|
try {
|
|
35987
35222
|
const dataBuffer = data instanceof Blob ? await data.arrayBuffer() : new TextEncoder().encode(data);
|
|
35988
35223
|
const dataArray = new Uint8Array(dataBuffer);
|
|
35989
35224
|
const encrypted = await platformAdapter.crypto.encryptWithPassword(
|
|
35990
35225
|
dataArray,
|
|
35991
|
-
|
|
35226
|
+
key
|
|
35992
35227
|
);
|
|
35993
35228
|
return new Blob([encrypted], {
|
|
35994
35229
|
type: "application/octet-stream"
|
|
35995
35230
|
});
|
|
35996
35231
|
} catch (error) {
|
|
35997
|
-
throw new Error(`Failed to encrypt
|
|
35998
|
-
}
|
|
35999
|
-
}
|
|
36000
|
-
async function decryptUserData(encryptedData, walletSignature, platformAdapter) {
|
|
36001
|
-
try {
|
|
36002
|
-
const encryptedBuffer = encryptedData instanceof Blob ? await encryptedData.arrayBuffer() : new TextEncoder().encode(encryptedData);
|
|
36003
|
-
const encryptedArray = new Uint8Array(encryptedBuffer);
|
|
36004
|
-
const decrypted = await platformAdapter.crypto.decryptWithPassword(
|
|
36005
|
-
encryptedArray,
|
|
36006
|
-
walletSignature
|
|
36007
|
-
);
|
|
36008
|
-
return new Blob([decrypted], { type: "text/plain" });
|
|
36009
|
-
} catch (error) {
|
|
36010
|
-
throw new Error(`Failed to decrypt user data: ${error}`);
|
|
35232
|
+
throw new Error(`Failed to encrypt data: ${error}`);
|
|
36011
35233
|
}
|
|
36012
35234
|
}
|
|
36013
35235
|
async function generateEncryptionKeyPair(platformAdapter) {
|
|
@@ -36024,13 +35246,315 @@ async function generatePGPKeyPair(platformAdapter, options) {
|
|
|
36024
35246
|
throw new Error(`Failed to generate PGP key pair: ${error}`);
|
|
36025
35247
|
}
|
|
36026
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
|
+
}
|
|
36027
35262
|
|
|
36028
35263
|
// src/controllers/data.ts
|
|
36029
35264
|
var DataController = class {
|
|
36030
35265
|
constructor(context) {
|
|
36031
35266
|
this.context = context;
|
|
36032
|
-
|
|
36033
|
-
|
|
35267
|
+
}
|
|
35268
|
+
async upload(params) {
|
|
35269
|
+
const {
|
|
35270
|
+
content,
|
|
35271
|
+
filename,
|
|
35272
|
+
schemaId,
|
|
35273
|
+
permissions = [],
|
|
35274
|
+
encrypt: encrypt2 = true,
|
|
35275
|
+
providerName
|
|
35276
|
+
} = params;
|
|
35277
|
+
try {
|
|
35278
|
+
let blob;
|
|
35279
|
+
if (content instanceof Blob) {
|
|
35280
|
+
blob = content;
|
|
35281
|
+
} else if (typeof content === "string") {
|
|
35282
|
+
blob = new Blob([content], { type: "text/plain" });
|
|
35283
|
+
} else if (content instanceof Buffer) {
|
|
35284
|
+
blob = new Blob([content], { type: "application/octet-stream" });
|
|
35285
|
+
} else {
|
|
35286
|
+
blob = new Blob([JSON.stringify(content)], {
|
|
35287
|
+
type: "application/json"
|
|
35288
|
+
});
|
|
35289
|
+
}
|
|
35290
|
+
let isValid = true;
|
|
35291
|
+
let validationErrors = [];
|
|
35292
|
+
if (schemaId !== void 0) {
|
|
35293
|
+
try {
|
|
35294
|
+
const schema = await this.getSchema(schemaId);
|
|
35295
|
+
const response = await fetch(schema.definitionUrl);
|
|
35296
|
+
if (!response.ok) {
|
|
35297
|
+
throw new Error(
|
|
35298
|
+
`Failed to fetch schema definition: ${response.status}`
|
|
35299
|
+
);
|
|
35300
|
+
}
|
|
35301
|
+
const schemaDefinition = await response.json();
|
|
35302
|
+
const dataSchema = {
|
|
35303
|
+
name: schema.name,
|
|
35304
|
+
version: "1.0.0",
|
|
35305
|
+
dialect: "json",
|
|
35306
|
+
schema: schemaDefinition
|
|
35307
|
+
};
|
|
35308
|
+
let parsedContent;
|
|
35309
|
+
if (typeof content === "string") {
|
|
35310
|
+
try {
|
|
35311
|
+
parsedContent = JSON.parse(content);
|
|
35312
|
+
} catch {
|
|
35313
|
+
parsedContent = content;
|
|
35314
|
+
}
|
|
35315
|
+
} else {
|
|
35316
|
+
parsedContent = content;
|
|
35317
|
+
}
|
|
35318
|
+
validateDataAgainstSchema(parsedContent, dataSchema);
|
|
35319
|
+
} catch (error) {
|
|
35320
|
+
isValid = false;
|
|
35321
|
+
validationErrors = [
|
|
35322
|
+
error instanceof Error ? error.message : "Schema validation failed"
|
|
35323
|
+
];
|
|
35324
|
+
}
|
|
35325
|
+
}
|
|
35326
|
+
let finalBlob = blob;
|
|
35327
|
+
if (encrypt2) {
|
|
35328
|
+
const encryptionKey = await generateEncryptionKey(
|
|
35329
|
+
this.context.walletClient,
|
|
35330
|
+
DEFAULT_ENCRYPTION_SEED
|
|
35331
|
+
);
|
|
35332
|
+
finalBlob = await encryptBlobWithSignedKey(
|
|
35333
|
+
blob,
|
|
35334
|
+
encryptionKey,
|
|
35335
|
+
this.context.platform
|
|
35336
|
+
);
|
|
35337
|
+
}
|
|
35338
|
+
if (!this.context.storageManager) {
|
|
35339
|
+
if (this.context.validateStorageRequired) {
|
|
35340
|
+
this.context.validateStorageRequired();
|
|
35341
|
+
throw new Error("Storage validation failed");
|
|
35342
|
+
} else {
|
|
35343
|
+
throw new Error(
|
|
35344
|
+
"Storage manager not configured. Please provide storage providers in VanaConfig."
|
|
35345
|
+
);
|
|
35346
|
+
}
|
|
35347
|
+
}
|
|
35348
|
+
const uploadResult = await this.context.storageManager.upload(
|
|
35349
|
+
finalBlob,
|
|
35350
|
+
filename,
|
|
35351
|
+
providerName
|
|
35352
|
+
);
|
|
35353
|
+
const userAddress = await this.getUserAddress();
|
|
35354
|
+
let encryptedPermissions = [];
|
|
35355
|
+
if (permissions.length > 0 && encrypt2) {
|
|
35356
|
+
const userEncryptionKey = await generateEncryptionKey(
|
|
35357
|
+
this.context.walletClient,
|
|
35358
|
+
DEFAULT_ENCRYPTION_SEED
|
|
35359
|
+
);
|
|
35360
|
+
encryptedPermissions = await Promise.all(
|
|
35361
|
+
permissions.map(async (permission) => {
|
|
35362
|
+
const publicKey = permission.publicKey;
|
|
35363
|
+
if (!publicKey) {
|
|
35364
|
+
throw new Error(
|
|
35365
|
+
`Public key required for permission to ${permission.grantee} when encryption is enabled`
|
|
35366
|
+
);
|
|
35367
|
+
}
|
|
35368
|
+
const encryptedKey = await encryptWithWalletPublicKey(
|
|
35369
|
+
userEncryptionKey,
|
|
35370
|
+
publicKey,
|
|
35371
|
+
this.context.platform
|
|
35372
|
+
);
|
|
35373
|
+
return {
|
|
35374
|
+
account: permission.grantee,
|
|
35375
|
+
key: encryptedKey
|
|
35376
|
+
};
|
|
35377
|
+
})
|
|
35378
|
+
);
|
|
35379
|
+
}
|
|
35380
|
+
let result;
|
|
35381
|
+
if (this.context.relayerCallbacks?.submitFileAdditionComplete) {
|
|
35382
|
+
result = await this.context.relayerCallbacks.submitFileAdditionComplete(
|
|
35383
|
+
{
|
|
35384
|
+
url: uploadResult.url,
|
|
35385
|
+
userAddress,
|
|
35386
|
+
permissions: encryptedPermissions,
|
|
35387
|
+
schemaId: schemaId || 0
|
|
35388
|
+
}
|
|
35389
|
+
);
|
|
35390
|
+
} else if (this.context.relayerCallbacks?.submitFileAddition) {
|
|
35391
|
+
const needsComplexRegistration = schemaId !== void 0 || encryptedPermissions.length > 0;
|
|
35392
|
+
if (needsComplexRegistration) {
|
|
35393
|
+
throw new Error(
|
|
35394
|
+
"The configured relay callback does not support schemas or permissions. Please update your relay server implementation to provide the `submitFileAdditionComplete` callback."
|
|
35395
|
+
);
|
|
35396
|
+
}
|
|
35397
|
+
result = await this.context.relayerCallbacks.submitFileAddition(
|
|
35398
|
+
uploadResult.url,
|
|
35399
|
+
userAddress
|
|
35400
|
+
);
|
|
35401
|
+
} else {
|
|
35402
|
+
result = await this.addFileWithPermissionsAndSchema(
|
|
35403
|
+
uploadResult.url,
|
|
35404
|
+
userAddress,
|
|
35405
|
+
encryptedPermissions,
|
|
35406
|
+
schemaId || 0
|
|
35407
|
+
);
|
|
35408
|
+
}
|
|
35409
|
+
return {
|
|
35410
|
+
fileId: result.fileId,
|
|
35411
|
+
url: uploadResult.url,
|
|
35412
|
+
transactionHash: result.transactionHash,
|
|
35413
|
+
size: uploadResult.size,
|
|
35414
|
+
isValid,
|
|
35415
|
+
validationErrors: validationErrors.length > 0 ? validationErrors : void 0
|
|
35416
|
+
};
|
|
35417
|
+
} catch (error) {
|
|
35418
|
+
throw new Error(
|
|
35419
|
+
`Upload failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
35420
|
+
);
|
|
35421
|
+
}
|
|
35422
|
+
}
|
|
35423
|
+
/**
|
|
35424
|
+
* Decrypts a file owned by the user using their wallet signature.
|
|
35425
|
+
*
|
|
35426
|
+
* @remarks
|
|
35427
|
+
* This is the high-level convenience method for decrypting user files, serving as the
|
|
35428
|
+
* symmetrical counterpart to the `upload` method. It handles the complete decryption
|
|
35429
|
+
* workflow including key generation, URL protocol detection, content fetching, and
|
|
35430
|
+
* decryption.
|
|
35431
|
+
*
|
|
35432
|
+
* The method automatically:
|
|
35433
|
+
* - Generates the decryption key from the user's wallet signature
|
|
35434
|
+
* - Determines the appropriate fetch method based on the file URL protocol
|
|
35435
|
+
* - Fetches the encrypted content from IPFS or standard HTTP URLs
|
|
35436
|
+
* - Decrypts the content using the generated key
|
|
35437
|
+
*
|
|
35438
|
+
* For IPFS URLs, the method uses gateway fallback for improved reliability. For
|
|
35439
|
+
* standard HTTP URLs, it uses a simple fetch. If you need custom authentication
|
|
35440
|
+
* headers or specific gateway configurations, use the low-level primitives directly.
|
|
35441
|
+
*
|
|
35442
|
+
* @param file - The user file to decrypt (typically from getUserFiles)
|
|
35443
|
+
* @param encryptionSeed - Optional custom encryption seed (defaults to Vana standard)
|
|
35444
|
+
* @returns Promise resolving to the decrypted file content as a Blob
|
|
35445
|
+
* @throws {Error} When the wallet is not connected
|
|
35446
|
+
* @throws {Error} When fetching the encrypted content fails
|
|
35447
|
+
* @throws {Error} When decryption fails (wrong key or corrupted data)
|
|
35448
|
+
* @example
|
|
35449
|
+
* ```typescript
|
|
35450
|
+
* // Basic file decryption
|
|
35451
|
+
* const files = await vana.data.getUserFiles({ owner: userAddress });
|
|
35452
|
+
* const decryptedBlob = await vana.data.decryptFile(files[0]);
|
|
35453
|
+
*
|
|
35454
|
+
* // Convert to text
|
|
35455
|
+
* const text = await decryptedBlob.text();
|
|
35456
|
+
* console.log('Decrypted content:', text);
|
|
35457
|
+
*
|
|
35458
|
+
* // Convert to JSON
|
|
35459
|
+
* const json = JSON.parse(await decryptedBlob.text());
|
|
35460
|
+
* console.log('Decrypted data:', json);
|
|
35461
|
+
*
|
|
35462
|
+
* // With custom encryption seed
|
|
35463
|
+
* const decryptedBlob = await vana.data.decryptFile(
|
|
35464
|
+
* files[0],
|
|
35465
|
+
* "My custom encryption seed"
|
|
35466
|
+
* );
|
|
35467
|
+
*
|
|
35468
|
+
* // Save to file (in Node.js)
|
|
35469
|
+
* const buffer = await decryptedBlob.arrayBuffer();
|
|
35470
|
+
* fs.writeFileSync('decrypted-file.txt', Buffer.from(buffer));
|
|
35471
|
+
* ```
|
|
35472
|
+
*/
|
|
35473
|
+
async decryptFile(file, encryptionSeed) {
|
|
35474
|
+
try {
|
|
35475
|
+
const encryptionKey = await generateEncryptionKey(
|
|
35476
|
+
this.context.walletClient,
|
|
35477
|
+
encryptionSeed || DEFAULT_ENCRYPTION_SEED
|
|
35478
|
+
);
|
|
35479
|
+
let encryptedBlob;
|
|
35480
|
+
try {
|
|
35481
|
+
if (file.url.startsWith("ipfs://")) {
|
|
35482
|
+
encryptedBlob = await this.fetchFromIPFS(file.url);
|
|
35483
|
+
} else {
|
|
35484
|
+
encryptedBlob = await this.fetch(file.url);
|
|
35485
|
+
}
|
|
35486
|
+
} catch (fetchError) {
|
|
35487
|
+
const errorMessage = fetchError instanceof Error ? fetchError.message : "Unknown error";
|
|
35488
|
+
if (errorMessage.includes("Failed to fetch IPFS content") && errorMessage.includes("from all gateways")) {
|
|
35489
|
+
throw new Error(
|
|
35490
|
+
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
35491
|
+
);
|
|
35492
|
+
} else if (errorMessage.includes("Empty response")) {
|
|
35493
|
+
throw new Error("File is empty or could not be retrieved");
|
|
35494
|
+
} else if (errorMessage.includes("Network error:") || errorMessage.includes("Failed to fetch")) {
|
|
35495
|
+
throw new Error(
|
|
35496
|
+
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
35497
|
+
);
|
|
35498
|
+
} else if (errorMessage.includes("HTTP error!")) {
|
|
35499
|
+
const statusMatch = errorMessage.match(/status: (\d+)/);
|
|
35500
|
+
const status = statusMatch ? statusMatch[1] : "unknown";
|
|
35501
|
+
if (status === "500") {
|
|
35502
|
+
throw new Error(
|
|
35503
|
+
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
35504
|
+
);
|
|
35505
|
+
} else if (status === "403") {
|
|
35506
|
+
throw new Error(
|
|
35507
|
+
"Access denied. You may not have permission to access this file"
|
|
35508
|
+
);
|
|
35509
|
+
} else if (status === "404") {
|
|
35510
|
+
throw new Error(
|
|
35511
|
+
"File not found: The encrypted file is no longer available at the stored URL."
|
|
35512
|
+
);
|
|
35513
|
+
} else {
|
|
35514
|
+
throw new Error(
|
|
35515
|
+
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
35516
|
+
);
|
|
35517
|
+
}
|
|
35518
|
+
}
|
|
35519
|
+
throw fetchError;
|
|
35520
|
+
}
|
|
35521
|
+
if (encryptedBlob.size === 0) {
|
|
35522
|
+
throw new Error("File is empty or could not be retrieved");
|
|
35523
|
+
}
|
|
35524
|
+
let decryptedBlob;
|
|
35525
|
+
try {
|
|
35526
|
+
decryptedBlob = await decryptBlobWithSignedKey(
|
|
35527
|
+
encryptedBlob,
|
|
35528
|
+
encryptionKey,
|
|
35529
|
+
this.context.platform
|
|
35530
|
+
);
|
|
35531
|
+
} catch (decryptError) {
|
|
35532
|
+
const errorMessage = decryptError instanceof Error ? decryptError.message : "Unknown error";
|
|
35533
|
+
if (errorMessage.includes("not a valid OpenPGP message")) {
|
|
35534
|
+
throw new Error(
|
|
35535
|
+
"Invalid file format: This file doesn't appear to be encrypted with the Vana protocol"
|
|
35536
|
+
);
|
|
35537
|
+
} else if (errorMessage.includes("Session key decryption failed")) {
|
|
35538
|
+
throw new Error("Wrong encryption key");
|
|
35539
|
+
} else if (errorMessage.includes("Error decrypting message")) {
|
|
35540
|
+
throw new Error("Wrong encryption key");
|
|
35541
|
+
} else if (errorMessage.includes("File not found")) {
|
|
35542
|
+
throw new Error(
|
|
35543
|
+
"File not found: The encrypted file is no longer available"
|
|
35544
|
+
);
|
|
35545
|
+
} else {
|
|
35546
|
+
throw decryptError;
|
|
35547
|
+
}
|
|
35548
|
+
}
|
|
35549
|
+
return decryptedBlob;
|
|
35550
|
+
} catch (error) {
|
|
35551
|
+
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"))) {
|
|
35552
|
+
throw error;
|
|
35553
|
+
}
|
|
35554
|
+
throw new Error(
|
|
35555
|
+
`Failed to decrypt file: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
35556
|
+
);
|
|
35557
|
+
}
|
|
36034
35558
|
}
|
|
36035
35559
|
/**
|
|
36036
35560
|
* Retrieves all data files owned by a specific user address.
|
|
@@ -36615,6 +36139,7 @@ var DataController = class {
|
|
|
36615
36139
|
/**
|
|
36616
36140
|
* Uploads an encrypted file to storage and registers it on the blockchain.
|
|
36617
36141
|
*
|
|
36142
|
+
* @deprecated Use vana.data.upload() instead for the high-level API with automatic encryption
|
|
36618
36143
|
* @param encryptedFile - The encrypted file blob to upload
|
|
36619
36144
|
* @param filename - Optional filename for the upload
|
|
36620
36145
|
* @param providerName - Optional storage provider to use
|
|
@@ -36702,6 +36227,7 @@ var DataController = class {
|
|
|
36702
36227
|
/**
|
|
36703
36228
|
* Uploads an encrypted file to storage and registers it on the blockchain with a schema.
|
|
36704
36229
|
*
|
|
36230
|
+
* @deprecated Use vana.data.upload() instead for the high-level API with automatic encryption and schema validation
|
|
36705
36231
|
* @param encryptedFile - The encrypted file blob to upload
|
|
36706
36232
|
* @param schemaId - The schema ID to associate with the file
|
|
36707
36233
|
* @param filename - Optional filename for the upload
|
|
@@ -36780,81 +36306,6 @@ var DataController = class {
|
|
|
36780
36306
|
);
|
|
36781
36307
|
}
|
|
36782
36308
|
}
|
|
36783
|
-
/**
|
|
36784
|
-
* Decrypts a file that was encrypted using the Vana protocol.
|
|
36785
|
-
*
|
|
36786
|
-
* @param file - The UserFile object containing the file URL and metadata
|
|
36787
|
-
* @param encryptionSeed - Optional custom encryption seed (defaults to Vana standard)
|
|
36788
|
-
* @returns Promise resolving to the decrypted file as a Blob
|
|
36789
|
-
*
|
|
36790
|
-
* This method handles the complete flow of:
|
|
36791
|
-
* 1. Generating the encryption key from the user's wallet signature
|
|
36792
|
-
* 2. Fetching the encrypted file from the stored URL
|
|
36793
|
-
* 3. Decrypting the file using the canonical Vana decryption method
|
|
36794
|
-
*/
|
|
36795
|
-
async decryptFile(file, encryptionSeed = DEFAULT_ENCRYPTION_SEED) {
|
|
36796
|
-
try {
|
|
36797
|
-
const encryptionKey = await generateEncryptionKey(
|
|
36798
|
-
this.context.walletClient,
|
|
36799
|
-
encryptionSeed
|
|
36800
|
-
);
|
|
36801
|
-
const fetchUrl = this.convertToDownloadUrl(file.url);
|
|
36802
|
-
console.debug(
|
|
36803
|
-
`Fetching encrypted file from URL: ${fetchUrl} (original: ${file.url})`
|
|
36804
|
-
);
|
|
36805
|
-
const response = await fetch(fetchUrl);
|
|
36806
|
-
if (!response.ok) {
|
|
36807
|
-
if (response.status === 404) {
|
|
36808
|
-
throw new Error(
|
|
36809
|
-
"File not found. The encrypted file may have been moved or deleted."
|
|
36810
|
-
);
|
|
36811
|
-
} else if (response.status === 403) {
|
|
36812
|
-
throw new Error(
|
|
36813
|
-
"Access denied. You may not have permission to access this file."
|
|
36814
|
-
);
|
|
36815
|
-
} else {
|
|
36816
|
-
throw new Error(
|
|
36817
|
-
`Network error: ${response.status} ${response.statusText}`
|
|
36818
|
-
);
|
|
36819
|
-
}
|
|
36820
|
-
}
|
|
36821
|
-
const encryptedBlob = await response.blob();
|
|
36822
|
-
console.debug(
|
|
36823
|
-
`Retrieved blob of size: ${encryptedBlob.size} bytes, type: ${encryptedBlob.type}`
|
|
36824
|
-
);
|
|
36825
|
-
if (encryptedBlob.size === 0) {
|
|
36826
|
-
throw new Error("File is empty or could not be retrieved.");
|
|
36827
|
-
}
|
|
36828
|
-
const decryptedBlob = await decryptUserData(
|
|
36829
|
-
encryptedBlob,
|
|
36830
|
-
encryptionKey,
|
|
36831
|
-
this.context.platform
|
|
36832
|
-
);
|
|
36833
|
-
return decryptedBlob;
|
|
36834
|
-
} catch (error) {
|
|
36835
|
-
console.error("Failed to decrypt file:", error);
|
|
36836
|
-
if (error instanceof Error) {
|
|
36837
|
-
if (error.message.includes("Session key decryption failed") || error.message.includes("Error decrypting message")) {
|
|
36838
|
-
throw new Error(
|
|
36839
|
-
"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."
|
|
36840
|
-
);
|
|
36841
|
-
} else if (error.message.includes("Failed to fetch") || error.message.includes("Network error")) {
|
|
36842
|
-
throw new Error(
|
|
36843
|
-
"Network error: Cannot access the file URL. The file may be stored on a server that's not accessible or has CORS restrictions."
|
|
36844
|
-
);
|
|
36845
|
-
} else if (error.message.includes("File not found")) {
|
|
36846
|
-
throw new Error(
|
|
36847
|
-
"File not found: The encrypted file is no longer available at the stored URL."
|
|
36848
|
-
);
|
|
36849
|
-
} else if (error.message.includes("not a valid OpenPGP message") || error.message.includes("does not conform to a valid OpenPGP format")) {
|
|
36850
|
-
throw new Error(
|
|
36851
|
-
"Invalid file format: This file doesn't appear to be encrypted with the Vana protocol."
|
|
36852
|
-
);
|
|
36853
|
-
}
|
|
36854
|
-
}
|
|
36855
|
-
throw error;
|
|
36856
|
-
}
|
|
36857
|
-
}
|
|
36858
36309
|
/**
|
|
36859
36310
|
* Registers a file URL directly on the blockchain with a schema ID.
|
|
36860
36311
|
*
|
|
@@ -36915,26 +36366,6 @@ var DataController = class {
|
|
|
36915
36366
|
);
|
|
36916
36367
|
}
|
|
36917
36368
|
}
|
|
36918
|
-
/**
|
|
36919
|
-
* Converts IPFS and Google Drive URLs to direct download URLs for fetching.
|
|
36920
|
-
*
|
|
36921
|
-
* @param url - The URL to convert to a direct download URL
|
|
36922
|
-
* @returns The converted direct download URL or the original URL if not a special URL
|
|
36923
|
-
*/
|
|
36924
|
-
convertToDownloadUrl(url) {
|
|
36925
|
-
if (url.startsWith("ipfs://")) {
|
|
36926
|
-
const hash = url.replace("ipfs://", "");
|
|
36927
|
-
return `https://ipfs.io/ipfs/${hash}`;
|
|
36928
|
-
}
|
|
36929
|
-
if (url.includes("drive.google.com/file/d/")) {
|
|
36930
|
-
const fileIdMatch = url.match(/\/file\/d\/([a-zA-Z0-9-_]+)/);
|
|
36931
|
-
if (fileIdMatch) {
|
|
36932
|
-
const fileId = fileIdMatch[1];
|
|
36933
|
-
return `https://drive.google.com/uc?id=${fileId}&export=download`;
|
|
36934
|
-
}
|
|
36935
|
-
}
|
|
36936
|
-
return url;
|
|
36937
|
-
}
|
|
36938
36369
|
/**
|
|
36939
36370
|
* Gets the user's address from the wallet client.
|
|
36940
36371
|
*
|
|
@@ -37009,9 +36440,70 @@ var DataController = class {
|
|
|
37009
36440
|
);
|
|
37010
36441
|
}
|
|
37011
36442
|
}
|
|
36443
|
+
/**
|
|
36444
|
+
* Adds a file to the registry with permissions and schema.
|
|
36445
|
+
* This combines the functionality of addFileWithPermissions and schema validation.
|
|
36446
|
+
*
|
|
36447
|
+
* @param url - The URL of the file to register
|
|
36448
|
+
* @param ownerAddress - The address of the file owner
|
|
36449
|
+
* @param permissions - Array of permissions to grant (account and encrypted key)
|
|
36450
|
+
* @param schemaId - The schema ID to associate with the file (0 for no schema)
|
|
36451
|
+
* @returns Promise resolving to object with fileId and transactionHash
|
|
36452
|
+
*/
|
|
36453
|
+
async addFileWithPermissionsAndSchema(url, ownerAddress, permissions = [], schemaId = 0) {
|
|
36454
|
+
try {
|
|
36455
|
+
const chainId = this.context.walletClient.chain?.id;
|
|
36456
|
+
if (!chainId) {
|
|
36457
|
+
throw new Error("Chain ID not available");
|
|
36458
|
+
}
|
|
36459
|
+
const dataRegistryAddress = getContractAddress(chainId, "DataRegistry");
|
|
36460
|
+
const dataRegistryAbi = getAbi("DataRegistry");
|
|
36461
|
+
const txHash = await this.context.walletClient.writeContract({
|
|
36462
|
+
address: dataRegistryAddress,
|
|
36463
|
+
abi: dataRegistryAbi,
|
|
36464
|
+
functionName: "addFileWithPermissionsAndSchema",
|
|
36465
|
+
args: [url, ownerAddress, permissions, BigInt(schemaId)],
|
|
36466
|
+
account: this.context.walletClient.account || ownerAddress,
|
|
36467
|
+
chain: this.context.walletClient.chain || null
|
|
36468
|
+
});
|
|
36469
|
+
const receipt = await this.context.publicClient.waitForTransactionReceipt(
|
|
36470
|
+
{
|
|
36471
|
+
hash: txHash,
|
|
36472
|
+
timeout: 3e4
|
|
36473
|
+
// 30 seconds timeout
|
|
36474
|
+
}
|
|
36475
|
+
);
|
|
36476
|
+
let fileId = 0;
|
|
36477
|
+
for (const log of receipt.logs) {
|
|
36478
|
+
try {
|
|
36479
|
+
const decoded = decodeEventLog({
|
|
36480
|
+
abi: dataRegistryAbi,
|
|
36481
|
+
data: log.data,
|
|
36482
|
+
topics: log.topics
|
|
36483
|
+
});
|
|
36484
|
+
if (decoded.eventName === "FileAdded") {
|
|
36485
|
+
fileId = Number(decoded.args.fileId);
|
|
36486
|
+
break;
|
|
36487
|
+
}
|
|
36488
|
+
} catch {
|
|
36489
|
+
continue;
|
|
36490
|
+
}
|
|
36491
|
+
}
|
|
36492
|
+
return {
|
|
36493
|
+
fileId,
|
|
36494
|
+
transactionHash: txHash
|
|
36495
|
+
};
|
|
36496
|
+
} catch (error) {
|
|
36497
|
+
console.error("Failed to add file with permissions and schema:", error);
|
|
36498
|
+
throw new Error(
|
|
36499
|
+
`Failed to add file with permissions and schema: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
36500
|
+
);
|
|
36501
|
+
}
|
|
36502
|
+
}
|
|
37012
36503
|
/**
|
|
37013
36504
|
* Adds a new schema to the DataRefinerRegistry.
|
|
37014
36505
|
*
|
|
36506
|
+
* @deprecated Use vana.schemas.create() instead for the high-level API with automatic IPFS upload
|
|
37015
36507
|
* @param params - Schema parameters including name, type, and definition URL
|
|
37016
36508
|
* @returns Promise resolving to the new schema ID and transaction hash
|
|
37017
36509
|
*/
|
|
@@ -37070,6 +36562,7 @@ var DataController = class {
|
|
|
37070
36562
|
/**
|
|
37071
36563
|
* Retrieves a schema by its ID.
|
|
37072
36564
|
*
|
|
36565
|
+
* @deprecated Use vana.schemas.get() instead
|
|
37073
36566
|
* @param schemaId - The schema ID to retrieve
|
|
37074
36567
|
* @returns Promise resolving to the schema information
|
|
37075
36568
|
*/
|
|
@@ -37095,11 +36588,15 @@ var DataController = class {
|
|
|
37095
36588
|
if (!schemaData) {
|
|
37096
36589
|
throw new Error("Schema not found");
|
|
37097
36590
|
}
|
|
36591
|
+
const schemaObj = schemaData;
|
|
36592
|
+
if (!schemaObj.name || !schemaObj.typ || !schemaObj.definitionUrl) {
|
|
36593
|
+
throw new Error("Incomplete schema data");
|
|
36594
|
+
}
|
|
37098
36595
|
return {
|
|
37099
36596
|
id: schemaId,
|
|
37100
|
-
name:
|
|
37101
|
-
type:
|
|
37102
|
-
definitionUrl:
|
|
36597
|
+
name: schemaObj.name,
|
|
36598
|
+
type: schemaObj.typ,
|
|
36599
|
+
definitionUrl: schemaObj.definitionUrl
|
|
37103
36600
|
};
|
|
37104
36601
|
} catch (error) {
|
|
37105
36602
|
console.error("Failed to get schema:", error);
|
|
@@ -37111,6 +36608,7 @@ var DataController = class {
|
|
|
37111
36608
|
/**
|
|
37112
36609
|
* Gets the total number of schemas in the registry.
|
|
37113
36610
|
*
|
|
36611
|
+
* @deprecated Use vana.schemas.count() instead
|
|
37114
36612
|
* @returns Promise resolving to the total schema count
|
|
37115
36613
|
*/
|
|
37116
36614
|
async getSchemasCount() {
|
|
@@ -37156,7 +36654,7 @@ var DataController = class {
|
|
|
37156
36654
|
const txHash = await this.context.walletClient.writeContract({
|
|
37157
36655
|
address: dataRefinerRegistryAddress,
|
|
37158
36656
|
abi: dataRefinerRegistryAbi,
|
|
37159
|
-
functionName: "
|
|
36657
|
+
functionName: "addRefinerWithSchemaId",
|
|
37160
36658
|
args: [
|
|
37161
36659
|
BigInt(params.dlpId),
|
|
37162
36660
|
params.name,
|
|
@@ -37361,7 +36859,7 @@ var DataController = class {
|
|
|
37361
36859
|
this.context.walletClient,
|
|
37362
36860
|
DEFAULT_ENCRYPTION_SEED
|
|
37363
36861
|
);
|
|
37364
|
-
const encryptedData = await
|
|
36862
|
+
const encryptedData = await encryptBlobWithSignedKey(
|
|
37365
36863
|
data,
|
|
37366
36864
|
userEncryptionKey,
|
|
37367
36865
|
this.context.platform
|
|
@@ -37495,25 +36993,6 @@ var DataController = class {
|
|
|
37495
36993
|
);
|
|
37496
36994
|
}
|
|
37497
36995
|
}
|
|
37498
|
-
/**
|
|
37499
|
-
* Gets the trusted server public key for a given server address.
|
|
37500
|
-
* This method reads from the permissions contract to find servers and their public keys.
|
|
37501
|
-
*
|
|
37502
|
-
* @param serverAddress - The address of the trusted server
|
|
37503
|
-
* @returns Promise resolving to the server's public key
|
|
37504
|
-
*/
|
|
37505
|
-
async getTrustedServerPublicKey(serverAddress) {
|
|
37506
|
-
try {
|
|
37507
|
-
return await this.serverController.getTrustedServerPublicKey(
|
|
37508
|
-
serverAddress
|
|
37509
|
-
);
|
|
37510
|
-
} catch (error) {
|
|
37511
|
-
console.error("Failed to get trusted server public key:", error);
|
|
37512
|
-
throw new Error(
|
|
37513
|
-
`Failed to get trusted server public key: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
37514
|
-
);
|
|
37515
|
-
}
|
|
37516
|
-
}
|
|
37517
36996
|
/**
|
|
37518
36997
|
* Decrypts a file that the user has permission to access using their private key.
|
|
37519
36998
|
*
|
|
@@ -37544,12 +37023,13 @@ var DataController = class {
|
|
|
37544
37023
|
privateKey,
|
|
37545
37024
|
this.context.platform
|
|
37546
37025
|
);
|
|
37547
|
-
|
|
37548
|
-
if (
|
|
37549
|
-
|
|
37026
|
+
let encryptedData;
|
|
37027
|
+
if (file.url.startsWith("ipfs://")) {
|
|
37028
|
+
encryptedData = await this.fetchFromIPFS(file.url);
|
|
37029
|
+
} else {
|
|
37030
|
+
encryptedData = await this.fetch(file.url);
|
|
37550
37031
|
}
|
|
37551
|
-
const
|
|
37552
|
-
const decryptedData = await decryptUserData(
|
|
37032
|
+
const decryptedData = await decryptBlobWithSignedKey(
|
|
37553
37033
|
encryptedData,
|
|
37554
37034
|
userEncryptionKey,
|
|
37555
37035
|
this.context.platform
|
|
@@ -37562,6 +37042,162 @@ var DataController = class {
|
|
|
37562
37042
|
);
|
|
37563
37043
|
}
|
|
37564
37044
|
}
|
|
37045
|
+
/**
|
|
37046
|
+
* Simple network-agnostic fetch utility for retrieving file content.
|
|
37047
|
+
*
|
|
37048
|
+
* @remarks
|
|
37049
|
+
* This is a thin wrapper around the global fetch API that returns the response as a Blob.
|
|
37050
|
+
* It provides a consistent interface for fetching encrypted content before decryption.
|
|
37051
|
+
* For IPFS URLs, consider using fetchFromIPFS for better reliability.
|
|
37052
|
+
*
|
|
37053
|
+
* @param url - The URL to fetch content from
|
|
37054
|
+
* @returns Promise resolving to the fetched content as a Blob
|
|
37055
|
+
* @throws {Error} When the fetch fails or returns a non-ok response
|
|
37056
|
+
*
|
|
37057
|
+
* @example
|
|
37058
|
+
* ```typescript
|
|
37059
|
+
* // Fetch and decrypt a file
|
|
37060
|
+
* const encryptionKey = await generateEncryptionKey(walletClient);
|
|
37061
|
+
* const encryptedBlob = await vana.data.fetch(file.url);
|
|
37062
|
+
* const decryptedBlob = await decryptBlob(encryptedBlob, encryptionKey, platform);
|
|
37063
|
+
*
|
|
37064
|
+
* // With custom headers for authentication
|
|
37065
|
+
* const response = await fetch(file.url, {
|
|
37066
|
+
* headers: { 'Authorization': 'Bearer token' }
|
|
37067
|
+
* });
|
|
37068
|
+
* const encryptedBlob = await response.blob();
|
|
37069
|
+
* ```
|
|
37070
|
+
*/
|
|
37071
|
+
async fetch(url) {
|
|
37072
|
+
try {
|
|
37073
|
+
const response = await fetch(url);
|
|
37074
|
+
if (!response.ok) {
|
|
37075
|
+
throw new Error(
|
|
37076
|
+
`HTTP error! status: ${response.status} ${response.statusText}`
|
|
37077
|
+
);
|
|
37078
|
+
}
|
|
37079
|
+
const blob = await response.blob();
|
|
37080
|
+
if (blob.size === 0) {
|
|
37081
|
+
throw new Error("Empty response");
|
|
37082
|
+
}
|
|
37083
|
+
return blob;
|
|
37084
|
+
} catch (error) {
|
|
37085
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
37086
|
+
throw new Error(
|
|
37087
|
+
`Network error: Failed to fetch from ${url}. The URL may be invalid or the server may not be accessible.`
|
|
37088
|
+
);
|
|
37089
|
+
}
|
|
37090
|
+
throw error;
|
|
37091
|
+
}
|
|
37092
|
+
}
|
|
37093
|
+
/**
|
|
37094
|
+
* Specialized IPFS fetcher with gateway fallback mechanism.
|
|
37095
|
+
*
|
|
37096
|
+
* @remarks
|
|
37097
|
+
* This method provides robust IPFS content fetching by trying multiple gateways
|
|
37098
|
+
* in sequence until one succeeds. It supports both ipfs:// URLs and raw CIDs.
|
|
37099
|
+
*
|
|
37100
|
+
* The default gateway list includes public gateways, but you should provide
|
|
37101
|
+
* your own gateways for production use to ensure reliability and privacy.
|
|
37102
|
+
*
|
|
37103
|
+
* @param url - The IPFS URL (ipfs://...) or CID to fetch
|
|
37104
|
+
* @param options - Optional configuration
|
|
37105
|
+
* @param options.gateways - Array of IPFS gateway URLs to try (must end with /)
|
|
37106
|
+
* @returns Promise resolving to the fetched content as a Blob
|
|
37107
|
+
* @throws {Error} When all gateways fail to fetch the content
|
|
37108
|
+
*
|
|
37109
|
+
* @example
|
|
37110
|
+
* ```typescript
|
|
37111
|
+
* // Fetch from IPFS with custom gateways
|
|
37112
|
+
* const encryptedBlob = await vana.data.fetchFromIPFS(file.url, {
|
|
37113
|
+
* gateways: [
|
|
37114
|
+
* 'https://my-private-gateway.com/ipfs/',
|
|
37115
|
+
* 'https://dweb.link/ipfs/',
|
|
37116
|
+
* 'https://ipfs.io/ipfs/'
|
|
37117
|
+
* ]
|
|
37118
|
+
* });
|
|
37119
|
+
*
|
|
37120
|
+
* // Decrypt the fetched content
|
|
37121
|
+
* const encryptionKey = await generateEncryptionKey(walletClient);
|
|
37122
|
+
* const decryptedBlob = await decryptBlob(encryptedBlob, encryptionKey, platform);
|
|
37123
|
+
*
|
|
37124
|
+
* // With raw CID
|
|
37125
|
+
* const blob = await vana.data.fetchFromIPFS('QmXxx...', {
|
|
37126
|
+
* gateways: ['https://ipfs.io/ipfs/']
|
|
37127
|
+
* });
|
|
37128
|
+
* ```
|
|
37129
|
+
*/
|
|
37130
|
+
async fetchFromIPFS(url, options) {
|
|
37131
|
+
const defaultGateways = [
|
|
37132
|
+
"https://dweb.link/ipfs/",
|
|
37133
|
+
"https://ipfs.io/ipfs/"
|
|
37134
|
+
];
|
|
37135
|
+
const gateways = options?.gateways || this.context.ipfsGateways || defaultGateways;
|
|
37136
|
+
let cid;
|
|
37137
|
+
if (url.startsWith("ipfs://")) {
|
|
37138
|
+
cid = url.replace("ipfs://", "");
|
|
37139
|
+
} else if (url.startsWith("Qm") || url.startsWith("bafy")) {
|
|
37140
|
+
cid = url;
|
|
37141
|
+
} else {
|
|
37142
|
+
throw new Error(
|
|
37143
|
+
`Invalid IPFS URL format. Expected ipfs://... or a raw CID, got: ${url}`
|
|
37144
|
+
);
|
|
37145
|
+
}
|
|
37146
|
+
const errors = [];
|
|
37147
|
+
for (let i = 0; i < gateways.length; i++) {
|
|
37148
|
+
const gateway = gateways[i];
|
|
37149
|
+
const isLastGateway = i === gateways.length - 1;
|
|
37150
|
+
const gatewayUrl = gateway.endsWith("/") ? `${gateway}${cid}` : `${gateway}/${cid}`;
|
|
37151
|
+
try {
|
|
37152
|
+
console.debug(`Trying IPFS gateway: ${gatewayUrl}`);
|
|
37153
|
+
const response = await fetch(gatewayUrl);
|
|
37154
|
+
if (response.ok) {
|
|
37155
|
+
const blob = await response.blob();
|
|
37156
|
+
if (blob.size > 0) {
|
|
37157
|
+
console.debug(`Successfully fetched from gateway: ${gateway}`);
|
|
37158
|
+
return blob;
|
|
37159
|
+
} else {
|
|
37160
|
+
if (isLastGateway) {
|
|
37161
|
+
throw new Error("Empty response");
|
|
37162
|
+
}
|
|
37163
|
+
errors.push({
|
|
37164
|
+
gateway,
|
|
37165
|
+
error: "Empty response"
|
|
37166
|
+
});
|
|
37167
|
+
}
|
|
37168
|
+
} else {
|
|
37169
|
+
if (isLastGateway) {
|
|
37170
|
+
if (response.status === 403) {
|
|
37171
|
+
throw new Error(`HTTP error! status: 403 ${response.statusText}`);
|
|
37172
|
+
} else if (response.status === 404) {
|
|
37173
|
+
throw new Error(`HTTP error! status: 404 ${response.statusText}`);
|
|
37174
|
+
} else {
|
|
37175
|
+
throw new Error(
|
|
37176
|
+
`HTTP error! status: ${response.status} ${response.statusText}`
|
|
37177
|
+
);
|
|
37178
|
+
}
|
|
37179
|
+
}
|
|
37180
|
+
errors.push({
|
|
37181
|
+
gateway,
|
|
37182
|
+
error: `HTTP ${response.status} ${response.statusText}`
|
|
37183
|
+
});
|
|
37184
|
+
}
|
|
37185
|
+
} catch (error) {
|
|
37186
|
+
if (isLastGateway && error instanceof Error && (error.message.includes("Empty response") || error.message.includes("HTTP error!"))) {
|
|
37187
|
+
throw error;
|
|
37188
|
+
}
|
|
37189
|
+
errors.push({
|
|
37190
|
+
gateway,
|
|
37191
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
37192
|
+
});
|
|
37193
|
+
}
|
|
37194
|
+
}
|
|
37195
|
+
const errorDetails = errors.map((e) => `${e.gateway}: ${e.error}`).join("\n ");
|
|
37196
|
+
throw new Error(
|
|
37197
|
+
`Failed to fetch IPFS content ${cid} from all gateways:
|
|
37198
|
+
${errorDetails}`
|
|
37199
|
+
);
|
|
37200
|
+
}
|
|
37565
37201
|
/**
|
|
37566
37202
|
* Validates a data schema against the Vana meta-schema.
|
|
37567
37203
|
*
|
|
@@ -37683,9 +37319,589 @@ var DataController = class {
|
|
|
37683
37319
|
}
|
|
37684
37320
|
};
|
|
37685
37321
|
|
|
37322
|
+
// src/controllers/schemas.ts
|
|
37323
|
+
import { getContract as getContract2, decodeEventLog as decodeEventLog2 } from "viem";
|
|
37324
|
+
var SchemaController = class {
|
|
37325
|
+
constructor(context) {
|
|
37326
|
+
this.context = context;
|
|
37327
|
+
}
|
|
37328
|
+
/**
|
|
37329
|
+
* Creates a new schema with automatic validation and IPFS upload.
|
|
37330
|
+
*
|
|
37331
|
+
* @remarks
|
|
37332
|
+
* This is the primary method for creating schemas on the Vana network. It handles
|
|
37333
|
+
* the complete workflow including schema validation, IPFS upload, and blockchain
|
|
37334
|
+
* registration. The schema definition is stored unencrypted on IPFS to enable
|
|
37335
|
+
* public access and reusability.
|
|
37336
|
+
*
|
|
37337
|
+
* The method automatically:
|
|
37338
|
+
* - Validates the schema definition against the Vana metaschema
|
|
37339
|
+
* - Uploads the definition to IPFS to generate a permanent URL
|
|
37340
|
+
* - Registers the schema on the blockchain with the generated URL
|
|
37341
|
+
*
|
|
37342
|
+
* @param params - Schema creation parameters including name, type, and definition
|
|
37343
|
+
* @returns Promise resolving to creation results with schema ID and transaction hash
|
|
37344
|
+
* @throws {SchemaValidationError} When the schema definition is invalid
|
|
37345
|
+
* @throws {Error} When IPFS upload or blockchain registration fails
|
|
37346
|
+
* @example
|
|
37347
|
+
* ```typescript
|
|
37348
|
+
* // Create a JSON schema for user profiles
|
|
37349
|
+
* const result = await vana.schemas.create({
|
|
37350
|
+
* name: "User Profile",
|
|
37351
|
+
* type: "personal",
|
|
37352
|
+
* definition: {
|
|
37353
|
+
* type: "object",
|
|
37354
|
+
* properties: {
|
|
37355
|
+
* name: { type: "string" },
|
|
37356
|
+
* age: { type: "number", minimum: 0 }
|
|
37357
|
+
* },
|
|
37358
|
+
* required: ["name"]
|
|
37359
|
+
* }
|
|
37360
|
+
* });
|
|
37361
|
+
*
|
|
37362
|
+
* console.log(`Schema created with ID: ${result.schemaId}`);
|
|
37363
|
+
* ```
|
|
37364
|
+
*/
|
|
37365
|
+
async create(params) {
|
|
37366
|
+
const { name, type, definition } = params;
|
|
37367
|
+
try {
|
|
37368
|
+
let schemaDefinition;
|
|
37369
|
+
if (typeof definition === "string") {
|
|
37370
|
+
try {
|
|
37371
|
+
schemaDefinition = JSON.parse(definition);
|
|
37372
|
+
} catch {
|
|
37373
|
+
throw new SchemaValidationError(
|
|
37374
|
+
"Invalid JSON in schema definition",
|
|
37375
|
+
[]
|
|
37376
|
+
);
|
|
37377
|
+
}
|
|
37378
|
+
} else {
|
|
37379
|
+
schemaDefinition = definition;
|
|
37380
|
+
}
|
|
37381
|
+
const dataSchema = {
|
|
37382
|
+
name,
|
|
37383
|
+
version: "1.0.0",
|
|
37384
|
+
dialect: "json",
|
|
37385
|
+
schema: schemaDefinition
|
|
37386
|
+
};
|
|
37387
|
+
validateDataSchema(dataSchema);
|
|
37388
|
+
if (!this.context.storageManager) {
|
|
37389
|
+
if (this.context.validateStorageRequired) {
|
|
37390
|
+
this.context.validateStorageRequired();
|
|
37391
|
+
throw new Error("Storage validation failed");
|
|
37392
|
+
} else {
|
|
37393
|
+
throw new Error(
|
|
37394
|
+
"Storage manager not configured. Please provide storage providers in VanaConfig."
|
|
37395
|
+
);
|
|
37396
|
+
}
|
|
37397
|
+
}
|
|
37398
|
+
const schemaBlob = new Blob([JSON.stringify(schemaDefinition)], {
|
|
37399
|
+
type: "application/json"
|
|
37400
|
+
});
|
|
37401
|
+
const uploadResult = await this.context.storageManager.upload(
|
|
37402
|
+
schemaBlob,
|
|
37403
|
+
`${name.replace(/[^a-zA-Z0-9]/g, "_")}.json`,
|
|
37404
|
+
"ipfs"
|
|
37405
|
+
// Use IPFS for public schema storage
|
|
37406
|
+
);
|
|
37407
|
+
const chainId = this.context.walletClient.chain?.id;
|
|
37408
|
+
if (!chainId) {
|
|
37409
|
+
throw new Error("Chain ID not available");
|
|
37410
|
+
}
|
|
37411
|
+
const dataRefinerRegistryAddress = getContractAddress(
|
|
37412
|
+
chainId,
|
|
37413
|
+
"DataRefinerRegistry"
|
|
37414
|
+
);
|
|
37415
|
+
const dataRefinerRegistryAbi = getAbi("DataRefinerRegistry");
|
|
37416
|
+
const userAddress = await this.getUserAddress();
|
|
37417
|
+
const txHash = await this.context.walletClient.writeContract({
|
|
37418
|
+
address: dataRefinerRegistryAddress,
|
|
37419
|
+
abi: dataRefinerRegistryAbi,
|
|
37420
|
+
functionName: "addSchema",
|
|
37421
|
+
args: [name, type, uploadResult.url],
|
|
37422
|
+
account: this.context.walletClient.account || userAddress,
|
|
37423
|
+
chain: this.context.walletClient.chain || null
|
|
37424
|
+
});
|
|
37425
|
+
const receipt = await this.context.publicClient.waitForTransactionReceipt(
|
|
37426
|
+
{
|
|
37427
|
+
hash: txHash,
|
|
37428
|
+
timeout: 3e4
|
|
37429
|
+
// 30 seconds timeout
|
|
37430
|
+
}
|
|
37431
|
+
);
|
|
37432
|
+
let schemaId = 0;
|
|
37433
|
+
for (const log of receipt.logs) {
|
|
37434
|
+
try {
|
|
37435
|
+
const decoded = decodeEventLog2({
|
|
37436
|
+
abi: dataRefinerRegistryAbi,
|
|
37437
|
+
data: log.data,
|
|
37438
|
+
topics: log.topics
|
|
37439
|
+
});
|
|
37440
|
+
if (decoded.eventName === "SchemaAdded") {
|
|
37441
|
+
schemaId = Number(decoded.args.schemaId);
|
|
37442
|
+
break;
|
|
37443
|
+
}
|
|
37444
|
+
} catch {
|
|
37445
|
+
}
|
|
37446
|
+
}
|
|
37447
|
+
return {
|
|
37448
|
+
schemaId,
|
|
37449
|
+
definitionUrl: uploadResult.url,
|
|
37450
|
+
transactionHash: txHash
|
|
37451
|
+
};
|
|
37452
|
+
} catch (error) {
|
|
37453
|
+
if (error instanceof SchemaValidationError) {
|
|
37454
|
+
throw error;
|
|
37455
|
+
}
|
|
37456
|
+
throw new Error(
|
|
37457
|
+
`Schema creation failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
37458
|
+
);
|
|
37459
|
+
}
|
|
37460
|
+
}
|
|
37461
|
+
/**
|
|
37462
|
+
* Retrieves a schema by its ID.
|
|
37463
|
+
*
|
|
37464
|
+
* @param schemaId - The ID of the schema to retrieve
|
|
37465
|
+
* @returns Promise resolving to the schema object
|
|
37466
|
+
* @throws {Error} When the schema is not found or chain is unavailable
|
|
37467
|
+
* @example
|
|
37468
|
+
* ```typescript
|
|
37469
|
+
* const schema = await vana.schemas.get(1);
|
|
37470
|
+
* console.log(`Schema: ${schema.name} (${schema.type})`);
|
|
37471
|
+
* ```
|
|
37472
|
+
*/
|
|
37473
|
+
async get(schemaId) {
|
|
37474
|
+
try {
|
|
37475
|
+
const chainId = this.context.walletClient.chain?.id;
|
|
37476
|
+
if (!chainId) {
|
|
37477
|
+
throw new Error("Chain ID not available");
|
|
37478
|
+
}
|
|
37479
|
+
const dataRefinerRegistryAddress = getContractAddress(
|
|
37480
|
+
chainId,
|
|
37481
|
+
"DataRefinerRegistry"
|
|
37482
|
+
);
|
|
37483
|
+
const dataRefinerRegistryAbi = getAbi("DataRefinerRegistry");
|
|
37484
|
+
const dataRefinerRegistry = getContract2({
|
|
37485
|
+
address: dataRefinerRegistryAddress,
|
|
37486
|
+
abi: dataRefinerRegistryAbi,
|
|
37487
|
+
client: this.context.publicClient
|
|
37488
|
+
});
|
|
37489
|
+
const schemaData = await dataRefinerRegistry.read.schemas([
|
|
37490
|
+
BigInt(schemaId)
|
|
37491
|
+
]);
|
|
37492
|
+
if (!schemaData) {
|
|
37493
|
+
throw new Error(`Schema with ID ${schemaId} not found`);
|
|
37494
|
+
}
|
|
37495
|
+
const schemaObj = schemaData;
|
|
37496
|
+
if (!schemaObj.name || !schemaObj.typ || !schemaObj.definitionUrl) {
|
|
37497
|
+
throw new Error("Incomplete schema data");
|
|
37498
|
+
}
|
|
37499
|
+
return {
|
|
37500
|
+
id: schemaId,
|
|
37501
|
+
name: schemaObj.name,
|
|
37502
|
+
type: schemaObj.typ,
|
|
37503
|
+
definitionUrl: schemaObj.definitionUrl
|
|
37504
|
+
};
|
|
37505
|
+
} catch (error) {
|
|
37506
|
+
throw new Error(
|
|
37507
|
+
`Failed to get schema: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
37508
|
+
);
|
|
37509
|
+
}
|
|
37510
|
+
}
|
|
37511
|
+
/**
|
|
37512
|
+
* Gets the total number of schemas registered on the network.
|
|
37513
|
+
*
|
|
37514
|
+
* @returns Promise resolving to the total schema count
|
|
37515
|
+
* @throws {Error} When the count cannot be retrieved
|
|
37516
|
+
* @example
|
|
37517
|
+
* ```typescript
|
|
37518
|
+
* const count = await vana.schemas.count();
|
|
37519
|
+
* console.log(`Total schemas: ${count}`);
|
|
37520
|
+
* ```
|
|
37521
|
+
*/
|
|
37522
|
+
async count() {
|
|
37523
|
+
try {
|
|
37524
|
+
const chainId = this.context.walletClient.chain?.id;
|
|
37525
|
+
if (!chainId) {
|
|
37526
|
+
throw new Error("Chain ID not available");
|
|
37527
|
+
}
|
|
37528
|
+
const dataRefinerRegistryAddress = getContractAddress(
|
|
37529
|
+
chainId,
|
|
37530
|
+
"DataRefinerRegistry"
|
|
37531
|
+
);
|
|
37532
|
+
const dataRefinerRegistryAbi = getAbi("DataRefinerRegistry");
|
|
37533
|
+
const dataRefinerRegistry = getContract2({
|
|
37534
|
+
address: dataRefinerRegistryAddress,
|
|
37535
|
+
abi: dataRefinerRegistryAbi,
|
|
37536
|
+
client: this.context.publicClient
|
|
37537
|
+
});
|
|
37538
|
+
const count = await dataRefinerRegistry.read.schemasCount();
|
|
37539
|
+
return Number(count);
|
|
37540
|
+
} catch (error) {
|
|
37541
|
+
throw new Error(
|
|
37542
|
+
`Failed to get schemas count: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
37543
|
+
);
|
|
37544
|
+
}
|
|
37545
|
+
}
|
|
37546
|
+
/**
|
|
37547
|
+
* Lists all schemas with pagination.
|
|
37548
|
+
*
|
|
37549
|
+
* @param options - Optional parameters for listing schemas
|
|
37550
|
+
* @param options.limit - Maximum number of schemas to return
|
|
37551
|
+
* @param options.offset - Number of schemas to skip
|
|
37552
|
+
* @returns Promise resolving to an array of schemas
|
|
37553
|
+
* @example
|
|
37554
|
+
* ```typescript
|
|
37555
|
+
* // Get all schemas
|
|
37556
|
+
* const schemas = await vana.schemas.list();
|
|
37557
|
+
*
|
|
37558
|
+
* // Get schemas with pagination
|
|
37559
|
+
* const schemas = await vana.schemas.list({ limit: 10, offset: 0 });
|
|
37560
|
+
* ```
|
|
37561
|
+
*/
|
|
37562
|
+
async list(options = {}) {
|
|
37563
|
+
const { limit = 100, offset = 0 } = options;
|
|
37564
|
+
try {
|
|
37565
|
+
const totalCount = await this.count();
|
|
37566
|
+
const schemas = [];
|
|
37567
|
+
const start = offset;
|
|
37568
|
+
const end = Math.min(start + limit, totalCount);
|
|
37569
|
+
for (let i = start; i < end; i++) {
|
|
37570
|
+
try {
|
|
37571
|
+
const schema = await this.get(i + 1);
|
|
37572
|
+
schemas.push(schema);
|
|
37573
|
+
} catch (error) {
|
|
37574
|
+
console.warn(`Failed to retrieve schema ${i + 1}:`, error);
|
|
37575
|
+
}
|
|
37576
|
+
}
|
|
37577
|
+
return schemas;
|
|
37578
|
+
} catch (error) {
|
|
37579
|
+
throw new Error(
|
|
37580
|
+
`Failed to list schemas: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
37581
|
+
);
|
|
37582
|
+
}
|
|
37583
|
+
}
|
|
37584
|
+
/**
|
|
37585
|
+
* Adds a schema using the legacy method (low-level API).
|
|
37586
|
+
*
|
|
37587
|
+
* @deprecated Use create() instead for the high-level API with automatic IPFS upload
|
|
37588
|
+
* @param params - Schema parameters including pre-generated definition URL
|
|
37589
|
+
* @returns Promise resolving to the add schema result
|
|
37590
|
+
*/
|
|
37591
|
+
async addSchema(params) {
|
|
37592
|
+
try {
|
|
37593
|
+
const chainId = this.context.walletClient.chain?.id;
|
|
37594
|
+
if (!chainId) {
|
|
37595
|
+
throw new Error("Chain ID not available");
|
|
37596
|
+
}
|
|
37597
|
+
const dataRefinerRegistryAddress = getContractAddress(
|
|
37598
|
+
chainId,
|
|
37599
|
+
"DataRefinerRegistry"
|
|
37600
|
+
);
|
|
37601
|
+
const dataRefinerRegistryAbi = getAbi("DataRefinerRegistry");
|
|
37602
|
+
const userAddress = await this.getUserAddress();
|
|
37603
|
+
const txHash = await this.context.walletClient.writeContract({
|
|
37604
|
+
address: dataRefinerRegistryAddress,
|
|
37605
|
+
abi: dataRefinerRegistryAbi,
|
|
37606
|
+
functionName: "addSchema",
|
|
37607
|
+
args: [params.name, params.type, params.definitionUrl],
|
|
37608
|
+
account: this.context.walletClient.account || userAddress,
|
|
37609
|
+
chain: this.context.walletClient.chain || null
|
|
37610
|
+
});
|
|
37611
|
+
return {
|
|
37612
|
+
schemaId: 0,
|
|
37613
|
+
// TODO: Parse from transaction receipt
|
|
37614
|
+
transactionHash: txHash
|
|
37615
|
+
};
|
|
37616
|
+
} catch (error) {
|
|
37617
|
+
throw new Error(
|
|
37618
|
+
`Failed to add schema: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
37619
|
+
);
|
|
37620
|
+
}
|
|
37621
|
+
}
|
|
37622
|
+
/**
|
|
37623
|
+
* Gets the user's wallet address.
|
|
37624
|
+
*
|
|
37625
|
+
* @private
|
|
37626
|
+
* @returns Promise resolving to the user's address
|
|
37627
|
+
*/
|
|
37628
|
+
async getUserAddress() {
|
|
37629
|
+
if (!this.context.walletClient.account) {
|
|
37630
|
+
throw new Error("No wallet account connected");
|
|
37631
|
+
}
|
|
37632
|
+
if (typeof this.context.walletClient.account === "string") {
|
|
37633
|
+
return this.context.walletClient.account;
|
|
37634
|
+
}
|
|
37635
|
+
if (typeof this.context.walletClient.account === "object" && this.context.walletClient.account.address) {
|
|
37636
|
+
return this.context.walletClient.account.address;
|
|
37637
|
+
}
|
|
37638
|
+
throw new Error("Unable to determine wallet address");
|
|
37639
|
+
}
|
|
37640
|
+
};
|
|
37641
|
+
|
|
37642
|
+
// src/controllers/server.ts
|
|
37643
|
+
var ServerController = class {
|
|
37644
|
+
constructor(context) {
|
|
37645
|
+
this.context = context;
|
|
37646
|
+
__publicField(this, "PERSONAL_SERVER_BASE_URL", process.env.NEXT_PUBLIC_PERSONAL_SERVER_BASE_URL);
|
|
37647
|
+
}
|
|
37648
|
+
async getIdentity(request) {
|
|
37649
|
+
try {
|
|
37650
|
+
const response = await fetch(
|
|
37651
|
+
`${this.PERSONAL_SERVER_BASE_URL}/identity?address=${request.userAddress}`,
|
|
37652
|
+
{
|
|
37653
|
+
method: "GET",
|
|
37654
|
+
headers: {
|
|
37655
|
+
"Content-Type": "application/json"
|
|
37656
|
+
}
|
|
37657
|
+
}
|
|
37658
|
+
);
|
|
37659
|
+
console.debug("\u{1F50D} Debug - getIdentity response", response);
|
|
37660
|
+
if (!response.ok) {
|
|
37661
|
+
const errorText = await response.text();
|
|
37662
|
+
throw new NetworkError(
|
|
37663
|
+
`Local identity API request failed: ${response.status} ${response.statusText} - ${errorText}`
|
|
37664
|
+
);
|
|
37665
|
+
}
|
|
37666
|
+
const serverResponse = await response.json();
|
|
37667
|
+
return {
|
|
37668
|
+
kind: serverResponse.personal_server.kind,
|
|
37669
|
+
address: serverResponse.personal_server.address,
|
|
37670
|
+
public_key: serverResponse.personal_server.public_key,
|
|
37671
|
+
base_url: this.PERSONAL_SERVER_BASE_URL || "",
|
|
37672
|
+
name: "Hosted Vana Server"
|
|
37673
|
+
};
|
|
37674
|
+
} catch (error) {
|
|
37675
|
+
if (error instanceof NetworkError || error instanceof PersonalServerError) {
|
|
37676
|
+
throw error;
|
|
37677
|
+
}
|
|
37678
|
+
throw new PersonalServerError(
|
|
37679
|
+
`Failed to get personal server identity: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
37680
|
+
);
|
|
37681
|
+
}
|
|
37682
|
+
}
|
|
37683
|
+
/**
|
|
37684
|
+
* Creates an operation via the personal server API.
|
|
37685
|
+
*
|
|
37686
|
+
* @remarks
|
|
37687
|
+
* This method submits a computation request to the personal server API.
|
|
37688
|
+
* The response includes the operation ID.
|
|
37689
|
+
* @param params - The request parameters object
|
|
37690
|
+
* @param params.permissionId - The permission ID authorizing this operation
|
|
37691
|
+
* @returns A Promise that resolves to an operation response with status and control URLs
|
|
37692
|
+
* @throws {PersonalServerError} When server request fails or parameters are invalid
|
|
37693
|
+
* @throws {NetworkError} When personal server API communication fails
|
|
37694
|
+
* @example
|
|
37695
|
+
* ```typescript
|
|
37696
|
+
* const response = await vana.server.createOperation({
|
|
37697
|
+
* permissionId: 123,
|
|
37698
|
+
* });
|
|
37699
|
+
*
|
|
37700
|
+
* console.log(`Operation created: ${response.id}`);
|
|
37701
|
+
* ```
|
|
37702
|
+
*/
|
|
37703
|
+
async createOperation(params) {
|
|
37704
|
+
try {
|
|
37705
|
+
const requestData = {
|
|
37706
|
+
permission_id: params.permissionId
|
|
37707
|
+
};
|
|
37708
|
+
const requestJson = JSON.stringify(requestData);
|
|
37709
|
+
const signature = await this.createSignature(requestJson);
|
|
37710
|
+
const requestBody = {
|
|
37711
|
+
app_signature: signature,
|
|
37712
|
+
operation_request_json: requestJson
|
|
37713
|
+
};
|
|
37714
|
+
console.debug("\u{1F50D} Debug - createOperation requestBody", requestBody);
|
|
37715
|
+
const response = await this.makeRequest(requestBody);
|
|
37716
|
+
return response;
|
|
37717
|
+
} catch (error) {
|
|
37718
|
+
if (error instanceof Error) {
|
|
37719
|
+
if (error instanceof NetworkError || error instanceof SerializationError || error instanceof SignatureError || error instanceof PersonalServerError) {
|
|
37720
|
+
throw error;
|
|
37721
|
+
}
|
|
37722
|
+
throw new PersonalServerError(
|
|
37723
|
+
`Personal server operation creation failed: ${error.message}`,
|
|
37724
|
+
error
|
|
37725
|
+
);
|
|
37726
|
+
}
|
|
37727
|
+
throw new PersonalServerError(
|
|
37728
|
+
"Personal server operation creation failed with unknown error"
|
|
37729
|
+
);
|
|
37730
|
+
}
|
|
37731
|
+
}
|
|
37732
|
+
/**
|
|
37733
|
+
* Polls the status of a computation request for updates and results.
|
|
37734
|
+
*
|
|
37735
|
+
* @remarks
|
|
37736
|
+
* This method checks the current status of a computation request by querying
|
|
37737
|
+
* the personal server API using the provided operation ID. It returns the current
|
|
37738
|
+
* status, any available output, and error information. The method can be
|
|
37739
|
+
* called periodically until the operation completes or fails.
|
|
37740
|
+
*
|
|
37741
|
+
* Common status values include: `starting`, `processing`, `succeeded`, `failed`, `canceled`.
|
|
37742
|
+
* @param operationId - The operation ID returned from the initial request submission
|
|
37743
|
+
* @returns A Promise that resolves to the current operation response with status and results
|
|
37744
|
+
* @throws {NetworkError} When the polling request fails or returns invalid data
|
|
37745
|
+
* @example
|
|
37746
|
+
* ```typescript
|
|
37747
|
+
* // Poll until completion
|
|
37748
|
+
* let result = await vana.server.getOperation(response.id);
|
|
37749
|
+
*
|
|
37750
|
+
* while (result.status === "processing") {
|
|
37751
|
+
* await new Promise(resolve => setTimeout(resolve, 1000));
|
|
37752
|
+
* result = await vana.server.getOperation(response.id);
|
|
37753
|
+
* }
|
|
37754
|
+
*
|
|
37755
|
+
* if (result.status === "succeeded") {
|
|
37756
|
+
* console.log("Computation completed:", result.output);
|
|
37757
|
+
* }
|
|
37758
|
+
* ```
|
|
37759
|
+
*/
|
|
37760
|
+
async getOperation(operationId) {
|
|
37761
|
+
try {
|
|
37762
|
+
console.debug("Polling Operation Status:", operationId);
|
|
37763
|
+
const response = await fetch(
|
|
37764
|
+
`${this.PERSONAL_SERVER_BASE_URL}/operations/${operationId}`,
|
|
37765
|
+
{
|
|
37766
|
+
method: "GET",
|
|
37767
|
+
headers: {
|
|
37768
|
+
"Content-Type": "application/json"
|
|
37769
|
+
}
|
|
37770
|
+
}
|
|
37771
|
+
);
|
|
37772
|
+
if (!response.ok) {
|
|
37773
|
+
const errorText = await response.text();
|
|
37774
|
+
console.debug("Polling Error Response:", {
|
|
37775
|
+
status: response.status,
|
|
37776
|
+
statusText: response.statusText,
|
|
37777
|
+
error: errorText
|
|
37778
|
+
});
|
|
37779
|
+
throw new NetworkError(
|
|
37780
|
+
`Status polling failed: ${response.status} ${response.statusText} - ${errorText}`
|
|
37781
|
+
);
|
|
37782
|
+
}
|
|
37783
|
+
const data = await response.json();
|
|
37784
|
+
console.debug("Polling Success Response:", data);
|
|
37785
|
+
return data;
|
|
37786
|
+
} catch (error) {
|
|
37787
|
+
if (error instanceof NetworkError) {
|
|
37788
|
+
throw error;
|
|
37789
|
+
}
|
|
37790
|
+
throw new NetworkError(
|
|
37791
|
+
`Failed to poll status: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
37792
|
+
);
|
|
37793
|
+
}
|
|
37794
|
+
}
|
|
37795
|
+
async cancelOperation(operationId) {
|
|
37796
|
+
try {
|
|
37797
|
+
const response = await fetch(
|
|
37798
|
+
`${this.PERSONAL_SERVER_BASE_URL}/operations/${operationId}/cancel`,
|
|
37799
|
+
{
|
|
37800
|
+
method: "POST"
|
|
37801
|
+
}
|
|
37802
|
+
);
|
|
37803
|
+
if (!response.ok) {
|
|
37804
|
+
const errorText = await response.text();
|
|
37805
|
+
throw new PersonalServerError(
|
|
37806
|
+
`Failed to cancel operation: ${response.status} ${response.statusText} - ${errorText}`
|
|
37807
|
+
);
|
|
37808
|
+
}
|
|
37809
|
+
} catch (error) {
|
|
37810
|
+
if (error instanceof NetworkError) {
|
|
37811
|
+
throw error;
|
|
37812
|
+
}
|
|
37813
|
+
throw new NetworkError(
|
|
37814
|
+
`Failed to cancel operation: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
37815
|
+
);
|
|
37816
|
+
}
|
|
37817
|
+
}
|
|
37818
|
+
/**
|
|
37819
|
+
* Makes the request to the personal server API.
|
|
37820
|
+
*
|
|
37821
|
+
* @param requestBody - The post request parameters to serialize
|
|
37822
|
+
* @returns JSON string representation of the request data
|
|
37823
|
+
*/
|
|
37824
|
+
async makeRequest(requestBody) {
|
|
37825
|
+
try {
|
|
37826
|
+
console.debug("Personal Server Request:", {
|
|
37827
|
+
url: `${this.PERSONAL_SERVER_BASE_URL}/operations`,
|
|
37828
|
+
method: "POST",
|
|
37829
|
+
headers: {
|
|
37830
|
+
"Content-Type": "application/json"
|
|
37831
|
+
},
|
|
37832
|
+
body: requestBody
|
|
37833
|
+
});
|
|
37834
|
+
const response = await fetch(
|
|
37835
|
+
`${this.PERSONAL_SERVER_BASE_URL}/operations`,
|
|
37836
|
+
{
|
|
37837
|
+
method: "POST",
|
|
37838
|
+
headers: {
|
|
37839
|
+
"Content-Type": "application/json"
|
|
37840
|
+
},
|
|
37841
|
+
body: JSON.stringify(requestBody)
|
|
37842
|
+
}
|
|
37843
|
+
);
|
|
37844
|
+
if (!response.ok) {
|
|
37845
|
+
const errorText = await response.text();
|
|
37846
|
+
console.debug("Personal Server Error Response:", {
|
|
37847
|
+
status: response.status,
|
|
37848
|
+
statusText: response.statusText,
|
|
37849
|
+
error: errorText
|
|
37850
|
+
});
|
|
37851
|
+
throw new NetworkError(
|
|
37852
|
+
`Personal server API request failed: ${response.status} ${response.statusText} - ${errorText}`
|
|
37853
|
+
);
|
|
37854
|
+
}
|
|
37855
|
+
const data = await response.json();
|
|
37856
|
+
console.debug("Personal Server Success Response:", data);
|
|
37857
|
+
return data;
|
|
37858
|
+
} catch (error) {
|
|
37859
|
+
if (error instanceof NetworkError) {
|
|
37860
|
+
throw error;
|
|
37861
|
+
}
|
|
37862
|
+
throw new NetworkError(
|
|
37863
|
+
`Failed to make personal server API request: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
37864
|
+
);
|
|
37865
|
+
}
|
|
37866
|
+
}
|
|
37867
|
+
/**
|
|
37868
|
+
* Creates a signature for the request JSON.
|
|
37869
|
+
*
|
|
37870
|
+
* @param requestJson - The JSON string to sign
|
|
37871
|
+
* @returns Promise resolving to the cryptographic signature
|
|
37872
|
+
*/
|
|
37873
|
+
async createSignature(requestJson) {
|
|
37874
|
+
try {
|
|
37875
|
+
console.debug("\u{1F50D} Debug - createSignature", requestJson);
|
|
37876
|
+
const client = this.context.applicationClient || this.context.walletClient;
|
|
37877
|
+
const account = client.account;
|
|
37878
|
+
if (!account) {
|
|
37879
|
+
throw new SignatureError("No account available for signing");
|
|
37880
|
+
}
|
|
37881
|
+
if (account.type !== "local") {
|
|
37882
|
+
throw new SignatureError(
|
|
37883
|
+
"Only local accounts are supported for signing"
|
|
37884
|
+
);
|
|
37885
|
+
}
|
|
37886
|
+
console.debug("\u{1F50D} Debug - createSignature account", account);
|
|
37887
|
+
const signature = await account.signMessage({
|
|
37888
|
+
message: requestJson
|
|
37889
|
+
});
|
|
37890
|
+
return signature;
|
|
37891
|
+
} catch (error) {
|
|
37892
|
+
if (error instanceof Error && error.message.includes("User rejected")) {
|
|
37893
|
+
throw new SignatureError("User rejected the signature request");
|
|
37894
|
+
}
|
|
37895
|
+
throw new SignatureError(
|
|
37896
|
+
`Failed to create signature: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
37897
|
+
);
|
|
37898
|
+
}
|
|
37899
|
+
}
|
|
37900
|
+
};
|
|
37901
|
+
|
|
37686
37902
|
// src/contracts/contractController.ts
|
|
37687
37903
|
import {
|
|
37688
|
-
getContract as
|
|
37904
|
+
getContract as getContract3
|
|
37689
37905
|
} from "viem";
|
|
37690
37906
|
|
|
37691
37907
|
// src/core/client.ts
|
|
@@ -37786,7 +38002,7 @@ function getContractController(contract, client = createClient()) {
|
|
|
37786
38002
|
const cacheKey = createCacheKey(contract, chainId);
|
|
37787
38003
|
let controller = contractCache.get(cacheKey);
|
|
37788
38004
|
if (!controller) {
|
|
37789
|
-
controller =
|
|
38005
|
+
controller = getContract3({
|
|
37790
38006
|
address: getContractAddress(chainId, contract),
|
|
37791
38007
|
abi: getAbi(contract),
|
|
37792
38008
|
client
|
|
@@ -39445,7 +39661,7 @@ var moksha = {
|
|
|
39445
39661
|
url: "https://moksha.vanascan.io"
|
|
39446
39662
|
}
|
|
39447
39663
|
},
|
|
39448
|
-
subgraphUrl: "https://api.goldsky.com/api/public/project_cm168cz887zva010j39il7a6p/subgraphs/moksha/7.0.
|
|
39664
|
+
subgraphUrl: "https://api.goldsky.com/api/public/project_cm168cz887zva010j39il7a6p/subgraphs/moksha/7.0.3/gn"
|
|
39449
39665
|
};
|
|
39450
39666
|
function getChainConfig(chainId) {
|
|
39451
39667
|
switch (chainId) {
|
|
@@ -39457,11 +39673,53 @@ function getChainConfig(chainId) {
|
|
|
39457
39673
|
return void 0;
|
|
39458
39674
|
}
|
|
39459
39675
|
}
|
|
39676
|
+
var mokshaTestnet2 = moksha;
|
|
39460
39677
|
function getAllChains() {
|
|
39461
39678
|
return [vanaMainnet2, moksha];
|
|
39462
39679
|
}
|
|
39463
39680
|
|
|
39464
39681
|
// src/core.ts
|
|
39682
|
+
var VanaCoreFactory = class {
|
|
39683
|
+
/**
|
|
39684
|
+
* Creates a VanaCore instance that enforces storage requirements at compile time.
|
|
39685
|
+
* Use this factory when you know you'll need storage-dependent operations.
|
|
39686
|
+
*
|
|
39687
|
+
* @param platform - The platform adapter for environment-specific operations
|
|
39688
|
+
* @param config - Configuration that includes required storage providers
|
|
39689
|
+
* @returns VanaCore instance with storage validation
|
|
39690
|
+
* @example
|
|
39691
|
+
* ```typescript
|
|
39692
|
+
* const vanaCore = VanaCoreFactory.createWithStorage(platformAdapter, {
|
|
39693
|
+
* walletClient: myWalletClient,
|
|
39694
|
+
* storage: {
|
|
39695
|
+
* providers: { ipfs: new IPFSStorage() },
|
|
39696
|
+
* defaultProvider: 'ipfs'
|
|
39697
|
+
* }
|
|
39698
|
+
* });
|
|
39699
|
+
* ```
|
|
39700
|
+
*/
|
|
39701
|
+
static createWithStorage(platform, config) {
|
|
39702
|
+
const core = new VanaCore(platform, config);
|
|
39703
|
+
return core;
|
|
39704
|
+
}
|
|
39705
|
+
/**
|
|
39706
|
+
* Creates a VanaCore instance without storage requirements.
|
|
39707
|
+
* Storage-dependent operations will fail at runtime if not configured.
|
|
39708
|
+
*
|
|
39709
|
+
* @param platform - The platform adapter for environment-specific operations
|
|
39710
|
+
* @param config - Basic configuration without required storage
|
|
39711
|
+
* @returns VanaCore instance
|
|
39712
|
+
* @example
|
|
39713
|
+
* ```typescript
|
|
39714
|
+
* const vanaCore = VanaCoreFactory.create(platformAdapter, {
|
|
39715
|
+
* walletClient: myWalletClient
|
|
39716
|
+
* });
|
|
39717
|
+
* ```
|
|
39718
|
+
*/
|
|
39719
|
+
static create(platform, config) {
|
|
39720
|
+
return new VanaCore(platform, config);
|
|
39721
|
+
}
|
|
39722
|
+
};
|
|
39465
39723
|
var VanaCore = class {
|
|
39466
39724
|
/**
|
|
39467
39725
|
* Initializes a new VanaCore client instance with the provided configuration.
|
|
@@ -39469,6 +39727,10 @@ var VanaCore = class {
|
|
|
39469
39727
|
* @remarks
|
|
39470
39728
|
* The constructor validates the configuration, initializes storage providers if configured,
|
|
39471
39729
|
* creates wallet and public clients, and sets up all SDK controllers with shared context.
|
|
39730
|
+
*
|
|
39731
|
+
* IMPORTANT: This constructor will validate storage requirements at runtime to fail fast.
|
|
39732
|
+
* Methods that require storage will throw runtime errors if storage is not configured.
|
|
39733
|
+
*
|
|
39472
39734
|
* @param platform - The platform adapter for environment-specific operations
|
|
39473
39735
|
* @param config - The configuration object specifying wallet or chain settings
|
|
39474
39736
|
* @throws {InvalidConfigurationError} When the configuration is invalid or incomplete
|
|
@@ -39483,8 +39745,10 @@ var VanaCore = class {
|
|
|
39483
39745
|
constructor(platform, config) {
|
|
39484
39746
|
/** Manages gasless data access permissions and trusted server registry. */
|
|
39485
39747
|
__publicField(this, "permissions");
|
|
39486
|
-
/** Handles user data file operations
|
|
39748
|
+
/** Handles user data file operations. */
|
|
39487
39749
|
__publicField(this, "data");
|
|
39750
|
+
/** Manages data schemas and refiners. */
|
|
39751
|
+
__publicField(this, "schemas");
|
|
39488
39752
|
/** Provides personal server setup and trusted server interactions. */
|
|
39489
39753
|
__publicField(this, "server");
|
|
39490
39754
|
/** Offers low-level access to Vana protocol smart contracts. */
|
|
@@ -39493,9 +39757,13 @@ var VanaCore = class {
|
|
|
39493
39757
|
__publicField(this, "platform");
|
|
39494
39758
|
__publicField(this, "relayerCallbacks");
|
|
39495
39759
|
__publicField(this, "storageManager");
|
|
39760
|
+
__publicField(this, "hasRequiredStorage");
|
|
39761
|
+
__publicField(this, "ipfsGateways");
|
|
39496
39762
|
this.platform = platform;
|
|
39497
39763
|
this.validateConfig(config);
|
|
39498
39764
|
this.relayerCallbacks = config.relayerCallbacks;
|
|
39765
|
+
this.ipfsGateways = config.ipfsGateways;
|
|
39766
|
+
this.hasRequiredStorage = hasStorageConfig(config);
|
|
39499
39767
|
if (config.storage?.providers) {
|
|
39500
39768
|
this.storageManager = new StorageManager();
|
|
39501
39769
|
for (const [name, provider] of Object.entries(config.storage.providers)) {
|
|
@@ -39550,14 +39818,70 @@ var VanaCore = class {
|
|
|
39550
39818
|
relayerCallbacks: this.relayerCallbacks,
|
|
39551
39819
|
storageManager: this.storageManager,
|
|
39552
39820
|
subgraphUrl,
|
|
39553
|
-
platform: this.platform
|
|
39821
|
+
platform: this.platform,
|
|
39554
39822
|
// Pass the platform adapter to controllers
|
|
39823
|
+
validateStorageRequired: this.validateStorageRequired.bind(this),
|
|
39824
|
+
hasStorage: this.hasStorage.bind(this),
|
|
39825
|
+
ipfsGateways: this.ipfsGateways
|
|
39555
39826
|
};
|
|
39556
39827
|
this.permissions = new PermissionsController(sharedContext);
|
|
39557
39828
|
this.data = new DataController(sharedContext);
|
|
39829
|
+
this.schemas = new SchemaController(sharedContext);
|
|
39558
39830
|
this.server = new ServerController(sharedContext);
|
|
39559
39831
|
this.protocol = new ProtocolController(sharedContext);
|
|
39560
39832
|
}
|
|
39833
|
+
/**
|
|
39834
|
+
* Validates that storage is available for storage-dependent operations.
|
|
39835
|
+
* This method enforces the fail-fast principle by checking storage availability
|
|
39836
|
+
* at method call time rather than during expensive operations.
|
|
39837
|
+
*
|
|
39838
|
+
* @throws {InvalidConfigurationError} When storage is required but not configured
|
|
39839
|
+
* @example
|
|
39840
|
+
* ```typescript
|
|
39841
|
+
* // This will throw if storage is not configured
|
|
39842
|
+
* vana.validateStorageRequired();
|
|
39843
|
+
* await vana.data.uploadFile(file); // Safe to proceed
|
|
39844
|
+
* ```
|
|
39845
|
+
*/
|
|
39846
|
+
validateStorageRequired() {
|
|
39847
|
+
if (!this.hasRequiredStorage) {
|
|
39848
|
+
throw new InvalidConfigurationError(
|
|
39849
|
+
"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."
|
|
39850
|
+
);
|
|
39851
|
+
}
|
|
39852
|
+
}
|
|
39853
|
+
/**
|
|
39854
|
+
* Checks whether storage is configured without throwing an error.
|
|
39855
|
+
*
|
|
39856
|
+
* @returns True if storage is properly configured
|
|
39857
|
+
* @example
|
|
39858
|
+
* ```typescript
|
|
39859
|
+
* if (vana.hasStorage()) {
|
|
39860
|
+
* await vana.data.uploadFile(file);
|
|
39861
|
+
* } else {
|
|
39862
|
+
* console.warn('Storage not configured - using pre-stored URLs only');
|
|
39863
|
+
* }
|
|
39864
|
+
* ```
|
|
39865
|
+
*/
|
|
39866
|
+
hasStorage() {
|
|
39867
|
+
return this.hasRequiredStorage;
|
|
39868
|
+
}
|
|
39869
|
+
/**
|
|
39870
|
+
* Type guard to check if this instance has storage enabled at compile time.
|
|
39871
|
+
* Use this when you need TypeScript to understand that storage is available.
|
|
39872
|
+
*
|
|
39873
|
+
* @returns True if storage is configured, with type narrowing
|
|
39874
|
+
* @example
|
|
39875
|
+
* ```typescript
|
|
39876
|
+
* if (vana.isStorageEnabled()) {
|
|
39877
|
+
* // TypeScript knows storage is available here
|
|
39878
|
+
* await vana.data.uploadFile(file);
|
|
39879
|
+
* }
|
|
39880
|
+
* ```
|
|
39881
|
+
*/
|
|
39882
|
+
isStorageEnabled() {
|
|
39883
|
+
return this.hasRequiredStorage;
|
|
39884
|
+
}
|
|
39561
39885
|
/**
|
|
39562
39886
|
* Validates the provided configuration object against all requirements.
|
|
39563
39887
|
*
|
|
@@ -39746,23 +40070,23 @@ var VanaCore = class {
|
|
|
39746
40070
|
return this.platform;
|
|
39747
40071
|
}
|
|
39748
40072
|
/**
|
|
39749
|
-
* Encrypts
|
|
40073
|
+
* Encrypts data using the Vana protocol standard encryption.
|
|
39750
40074
|
* This method automatically uses the correct platform adapter for the current environment.
|
|
39751
40075
|
*
|
|
39752
40076
|
* @param data The data to encrypt (string or Blob)
|
|
39753
|
-
* @param
|
|
40077
|
+
* @param key The key to use as encryption key
|
|
39754
40078
|
* @returns The encrypted data as Blob
|
|
39755
40079
|
* @example
|
|
39756
40080
|
* ```typescript
|
|
39757
40081
|
* const encryptionKey = await generateEncryptionKey(walletClient);
|
|
39758
|
-
* const encrypted = await vana.
|
|
40082
|
+
* const encrypted = await vana.encryptBlob("sensitive data", encryptionKey);
|
|
39759
40083
|
* ```
|
|
39760
40084
|
*/
|
|
39761
|
-
async
|
|
39762
|
-
return
|
|
40085
|
+
async encryptBlob(data, key) {
|
|
40086
|
+
return encryptBlobWithSignedKey(data, key, this.platform);
|
|
39763
40087
|
}
|
|
39764
40088
|
/**
|
|
39765
|
-
* Decrypts
|
|
40089
|
+
* Decrypts data that was encrypted using the Vana protocol.
|
|
39766
40090
|
* This method automatically uses the correct platform adapter for the current environment.
|
|
39767
40091
|
*
|
|
39768
40092
|
* @param encryptedData The encrypted data (string or Blob)
|
|
@@ -39771,12 +40095,16 @@ var VanaCore = class {
|
|
|
39771
40095
|
* @example
|
|
39772
40096
|
* ```typescript
|
|
39773
40097
|
* const encryptionKey = await generateEncryptionKey(walletClient);
|
|
39774
|
-
* const decrypted = await vana.
|
|
40098
|
+
* const decrypted = await vana.decryptBlob(encryptedData, encryptionKey);
|
|
39775
40099
|
* const text = await decrypted.text();
|
|
39776
40100
|
* ```
|
|
39777
40101
|
*/
|
|
39778
|
-
async
|
|
39779
|
-
return
|
|
40102
|
+
async decryptBlob(encryptedData, walletSignature) {
|
|
40103
|
+
return decryptBlobWithSignedKey(
|
|
40104
|
+
encryptedData,
|
|
40105
|
+
walletSignature,
|
|
40106
|
+
this.platform
|
|
40107
|
+
);
|
|
39780
40108
|
}
|
|
39781
40109
|
};
|
|
39782
40110
|
|
|
@@ -39807,7 +40135,7 @@ function createValidatedGrant(params) {
|
|
|
39807
40135
|
try {
|
|
39808
40136
|
validateGrant(grantFile, {
|
|
39809
40137
|
schema: true,
|
|
39810
|
-
grantee: params.
|
|
40138
|
+
grantee: params.grantee,
|
|
39811
40139
|
operation: params.operation
|
|
39812
40140
|
});
|
|
39813
40141
|
} catch (error) {
|
|
@@ -40564,25 +40892,15 @@ var ApiClient = class {
|
|
|
40564
40892
|
};
|
|
40565
40893
|
|
|
40566
40894
|
// src/index.browser.ts
|
|
40567
|
-
var
|
|
40568
|
-
/**
|
|
40569
|
-
* Creates a Vana SDK instance configured for browser environments.
|
|
40570
|
-
*
|
|
40571
|
-
* @param config - SDK configuration object (wallet client or chain config)
|
|
40572
|
-
* @example
|
|
40573
|
-
* ```typescript
|
|
40574
|
-
* // With wallet client
|
|
40575
|
-
* const vana = new Vana({ walletClient });
|
|
40576
|
-
*
|
|
40577
|
-
* // With chain configuration
|
|
40578
|
-
* const vana = new Vana({ chainId: 14800, account });
|
|
40579
|
-
* ```
|
|
40580
|
-
*/
|
|
40895
|
+
var VanaBrowserImpl = class extends VanaCore {
|
|
40581
40896
|
constructor(config) {
|
|
40582
40897
|
super(new BrowserPlatformAdapter(), config);
|
|
40583
40898
|
}
|
|
40584
40899
|
};
|
|
40585
|
-
|
|
40900
|
+
function Vana(config) {
|
|
40901
|
+
return new VanaBrowserImpl(config);
|
|
40902
|
+
}
|
|
40903
|
+
var index_browser_default = Vana;
|
|
40586
40904
|
export {
|
|
40587
40905
|
ApiClient,
|
|
40588
40906
|
AsyncQueue,
|
|
@@ -40627,8 +40945,10 @@ export {
|
|
|
40627
40945
|
StorageError,
|
|
40628
40946
|
StorageManager,
|
|
40629
40947
|
UserRejectedRequestError,
|
|
40630
|
-
|
|
40631
|
-
|
|
40948
|
+
Vana,
|
|
40949
|
+
VanaBrowserImpl,
|
|
40950
|
+
VanaCore,
|
|
40951
|
+
VanaCoreFactory,
|
|
40632
40952
|
VanaError,
|
|
40633
40953
|
__contractCache,
|
|
40634
40954
|
chains,
|
|
@@ -40641,13 +40961,13 @@ export {
|
|
|
40641
40961
|
createGrantFile,
|
|
40642
40962
|
createPlatformAdapterSafe,
|
|
40643
40963
|
createValidatedGrant,
|
|
40644
|
-
|
|
40964
|
+
decryptBlobWithSignedKey,
|
|
40645
40965
|
decryptWithPrivateKey,
|
|
40646
40966
|
decryptWithWalletPrivateKey,
|
|
40647
40967
|
index_browser_default as default,
|
|
40648
40968
|
detectPlatform,
|
|
40969
|
+
encryptBlobWithSignedKey,
|
|
40649
40970
|
encryptFileKey,
|
|
40650
|
-
encryptUserData,
|
|
40651
40971
|
encryptWithWalletPublicKey,
|
|
40652
40972
|
extractIpfsHash,
|
|
40653
40973
|
fetchAndValidateSchema,
|
|
@@ -40671,13 +40991,11 @@ export {
|
|
|
40671
40991
|
getPlatformCapabilities,
|
|
40672
40992
|
isAPIResponse,
|
|
40673
40993
|
isGrantExpired,
|
|
40674
|
-
isIdentityServerOutput,
|
|
40675
40994
|
isIpfsUrl,
|
|
40676
|
-
isPersonalServerOutput,
|
|
40677
40995
|
isPlatformSupported,
|
|
40678
40996
|
isReplicateAPIResponse,
|
|
40679
40997
|
moksha,
|
|
40680
|
-
mokshaTestnet,
|
|
40998
|
+
mokshaTestnet2 as mokshaTestnet,
|
|
40681
40999
|
parseReplicateOutput,
|
|
40682
41000
|
retrieveAndValidateGrant,
|
|
40683
41001
|
retrieveGrantFile,
|
|
@@ -40693,6 +41011,6 @@ export {
|
|
|
40693
41011
|
validateGrantFile,
|
|
40694
41012
|
validateGranteeAccess,
|
|
40695
41013
|
validateOperationAccess,
|
|
40696
|
-
vanaMainnet
|
|
41014
|
+
vanaMainnet2 as vanaMainnet
|
|
40697
41015
|
};
|
|
40698
41016
|
//# sourceMappingURL=index.browser.js.map
|