@volr/sdk-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1342 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1338 -0
- package/dist/index.d.ts +1338 -0
- package/dist/index.js +1288 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1338 @@
|
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* HKDF-SHA256 key derivation
|
|
5
|
+
*
|
|
6
|
+
* @param ikm - Input Key Material (32 bytes recommended)
|
|
7
|
+
* @param salt - Salt (can be empty, but 32 bytes recommended)
|
|
8
|
+
* @param info - Application-specific info string
|
|
9
|
+
* @param len - Output length in bytes (default: 32)
|
|
10
|
+
* @returns Derived key material
|
|
11
|
+
* @throws VolrError if info is empty or other validation fails
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const ikm = new Uint8Array(32).fill(0x01);
|
|
16
|
+
* const salt = new Uint8Array(32).fill(0x02);
|
|
17
|
+
* const key = hkdfSha256(ikm, salt, 'volr/wrap-key/v1', 32);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
declare function hkdfSha256(ikm: Uint8Array, salt: Uint8Array, info: string, len?: number): Uint8Array;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* AES-GCM encryption parameters
|
|
24
|
+
*/
|
|
25
|
+
type AesGcmParams = {
|
|
26
|
+
/** 32-byte encryption key */
|
|
27
|
+
key: Uint8Array;
|
|
28
|
+
/** 12-byte nonce (optional, will be generated if not provided) */
|
|
29
|
+
nonce?: Uint8Array;
|
|
30
|
+
/** Additional Authenticated Data (optional) */
|
|
31
|
+
aad?: Uint8Array;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* AES-256-GCM encryption
|
|
35
|
+
*
|
|
36
|
+
* @param plain - Plaintext to encrypt
|
|
37
|
+
* @param params - Encryption parameters (key, optional nonce, optional AAD)
|
|
38
|
+
* @returns Object containing ciphertext and nonce
|
|
39
|
+
* @throws VolrError if key is not 32 bytes, nonce is not 12 bytes, or encryption fails
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const key = getRandomBytes(32);
|
|
44
|
+
* const plaintext = new TextEncoder().encode('secret data');
|
|
45
|
+
* const { cipher, nonce } = await aesGcmEncrypt(plaintext, { key });
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
declare function aesGcmEncrypt(plain: Uint8Array, params: AesGcmParams): Promise<{
|
|
49
|
+
cipher: Uint8Array;
|
|
50
|
+
nonce: Uint8Array;
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* AES-256-GCM decryption
|
|
54
|
+
*
|
|
55
|
+
* @param cipher - Ciphertext to decrypt
|
|
56
|
+
* @param params - Decryption parameters (key, nonce, optional AAD)
|
|
57
|
+
* @returns Decrypted plaintext
|
|
58
|
+
* @throws VolrError if decryption fails (wrong key, wrong AAD, corrupted data)
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* const plaintext = await aesGcmDecrypt(cipher, { key, nonce });
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
declare function aesGcmDecrypt(cipher: Uint8Array, params: AesGcmParams & {
|
|
66
|
+
nonce: Uint8Array;
|
|
67
|
+
}): Promise<Uint8Array>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Zeroize (overwrite with zeros) a buffer in place
|
|
71
|
+
* This helps prevent sensitive data from remaining in memory
|
|
72
|
+
*
|
|
73
|
+
* @param buf - Uint8Array or ArrayBuffer to zeroize
|
|
74
|
+
*/
|
|
75
|
+
declare function zeroize(buf: Uint8Array | ArrayBuffer): void;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get secure random bytes using WebCrypto API
|
|
79
|
+
* Falls back to Node.js crypto.webcrypto if WebCrypto is not available
|
|
80
|
+
*
|
|
81
|
+
* @param len - Number of bytes to generate
|
|
82
|
+
* @returns Uint8Array of random bytes
|
|
83
|
+
* @throws VolrError if random generation fails
|
|
84
|
+
*/
|
|
85
|
+
declare function getRandomBytes(len: number): Uint8Array;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Opaque type for WrapKey (32 bytes)
|
|
89
|
+
* This is a type-level marker to prevent accidental misuse
|
|
90
|
+
*/
|
|
91
|
+
type WrapKey = Uint8Array & {
|
|
92
|
+
readonly __brand: 'WrapKey';
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Opaque type for MasterSeed (32 bytes)
|
|
96
|
+
* This is a type-level marker to prevent accidental misuse
|
|
97
|
+
*/
|
|
98
|
+
type MasterSeed = Uint8Array & {
|
|
99
|
+
readonly __brand: 'MasterSeed';
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Opaque handle to master seed
|
|
104
|
+
* Provides controlled access and ensures cleanup via destroy()
|
|
105
|
+
*/
|
|
106
|
+
type MasterSeedHandle = {
|
|
107
|
+
/** Read-only access to master seed bytes */
|
|
108
|
+
readonly bytes: MasterSeed;
|
|
109
|
+
/** Destroy the handle and zeroize the seed */
|
|
110
|
+
destroy(): void;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Master key provider interface
|
|
114
|
+
* Handles generation and unsealing of master seeds
|
|
115
|
+
*/
|
|
116
|
+
interface MasterKeyProvider {
|
|
117
|
+
/**
|
|
118
|
+
* Unseal master seed from encrypted blob
|
|
119
|
+
*
|
|
120
|
+
* @param blob - Encrypted blob containing ciphertext, nonce, and AAD
|
|
121
|
+
* @param wrapKey - Wrap key for decryption
|
|
122
|
+
* @returns Handle to unsealed master seed
|
|
123
|
+
*/
|
|
124
|
+
unsealFromBlob(blob: {
|
|
125
|
+
cipher: Uint8Array;
|
|
126
|
+
nonce: Uint8Array;
|
|
127
|
+
aad: Uint8Array;
|
|
128
|
+
}, wrapKey: WrapKey): Promise<MasterSeedHandle>;
|
|
129
|
+
/**
|
|
130
|
+
* Generate a new master seed
|
|
131
|
+
*
|
|
132
|
+
* @returns Handle to newly generated master seed
|
|
133
|
+
*/
|
|
134
|
+
generate(): Promise<MasterSeedHandle>;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Create a master key provider instance
|
|
138
|
+
*
|
|
139
|
+
* @returns MasterKeyProvider instance
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* const provider = createMasterKeyProvider();
|
|
144
|
+
* const handle = await provider.generate();
|
|
145
|
+
* // Use handle.bytes for key derivation
|
|
146
|
+
* handle.destroy(); // Cleanup
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
declare function createMasterKeyProvider(): MasterKeyProvider;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Seal (encrypt) master seed with wrap key
|
|
153
|
+
*
|
|
154
|
+
* @param masterSeed - 32-byte master seed to encrypt
|
|
155
|
+
* @param wrapKey - 32-byte wrap key
|
|
156
|
+
* @param aad - Additional Authenticated Data
|
|
157
|
+
* @returns Object containing ciphertext and nonce
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```ts
|
|
161
|
+
* const masterSeed = getRandomBytes(32);
|
|
162
|
+
* const wrapKey = deriveWrapKey({ origin, projectId, credentialId });
|
|
163
|
+
* const aad = new TextEncoder().encode('volr/master-seed/v1');
|
|
164
|
+
* const { cipher, nonce } = await sealMasterSeed(masterSeed, wrapKey, aad);
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
declare function sealMasterSeed(masterSeed: Uint8Array, wrapKey: WrapKey, aad: Uint8Array): Promise<{
|
|
168
|
+
cipher: Uint8Array;
|
|
169
|
+
nonce: Uint8Array;
|
|
170
|
+
}>;
|
|
171
|
+
/**
|
|
172
|
+
* Unseal (decrypt) master seed with wrap key
|
|
173
|
+
*
|
|
174
|
+
* @param cipher - Encrypted master seed
|
|
175
|
+
* @param wrapKey - 32-byte wrap key
|
|
176
|
+
* @param aad - Additional Authenticated Data (must match encryption)
|
|
177
|
+
* @param nonce - 12-byte nonce used during encryption
|
|
178
|
+
* @returns Decrypted master seed (32 bytes)
|
|
179
|
+
* @throws Error if decryption fails (wrong key, wrong AAD, corrupted data)
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```ts
|
|
183
|
+
* const masterSeed = await unsealMasterSeed(cipher, wrapKey, aad, nonce);
|
|
184
|
+
* // Use masterSeed, then zeroize it
|
|
185
|
+
* zeroize(masterSeed);
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
declare function unsealMasterSeed(cipher: Uint8Array, wrapKey: WrapKey, aad: Uint8Array, nonce: Uint8Array): Promise<MasterSeed>;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* PRF input parameters for wrap key derivation
|
|
192
|
+
*/
|
|
193
|
+
type PrfInput = {
|
|
194
|
+
/** Origin (e.g., "https://example.com") */
|
|
195
|
+
origin: string;
|
|
196
|
+
/** Project ID */
|
|
197
|
+
projectId: string;
|
|
198
|
+
/** Credential ID from WebAuthn */
|
|
199
|
+
credentialId: string;
|
|
200
|
+
/** Optional salt (defaults to SHA256 of concatenated inputs) */
|
|
201
|
+
salt?: Uint8Array;
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* Derive wrap key from PRF inputs using HKDF
|
|
205
|
+
*
|
|
206
|
+
* PRF input domain is fixed: rpId|projectId (credentialId excluded)
|
|
207
|
+
* This ensures consistent wrap key derivation across sessions
|
|
208
|
+
*
|
|
209
|
+
* Note: credentialId is NOT included in salt derivation because:
|
|
210
|
+
* - During enrollment, actual credentialId is not known until after credential creation
|
|
211
|
+
* - Using credentialId would create different salts for enrollment vs authentication
|
|
212
|
+
* - This would cause PRF to return different outputs, breaking decryption
|
|
213
|
+
*
|
|
214
|
+
* This function maps PRF output (from WebAuthn) to a 32-byte wrap key.
|
|
215
|
+
* In the real implementation, the PRF output would come from WebAuthn PRF API.
|
|
216
|
+
* For now, we simulate it by using SHA256 of the inputs.
|
|
217
|
+
*
|
|
218
|
+
* @param input - PRF input parameters
|
|
219
|
+
* @returns 32-byte wrap key
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```ts
|
|
223
|
+
* const wrapKey = deriveWrapKey({
|
|
224
|
+
* origin: 'https://example.com',
|
|
225
|
+
* projectId: 'project-123',
|
|
226
|
+
* credentialId: 'cred-456' // stored for authentication, but not used in salt derivation
|
|
227
|
+
* });
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
declare function deriveWrapKey(input: PrfInput): WrapKey;
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Default BIP-32 derivation path for Ethereum
|
|
234
|
+
* m / purpose' / coin_type' / account' / change / address_index
|
|
235
|
+
* 60' = Ethereum coin type
|
|
236
|
+
*/
|
|
237
|
+
declare const DEFAULT_EVM_PATH = "m/60'/0'/0'/0/0";
|
|
238
|
+
/**
|
|
239
|
+
* Arguments for EVM key derivation
|
|
240
|
+
*/
|
|
241
|
+
type DeriveArgs = {
|
|
242
|
+
/** Master seed (32 bytes recommended) */
|
|
243
|
+
masterSeed: Uint8Array;
|
|
244
|
+
/** BIP-32 derivation path (default: m/60'/0'/0'/0/0) */
|
|
245
|
+
path?: string;
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* EVM keypair derived from master seed
|
|
249
|
+
*/
|
|
250
|
+
type EvmKeypair = {
|
|
251
|
+
/** 32-byte private key */
|
|
252
|
+
privateKey: Uint8Array;
|
|
253
|
+
/** 65-byte uncompressed public key (0x04 prefix) */
|
|
254
|
+
publicKey: Uint8Array;
|
|
255
|
+
/** 20-byte EOA address (checksummed) */
|
|
256
|
+
address: `0x${string}`;
|
|
257
|
+
/** Derivation path used */
|
|
258
|
+
path: string;
|
|
259
|
+
};
|
|
260
|
+
/**
|
|
261
|
+
* Derive EVM keypair from master seed using BIP-32
|
|
262
|
+
*
|
|
263
|
+
* @param args - Derivation arguments
|
|
264
|
+
* @returns EVM keypair with private key, public key, and address
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* ```ts
|
|
268
|
+
* const masterSeed = getRandomBytes(32);
|
|
269
|
+
* const keypair = deriveEvmKey({ masterSeed });
|
|
270
|
+
* // Use keypair.privateKey for signing
|
|
271
|
+
* // Use keypair.address as EOA address
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
declare function deriveEvmKey(args: DeriveArgs): EvmKeypair;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Convert Ethereum address to EIP-55 checksum format
|
|
278
|
+
*
|
|
279
|
+
* EIP-55: Mixed-case checksum address encoding
|
|
280
|
+
* https://eips.ethereum.org/EIPS/eip-55
|
|
281
|
+
*
|
|
282
|
+
* @param addr - Lowercase Ethereum address (0x prefix required)
|
|
283
|
+
* @returns Checksummed address
|
|
284
|
+
* @throws Error if address is invalid (wrong length, non-hex, missing 0x prefix)
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* ```ts
|
|
288
|
+
* const checksummed = toChecksumAddress('0x742d35cc6634c0532925a3b844bc9e7595f0bebd');
|
|
289
|
+
* // Returns: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbD'
|
|
290
|
+
* ```
|
|
291
|
+
*/
|
|
292
|
+
declare function toChecksumAddress(addr: `0x${string}`): `0x${string}`;
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* EVM signature structure
|
|
296
|
+
*/
|
|
297
|
+
type Sig$1 = {
|
|
298
|
+
/** 32-byte r value */
|
|
299
|
+
r: Uint8Array;
|
|
300
|
+
/** 32-byte s value (low-S canonicalized) */
|
|
301
|
+
s: Uint8Array;
|
|
302
|
+
/** y-parity (0 or 1) */
|
|
303
|
+
yParity: 0 | 1;
|
|
304
|
+
};
|
|
305
|
+
/**
|
|
306
|
+
* Sign a message hash with secp256k1 private key
|
|
307
|
+
*
|
|
308
|
+
* Produces deterministic, low-S canonicalized signatures compatible with EVM.
|
|
309
|
+
*
|
|
310
|
+
* @param privKey - 32-byte private key
|
|
311
|
+
* @param msgHash32 - 32-byte message hash
|
|
312
|
+
* @returns Signature with r, s, and yParity
|
|
313
|
+
* @throws Error if private key or message hash is invalid
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```ts
|
|
317
|
+
* const privateKey = getRandomBytes(32);
|
|
318
|
+
* const msgHash = sha256(new TextEncoder().encode('Hello'));
|
|
319
|
+
* const sig = evmSign(privateKey, msgHash);
|
|
320
|
+
* // Use sig.r, sig.s, sig.yParity for transaction signing
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
declare function evmSign(privKey: Uint8Array, msgHash32: Uint8Array): Sig$1;
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Provider types for EVM wallet operations
|
|
327
|
+
*/
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* RPC client interface for capability detection
|
|
331
|
+
*/
|
|
332
|
+
interface RpcLike {
|
|
333
|
+
call(args: {
|
|
334
|
+
to: `0x${string}`;
|
|
335
|
+
data: `0x${string}`;
|
|
336
|
+
}): Promise<`0x${string}`>;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* EIP-712 typed data structure
|
|
340
|
+
*/
|
|
341
|
+
type TypedDataInput = {
|
|
342
|
+
domain: any;
|
|
343
|
+
types: any;
|
|
344
|
+
message: any;
|
|
345
|
+
};
|
|
346
|
+
/**
|
|
347
|
+
* Common wallet provider port interface
|
|
348
|
+
* Both Passkey and MPC providers implement this interface
|
|
349
|
+
*/
|
|
350
|
+
interface WalletProviderPort {
|
|
351
|
+
/** Key storage type: 'passkey' or 'mpc' */
|
|
352
|
+
keyStorageType: KeyStorageType;
|
|
353
|
+
/**
|
|
354
|
+
* Ensure session is established
|
|
355
|
+
* - Passkey: PRF → unwrap master seed
|
|
356
|
+
* - MPC: Backend proxy session setup
|
|
357
|
+
*
|
|
358
|
+
* @param opts - Session options
|
|
359
|
+
* @param opts.interactive - If true, requires user gesture (WebAuthn prompt)
|
|
360
|
+
* @param opts.force - If true, invalidates existing session and re-authenticates
|
|
361
|
+
*/
|
|
362
|
+
ensureSession(opts?: {
|
|
363
|
+
interactive?: boolean;
|
|
364
|
+
force?: boolean;
|
|
365
|
+
}): Promise<void>;
|
|
366
|
+
/**
|
|
367
|
+
* Lock the provider (zeroize sensitive data from memory)
|
|
368
|
+
* Should be called after each transaction for TTL=0 behavior
|
|
369
|
+
*/
|
|
370
|
+
lock?(): Promise<void>;
|
|
371
|
+
/**
|
|
372
|
+
* Get EOA address
|
|
373
|
+
* @returns Ethereum address (checksummed)
|
|
374
|
+
*/
|
|
375
|
+
getAddress(): Promise<`0x${string}`>;
|
|
376
|
+
/**
|
|
377
|
+
* Sign message hash with secp256k1
|
|
378
|
+
* @param hash32 - 32-byte message hash
|
|
379
|
+
* @returns Signature components
|
|
380
|
+
*/
|
|
381
|
+
signMessage(hash32: Uint8Array): Promise<{
|
|
382
|
+
r: Uint8Array;
|
|
383
|
+
s: Uint8Array;
|
|
384
|
+
yParity: 0 | 1;
|
|
385
|
+
}>;
|
|
386
|
+
/**
|
|
387
|
+
* Sign EIP-712 typed data
|
|
388
|
+
* @param input - Typed data structure
|
|
389
|
+
* @returns Signature hex string
|
|
390
|
+
*/
|
|
391
|
+
signTypedData(input: TypedDataInput): Promise<`0x${string}`>;
|
|
392
|
+
/**
|
|
393
|
+
* Get public key (optional, for signer creation)
|
|
394
|
+
* @returns 65-byte uncompressed public key (0x04 prefix)
|
|
395
|
+
*/
|
|
396
|
+
getPublicKey?(): Promise<Uint8Array>;
|
|
397
|
+
/**
|
|
398
|
+
* Optional capability hints
|
|
399
|
+
* - p256: Whether P-256 signing is supported
|
|
400
|
+
* - secp256k1: Whether secp256k1 signing is supported (default: true)
|
|
401
|
+
*/
|
|
402
|
+
capabilities?: {
|
|
403
|
+
p256?: boolean;
|
|
404
|
+
secp256k1?: boolean;
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* MPC transport interface for backend proxy communication
|
|
409
|
+
* Vendor-agnostic: no vendor names in interface
|
|
410
|
+
*/
|
|
411
|
+
interface MpcTransport {
|
|
412
|
+
/**
|
|
413
|
+
* Ensure session is established with backend
|
|
414
|
+
*/
|
|
415
|
+
ensureSession(): Promise<void>;
|
|
416
|
+
/**
|
|
417
|
+
* Get EOA address from backend
|
|
418
|
+
* @returns Ethereum address
|
|
419
|
+
*/
|
|
420
|
+
getAddress(): Promise<`0x${string}`>;
|
|
421
|
+
/**
|
|
422
|
+
* Sign message hash via backend proxy
|
|
423
|
+
* @param hash32 - 32-byte message hash
|
|
424
|
+
* @returns Signature components
|
|
425
|
+
*/
|
|
426
|
+
signMessage(hash32: Uint8Array): Promise<{
|
|
427
|
+
r: Uint8Array;
|
|
428
|
+
s: Uint8Array;
|
|
429
|
+
yParity: 0 | 1;
|
|
430
|
+
}>;
|
|
431
|
+
/**
|
|
432
|
+
* Sign EIP-712 typed data via backend proxy
|
|
433
|
+
* @param input - Typed data structure
|
|
434
|
+
* @returns Signature hex string
|
|
435
|
+
*/
|
|
436
|
+
signTypedData(input: TypedDataInput): Promise<`0x${string}`>;
|
|
437
|
+
/**
|
|
438
|
+
* Get public key from backend (optional, for signer creation)
|
|
439
|
+
* @returns 65-byte uncompressed public key (0x04 prefix)
|
|
440
|
+
*/
|
|
441
|
+
getPublicKey?(): Promise<Uint8Array>;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Signer port interface for abstracting signing operations
|
|
446
|
+
* Supports both P-256 (TEE/passkey) and secp256k1 (software) signing
|
|
447
|
+
*/
|
|
448
|
+
interface SignerPort {
|
|
449
|
+
/**
|
|
450
|
+
* Get public key in uncompressed format
|
|
451
|
+
* - secp256k1: 65 bytes (0x04 || x || y)
|
|
452
|
+
* - P-256: 65 bytes (0x04 || x || y)
|
|
453
|
+
*
|
|
454
|
+
* @returns Uncompressed public key
|
|
455
|
+
*/
|
|
456
|
+
getPublicKey(): Promise<Uint8Array>;
|
|
457
|
+
/**
|
|
458
|
+
* Sign a message hash
|
|
459
|
+
*
|
|
460
|
+
* @param msgHash32 - 32-byte message hash (already hashed)
|
|
461
|
+
* @returns Signature with r, s, and yParity
|
|
462
|
+
*/
|
|
463
|
+
signMessage(msgHash32: Uint8Array): Promise<{
|
|
464
|
+
r: Uint8Array;
|
|
465
|
+
s: Uint8Array;
|
|
466
|
+
yParity: 0 | 1;
|
|
467
|
+
}>;
|
|
468
|
+
/**
|
|
469
|
+
* Sign raw hash without EIP-191 prefix (for EIP-7702)
|
|
470
|
+
*
|
|
471
|
+
* @param digest - 32-byte digest to sign (raw, no prefix)
|
|
472
|
+
* @returns Signature with r, s, and yParity
|
|
473
|
+
*/
|
|
474
|
+
signRawHash(digest: Uint8Array): Promise<{
|
|
475
|
+
r: Uint8Array;
|
|
476
|
+
s: Uint8Array;
|
|
477
|
+
yParity: 0 | 1;
|
|
478
|
+
}>;
|
|
479
|
+
/**
|
|
480
|
+
* Sign typed data (optional, for future EIP-712 support)
|
|
481
|
+
*
|
|
482
|
+
* @param typed - Typed data structure
|
|
483
|
+
* @returns Signature hex string
|
|
484
|
+
*/
|
|
485
|
+
signTyped?(typed: unknown): Promise<`0x${string}`>;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Passkey provider port interface
|
|
489
|
+
* Abstracts WebAuthn passkey operations (to be implemented in react package)
|
|
490
|
+
*/
|
|
491
|
+
interface PasskeyProviderPort {
|
|
492
|
+
/**
|
|
493
|
+
* Get P-256 public key from passkey
|
|
494
|
+
*
|
|
495
|
+
* @returns Public key coordinates (x, y)
|
|
496
|
+
*/
|
|
497
|
+
getPublicKey(): Promise<{
|
|
498
|
+
x: Uint8Array;
|
|
499
|
+
y: Uint8Array;
|
|
500
|
+
}>;
|
|
501
|
+
/**
|
|
502
|
+
* Sign message hash using P-256 passkey
|
|
503
|
+
*
|
|
504
|
+
* @param msgHash32 - 32-byte message hash
|
|
505
|
+
* @returns Signature components (r, s)
|
|
506
|
+
*/
|
|
507
|
+
signP256(msgHash32: Uint8Array): Promise<{
|
|
508
|
+
r: Uint8Array;
|
|
509
|
+
s: Uint8Array;
|
|
510
|
+
}>;
|
|
511
|
+
/**
|
|
512
|
+
* Authenticate with WebAuthn and get PRF output
|
|
513
|
+
* This triggers a user gesture (biometric/PIN prompt)
|
|
514
|
+
*
|
|
515
|
+
* @param prfInput - PRF evaluation input (salt)
|
|
516
|
+
* @param credentialId - Optional credential ID to use
|
|
517
|
+
* @returns PRF output (32 bytes)
|
|
518
|
+
*/
|
|
519
|
+
authenticate(prfInput: {
|
|
520
|
+
salt: Uint8Array;
|
|
521
|
+
credentialId?: string;
|
|
522
|
+
}): Promise<{
|
|
523
|
+
prfOutput: Uint8Array;
|
|
524
|
+
credentialId: string;
|
|
525
|
+
}>;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Key storage type identifier
|
|
529
|
+
* Represents the method used to store cryptographic keys: Passkey (hardware-based) or MPC (Multi-Party Computation)
|
|
530
|
+
*/
|
|
531
|
+
type KeyStorageType = 'passkey' | 'mpc';
|
|
532
|
+
/**
|
|
533
|
+
* Signer kind identifier
|
|
534
|
+
*/
|
|
535
|
+
type SignerKind = 'secp256k1' | 'passkey-p256';
|
|
536
|
+
/**
|
|
537
|
+
* Signer context for routing
|
|
538
|
+
*/
|
|
539
|
+
type SignerContext = {
|
|
540
|
+
/** RPC client for capability detection */
|
|
541
|
+
client: any;
|
|
542
|
+
/** Chain ID */
|
|
543
|
+
chainId: number;
|
|
544
|
+
/** Optional wallet provider (for provider-based routing) */
|
|
545
|
+
provider?: WalletProviderPort;
|
|
546
|
+
/** Optional passkey provider (for P-256 path) */
|
|
547
|
+
passkey?: PasskeyProviderPort;
|
|
548
|
+
/** Optional secp256k1 private key (for fallback path) */
|
|
549
|
+
secpKey?: Uint8Array;
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Select appropriate signer based on chain capabilities and available inputs
|
|
554
|
+
*
|
|
555
|
+
* Routing logic:
|
|
556
|
+
* 1. If provider provided → ProviderBackedSigner (delegates to provider)
|
|
557
|
+
* 2. Else if secpKey provided → Secp256k1SoftwareSigner
|
|
558
|
+
* 3. Else → throw error
|
|
559
|
+
*
|
|
560
|
+
* @param ctx - Signer context
|
|
561
|
+
* @returns Selected signer with kind identifier
|
|
562
|
+
* @throws VolrError if no signer input is available
|
|
563
|
+
*
|
|
564
|
+
* @example
|
|
565
|
+
* ```ts
|
|
566
|
+
* const ctx = {
|
|
567
|
+
* client: rpcClient,
|
|
568
|
+
* chainId: 10,
|
|
569
|
+
* provider: walletProvider,
|
|
570
|
+
* };
|
|
571
|
+
* const { kind, signer } = await selectSigner(ctx);
|
|
572
|
+
* const sig = await signer.signMessage(msgHash);
|
|
573
|
+
* ```
|
|
574
|
+
*/
|
|
575
|
+
declare function selectSigner$1(ctx: SignerContext): Promise<{
|
|
576
|
+
kind: SignerKind;
|
|
577
|
+
signer: SignerPort;
|
|
578
|
+
}>;
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* EVM signature structure
|
|
582
|
+
*/
|
|
583
|
+
type Sig = {
|
|
584
|
+
/** 32-byte r value */
|
|
585
|
+
r: Uint8Array;
|
|
586
|
+
/** 32-byte s value (low-S canonicalized) */
|
|
587
|
+
s: Uint8Array;
|
|
588
|
+
/** y-parity (0 or 1) */
|
|
589
|
+
yParity: 0 | 1;
|
|
590
|
+
};
|
|
591
|
+
/**
|
|
592
|
+
* Secp256k1 software signer implementation
|
|
593
|
+
* Uses existing master-key → BIP-32 → secp256k1 derivation path
|
|
594
|
+
*
|
|
595
|
+
* @example
|
|
596
|
+
* ```ts
|
|
597
|
+
* const privateKey = getRandomBytes(32);
|
|
598
|
+
* const signer = new Secp256k1SoftwareSigner(privateKey);
|
|
599
|
+
* const sig = await signer.signMessage(msgHash);
|
|
600
|
+
* ```
|
|
601
|
+
*/
|
|
602
|
+
declare class Secp256k1SoftwareSigner implements SignerPort {
|
|
603
|
+
private privateKey;
|
|
604
|
+
constructor(privateKey: Uint8Array);
|
|
605
|
+
/**
|
|
606
|
+
* Get uncompressed public key (65 bytes)
|
|
607
|
+
*
|
|
608
|
+
* @returns Uncompressed public key with 0x04 prefix
|
|
609
|
+
*/
|
|
610
|
+
getPublicKey(): Promise<Uint8Array>;
|
|
611
|
+
/**
|
|
612
|
+
* Sign message hash with deterministic, low-S canonicalization
|
|
613
|
+
*
|
|
614
|
+
* @param msgHash32 - 32-byte message hash
|
|
615
|
+
* @returns Signature with r, s, and yParity
|
|
616
|
+
* @throws Error if message hash is invalid
|
|
617
|
+
*/
|
|
618
|
+
signMessage(msgHash32: Uint8Array): Promise<{
|
|
619
|
+
r: Uint8Array;
|
|
620
|
+
s: Uint8Array;
|
|
621
|
+
yParity: 0 | 1;
|
|
622
|
+
}>;
|
|
623
|
+
/**
|
|
624
|
+
* Sign raw hash without EIP-191 prefix (for EIP-7702)
|
|
625
|
+
*
|
|
626
|
+
* @param digest - 32-byte digest to sign
|
|
627
|
+
* @returns Signature with r, s, and yParity
|
|
628
|
+
* @throws Error if digest is invalid
|
|
629
|
+
*/
|
|
630
|
+
signRawHash(digest: Uint8Array): Promise<{
|
|
631
|
+
r: Uint8Array;
|
|
632
|
+
s: Uint8Array;
|
|
633
|
+
yParity: 0 | 1;
|
|
634
|
+
}>;
|
|
635
|
+
/**
|
|
636
|
+
* Sign EIP-712 typed data
|
|
637
|
+
*/
|
|
638
|
+
signTyped(data: {
|
|
639
|
+
domain: any;
|
|
640
|
+
types: any;
|
|
641
|
+
primaryType: string;
|
|
642
|
+
message: any;
|
|
643
|
+
}): Promise<`0x${string}`>;
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Verify a secp256k1 signature against a public key and message hash
|
|
647
|
+
*
|
|
648
|
+
* @param pubKeyUncompressed - 65-byte uncompressed public key (0x04 prefix)
|
|
649
|
+
* @param msgHash32 - 32-byte message hash
|
|
650
|
+
* @param sig - Signature to verify
|
|
651
|
+
* @returns true if signature is valid, false otherwise
|
|
652
|
+
* @throws Error if public key or message hash is invalid
|
|
653
|
+
*
|
|
654
|
+
* @example
|
|
655
|
+
* ```ts
|
|
656
|
+
* const isValid = evmVerify(publicKey, msgHash, sig);
|
|
657
|
+
* if (isValid) {
|
|
658
|
+
* console.log('Signature is valid');
|
|
659
|
+
* }
|
|
660
|
+
* ```
|
|
661
|
+
*/
|
|
662
|
+
declare function evmVerify(pubKeyUncompressed: Uint8Array, msgHash32: Uint8Array, sig: Sig): boolean;
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Passkey P-256 signer implementation
|
|
666
|
+
* Uses device TEE/passkey for signing (P-256 curve)
|
|
667
|
+
*
|
|
668
|
+
* @example
|
|
669
|
+
* ```ts
|
|
670
|
+
* const passkeyProvider = createPasskeyProvider();
|
|
671
|
+
* const signer = new PasskeyP256Signer(passkeyProvider);
|
|
672
|
+
* const sig = await signer.signMessage(msgHash);
|
|
673
|
+
* ```
|
|
674
|
+
*/
|
|
675
|
+
declare class PasskeyP256Signer implements SignerPort {
|
|
676
|
+
private provider;
|
|
677
|
+
constructor(provider: PasskeyProviderPort);
|
|
678
|
+
/**
|
|
679
|
+
* Get uncompressed P-256 public key (65 bytes)
|
|
680
|
+
*
|
|
681
|
+
* @returns Uncompressed public key with 0x04 prefix
|
|
682
|
+
*/
|
|
683
|
+
getPublicKey(): Promise<Uint8Array>;
|
|
684
|
+
/**
|
|
685
|
+
* Sign message hash using P-256 passkey
|
|
686
|
+
*
|
|
687
|
+
* @param msgHash32 - 32-byte message hash
|
|
688
|
+
* @returns Signature with r, s, and yParity
|
|
689
|
+
* @throws Error if message hash is invalid
|
|
690
|
+
*/
|
|
691
|
+
signMessage(msgHash32: Uint8Array): Promise<{
|
|
692
|
+
r: Uint8Array;
|
|
693
|
+
s: Uint8Array;
|
|
694
|
+
yParity: 0 | 1;
|
|
695
|
+
}>;
|
|
696
|
+
/**
|
|
697
|
+
* Sign raw hash without EIP-191 prefix (for EIP-7702)
|
|
698
|
+
*
|
|
699
|
+
* @param _digest - 32-byte digest to sign
|
|
700
|
+
* @returns Signature with r, s, and yParity
|
|
701
|
+
* @throws Error - P-256 cannot be used for EIP-7702
|
|
702
|
+
*/
|
|
703
|
+
signRawHash(_digest: Uint8Array): Promise<{
|
|
704
|
+
r: Uint8Array;
|
|
705
|
+
s: Uint8Array;
|
|
706
|
+
yParity: 0 | 1;
|
|
707
|
+
}>;
|
|
708
|
+
/**
|
|
709
|
+
* Sign EIP-712 typed data
|
|
710
|
+
*/
|
|
711
|
+
signTyped(data: {
|
|
712
|
+
domain: any;
|
|
713
|
+
types: any;
|
|
714
|
+
primaryType: string;
|
|
715
|
+
message: any;
|
|
716
|
+
}): Promise<`0x${string}`>;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* EIP-1193 Provider interface
|
|
721
|
+
* https://eips.ethereum.org/EIPS/eip-1193
|
|
722
|
+
*/
|
|
723
|
+
interface EIP1193Provider {
|
|
724
|
+
request(args: {
|
|
725
|
+
method: string;
|
|
726
|
+
params?: unknown[] | object;
|
|
727
|
+
}): Promise<unknown>;
|
|
728
|
+
on?(event: string, listener: (...args: any[]) => void): void;
|
|
729
|
+
removeListener?(event: string, listener: (...args: any[]) => void): void;
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* External Wallet Signer
|
|
733
|
+
*
|
|
734
|
+
* Uses an external wallet (MetaMask, WalletConnect, etc.) via EIP-1193 provider
|
|
735
|
+
* for signing operations. This signer performs network validation before each
|
|
736
|
+
* signing operation to prevent cross-chain replay attacks.
|
|
737
|
+
*
|
|
738
|
+
* Security features:
|
|
739
|
+
* - Network validation before signing
|
|
740
|
+
* - Error normalization for consistent error handling
|
|
741
|
+
* - Support for EIP-712 typed data signing
|
|
742
|
+
*
|
|
743
|
+
* @example
|
|
744
|
+
* ```ts
|
|
745
|
+
* const provider = window.ethereum;
|
|
746
|
+
* const signer = new ExternalWalletSigner(provider, 1, '0xAbC...');
|
|
747
|
+
* const sig = await signer.signMessage(msgHash);
|
|
748
|
+
* ```
|
|
749
|
+
*/
|
|
750
|
+
declare class ExternalWalletSigner implements SignerPort {
|
|
751
|
+
private readonly provider;
|
|
752
|
+
private readonly expectedChainId;
|
|
753
|
+
private readonly address;
|
|
754
|
+
constructor(provider: EIP1193Provider, expectedChainId: number, address: string);
|
|
755
|
+
/**
|
|
756
|
+
* Verify that the wallet is on the correct network
|
|
757
|
+
* @throws WrongNetworkError if network mismatch
|
|
758
|
+
*/
|
|
759
|
+
private verifyNetwork;
|
|
760
|
+
/**
|
|
761
|
+
* Get the wallet address
|
|
762
|
+
*/
|
|
763
|
+
getAddress(): Promise<`0x${string}`>;
|
|
764
|
+
/**
|
|
765
|
+
* Get public key - not directly supported by EIP-1193
|
|
766
|
+
* Returns a placeholder as external wallets don't expose raw public keys
|
|
767
|
+
*
|
|
768
|
+
* For external wallets, we rely on address-based verification instead
|
|
769
|
+
*/
|
|
770
|
+
getPublicKey(): Promise<Uint8Array>;
|
|
771
|
+
/**
|
|
772
|
+
* Sign a message hash using personal_sign
|
|
773
|
+
*
|
|
774
|
+
* Note: personal_sign automatically adds EIP-191 prefix
|
|
775
|
+
*
|
|
776
|
+
* @param msgHash32 - 32-byte message hash
|
|
777
|
+
* @returns Signature components (r, s, yParity)
|
|
778
|
+
*/
|
|
779
|
+
signMessage(msgHash32: Uint8Array): Promise<{
|
|
780
|
+
r: Uint8Array;
|
|
781
|
+
s: Uint8Array;
|
|
782
|
+
yParity: 0 | 1;
|
|
783
|
+
}>;
|
|
784
|
+
/**
|
|
785
|
+
* Sign raw hash for EIP-7702 authorization
|
|
786
|
+
*
|
|
787
|
+
* External wallets don't support raw hash signing directly (eth_sign is deprecated/disabled).
|
|
788
|
+
* Instead, we use personal_sign which adds EIP-191 prefix, then we need to account for this
|
|
789
|
+
* when verifying the signature on-chain.
|
|
790
|
+
*
|
|
791
|
+
* ⚠️ IMPORTANT: This creates a signature over the EIP-191 prefixed message, NOT the raw digest.
|
|
792
|
+
* The backend/contract must verify accordingly.
|
|
793
|
+
*
|
|
794
|
+
* Alternative approach: Use EIP-712 typed data for authorization (more wallet-friendly)
|
|
795
|
+
*
|
|
796
|
+
* @param digest - 32-byte digest to sign
|
|
797
|
+
* @returns Signature components (r, s, yParity)
|
|
798
|
+
* @throws WalletError if signing fails
|
|
799
|
+
*/
|
|
800
|
+
signRawHash(digest: Uint8Array): Promise<{
|
|
801
|
+
r: Uint8Array;
|
|
802
|
+
s: Uint8Array;
|
|
803
|
+
yParity: 0 | 1;
|
|
804
|
+
}>;
|
|
805
|
+
/**
|
|
806
|
+
* Sign typed data (EIP-712)
|
|
807
|
+
*
|
|
808
|
+
* @param typed - Typed data structure with domain, types, and message
|
|
809
|
+
* @returns Signature hex string
|
|
810
|
+
*/
|
|
811
|
+
signTyped(typed: unknown): Promise<`0x${string}`>;
|
|
812
|
+
/**
|
|
813
|
+
* Switch to the expected network
|
|
814
|
+
*
|
|
815
|
+
* @returns true if switch successful, false if user rejected
|
|
816
|
+
* @throws Error if switch fails for other reasons
|
|
817
|
+
*/
|
|
818
|
+
switchNetwork(): Promise<boolean>;
|
|
819
|
+
/**
|
|
820
|
+
* Add a new network to the wallet
|
|
821
|
+
*
|
|
822
|
+
* @param chainConfig - Network configuration
|
|
823
|
+
* @returns true if add successful, false if user rejected
|
|
824
|
+
*/
|
|
825
|
+
addNetwork(chainConfig: {
|
|
826
|
+
chainId: string;
|
|
827
|
+
chainName: string;
|
|
828
|
+
nativeCurrency: {
|
|
829
|
+
name: string;
|
|
830
|
+
symbol: string;
|
|
831
|
+
decimals: number;
|
|
832
|
+
};
|
|
833
|
+
rpcUrls: string[];
|
|
834
|
+
blockExplorerUrls?: string[];
|
|
835
|
+
}): Promise<boolean>;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* EIP-1193 Provider Error Codes
|
|
840
|
+
* https://eips.ethereum.org/EIPS/eip-1193#provider-errors
|
|
841
|
+
*/
|
|
842
|
+
declare enum EIP1193ErrorCode {
|
|
843
|
+
USER_REJECTED = 4001,
|
|
844
|
+
UNAUTHORIZED = 4100,
|
|
845
|
+
UNSUPPORTED_METHOD = 4200,
|
|
846
|
+
DISCONNECTED = 4900,
|
|
847
|
+
CHAIN_DISCONNECTED = 4901,
|
|
848
|
+
UNRECOGNIZED_CHAIN = 4902,
|
|
849
|
+
RESOURCE_UNAVAILABLE = -32002,
|
|
850
|
+
RESOURCE_NOT_FOUND = -32001,
|
|
851
|
+
INVALID_INPUT = -32000,
|
|
852
|
+
INTERNAL_ERROR = -32603,
|
|
853
|
+
INVALID_REQUEST = -32600,
|
|
854
|
+
METHOD_NOT_FOUND = -32601,
|
|
855
|
+
INVALID_PARAMS = -32602,
|
|
856
|
+
PARSE_ERROR = -32700
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* Normalized wallet error types
|
|
860
|
+
*/
|
|
861
|
+
declare class WalletError extends Error {
|
|
862
|
+
readonly code: EIP1193ErrorCode | number;
|
|
863
|
+
readonly originalError?: unknown | undefined;
|
|
864
|
+
constructor(message: string, code: EIP1193ErrorCode | number, originalError?: unknown | undefined);
|
|
865
|
+
}
|
|
866
|
+
declare class UserRejectedError extends WalletError {
|
|
867
|
+
constructor(message?: string, originalError?: unknown);
|
|
868
|
+
}
|
|
869
|
+
declare class UnauthorizedError extends WalletError {
|
|
870
|
+
constructor(message?: string, originalError?: unknown);
|
|
871
|
+
}
|
|
872
|
+
declare class UnsupportedMethodError extends WalletError {
|
|
873
|
+
constructor(message?: string, originalError?: unknown);
|
|
874
|
+
}
|
|
875
|
+
declare class WrongNetworkError extends WalletError {
|
|
876
|
+
readonly expectedChainId?: number | undefined;
|
|
877
|
+
readonly actualChainId?: number | undefined;
|
|
878
|
+
constructor(message?: string, expectedChainId?: number | undefined, actualChainId?: number | undefined, originalError?: unknown);
|
|
879
|
+
}
|
|
880
|
+
declare class ChainNotAddedError extends WalletError {
|
|
881
|
+
constructor(message?: string, originalError?: unknown);
|
|
882
|
+
}
|
|
883
|
+
declare class RequestPendingError extends WalletError {
|
|
884
|
+
constructor(message?: string, originalError?: unknown);
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Normalize EIP-1193 provider errors to typed error classes
|
|
888
|
+
*/
|
|
889
|
+
declare function normalizeWalletError(error: unknown): WalletError;
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* Cross‑platform helper to upload an encrypted blob via backend (backend handles S3 upload).
|
|
893
|
+
* Uses axios for consistent error handling and request/response interceptors.
|
|
894
|
+
*/
|
|
895
|
+
|
|
896
|
+
interface UploadBlobOptions {
|
|
897
|
+
/** Backend base URL, e.g. https://api.example.com */
|
|
898
|
+
baseUrl: string;
|
|
899
|
+
/** Project API key to send as X-API-Key header */
|
|
900
|
+
apiKey: string;
|
|
901
|
+
/** Bearer token for Authorization header */
|
|
902
|
+
accessToken: string;
|
|
903
|
+
/** Blob or binary content to upload */
|
|
904
|
+
blob: Blob | ArrayBuffer | Uint8Array;
|
|
905
|
+
/** Optional axios instance (if you want to reuse an existing instance with interceptors) */
|
|
906
|
+
axiosInstance?: AxiosInstance;
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Uploads blob to backend, which handles S3 upload directly.
|
|
910
|
+
* Returns `{ key: string }` (S3 key) on success.
|
|
911
|
+
*
|
|
912
|
+
* This helper intentionally lives in @volr/sdk-core (not UI, not backend) so it can be reused
|
|
913
|
+
* by web, React Native, Node, etc. It uses axios for consistent error handling.
|
|
914
|
+
*/
|
|
915
|
+
declare function uploadBlob(options: UploadBlobOptions): Promise<{
|
|
916
|
+
key: string;
|
|
917
|
+
}>;
|
|
918
|
+
/**
|
|
919
|
+
* Cross‑platform helper to upload an encrypted blob via S3 presigned PUT.
|
|
920
|
+
* Runs in browsers (uses global fetch) and in Node 18+ (built‑in fetch).
|
|
921
|
+
*/
|
|
922
|
+
interface UploadViaPresignOptions {
|
|
923
|
+
/** Backend base URL, e.g. https://api.example.com */
|
|
924
|
+
baseUrl: string;
|
|
925
|
+
/** Project API key to send as X-API-Key header (optional if backend not requiring it) */
|
|
926
|
+
apiKey?: string | null;
|
|
927
|
+
/** Bearer token for Authorization header (optional if cookie-based auth is sufficient) */
|
|
928
|
+
accessToken?: string | null;
|
|
929
|
+
/** Blob or binary content to upload */
|
|
930
|
+
blob: Blob | ArrayBuffer | Uint8Array;
|
|
931
|
+
/** MIME type for the uploaded object; defaults to application/octet-stream */
|
|
932
|
+
contentType?: string;
|
|
933
|
+
/** Optional fetch implementation override (e.g. pass global fetch in RN) */
|
|
934
|
+
fetchFn?: typeof fetch;
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Calls backend `/blob/presign` with `{ op:'put', contentType }`, then uploads `blob`
|
|
938
|
+
* to the returned S3 presigned URL. Returns `{ s3Key, url }` on success.
|
|
939
|
+
*
|
|
940
|
+
* This helper intentionally lives in @volr/sdk-core (not UI, not backend) so it can be reused
|
|
941
|
+
* by web, React Native, Node, etc. It uses fetch instead of axios to avoid extra deps.
|
|
942
|
+
*/
|
|
943
|
+
declare function uploadBlobViaPresign(options: UploadViaPresignOptions): Promise<{
|
|
944
|
+
s3Key: string;
|
|
945
|
+
url: string;
|
|
946
|
+
}>;
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* Base error class for Volr SDK
|
|
950
|
+
*/
|
|
951
|
+
declare class VolrError extends Error {
|
|
952
|
+
readonly code: string;
|
|
953
|
+
readonly cause?: Error | undefined;
|
|
954
|
+
constructor(code: string, message: string, cause?: Error | undefined);
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Error codes
|
|
958
|
+
*/
|
|
959
|
+
declare const ERR_INVALID_PARAM = "ERR_INVALID_PARAM";
|
|
960
|
+
declare const ERR_CRYPTO_FAIL = "ERR_CRYPTO_FAIL";
|
|
961
|
+
declare const ERR_AAD_MISMATCH = "ERR_AAD_MISMATCH";
|
|
962
|
+
declare const ERR_NONCE_SIZE = "ERR_NONCE_SIZE";
|
|
963
|
+
declare const ERR_LOW_ENTROPY = "ERR_LOW_ENTROPY";
|
|
964
|
+
declare const ERR_CHAIN_MISMATCH = "ERR_CHAIN_MISMATCH";
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* EVM constants
|
|
968
|
+
*/
|
|
969
|
+
/**
|
|
970
|
+
* Zero hash (32 bytes of zeros)
|
|
971
|
+
* Equivalent to ethers.ZeroHash or keccak256("")
|
|
972
|
+
*/
|
|
973
|
+
declare const ZERO_HASH: `0x${string}`;
|
|
974
|
+
/**
|
|
975
|
+
* Zero address (20 bytes of zeros)
|
|
976
|
+
*/
|
|
977
|
+
declare const ZERO_ADDRESS: `0x${string}`;
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Public types for EVM chain operations
|
|
981
|
+
* These types match backend DTOs 1:1 for consistency
|
|
982
|
+
*/
|
|
983
|
+
/**
|
|
984
|
+
* Session authorization data
|
|
985
|
+
*/
|
|
986
|
+
type SessionAuth = {
|
|
987
|
+
chainId: number;
|
|
988
|
+
sessionKey: `0x${string}`;
|
|
989
|
+
sessionId: bigint;
|
|
990
|
+
expiresAt: number;
|
|
991
|
+
nonce: bigint;
|
|
992
|
+
policyId: `0x${string}`;
|
|
993
|
+
policySnapshotHash: `0x${string}`;
|
|
994
|
+
gasLimitMax: bigint;
|
|
995
|
+
maxFeePerGas: bigint;
|
|
996
|
+
maxPriorityFeePerGas: bigint;
|
|
997
|
+
totalGasCap: bigint;
|
|
998
|
+
};
|
|
999
|
+
/**
|
|
1000
|
+
* Call structure for batch execution
|
|
1001
|
+
*/
|
|
1002
|
+
type Call = {
|
|
1003
|
+
target: `0x${string}`;
|
|
1004
|
+
data: `0x${string}`;
|
|
1005
|
+
value: bigint;
|
|
1006
|
+
gasLimit: bigint;
|
|
1007
|
+
};
|
|
1008
|
+
/**
|
|
1009
|
+
* Precheck input (matches PrecheckDto)
|
|
1010
|
+
*/
|
|
1011
|
+
type PrecheckInput = {
|
|
1012
|
+
auth: SessionAuth;
|
|
1013
|
+
calls: Call[];
|
|
1014
|
+
};
|
|
1015
|
+
/**
|
|
1016
|
+
* Precheck quote response
|
|
1017
|
+
*/
|
|
1018
|
+
type PrecheckQuote = {
|
|
1019
|
+
maxGas: bigint;
|
|
1020
|
+
chainId: number;
|
|
1021
|
+
currentOpNonce?: string;
|
|
1022
|
+
gasCost?: bigint;
|
|
1023
|
+
fee?: bigint;
|
|
1024
|
+
policyId: `0x${string}`;
|
|
1025
|
+
policySnapshotHash: `0x${string}`;
|
|
1026
|
+
};
|
|
1027
|
+
/**
|
|
1028
|
+
* Authorization tuple for EIP-7702
|
|
1029
|
+
*/
|
|
1030
|
+
type AuthorizationTuple = {
|
|
1031
|
+
chainId: number;
|
|
1032
|
+
address: `0x${string}`;
|
|
1033
|
+
nonce: bigint;
|
|
1034
|
+
yParity: 0 | 1;
|
|
1035
|
+
r: `0x${string}`;
|
|
1036
|
+
s: `0x${string}`;
|
|
1037
|
+
};
|
|
1038
|
+
/**
|
|
1039
|
+
* Relay input (matches RelayDto)
|
|
1040
|
+
*/
|
|
1041
|
+
type RelayInput = {
|
|
1042
|
+
chainId: number;
|
|
1043
|
+
from: `0x${string}`;
|
|
1044
|
+
auth: SessionAuth;
|
|
1045
|
+
calls: Call[];
|
|
1046
|
+
sessionSig: `0x${string}`;
|
|
1047
|
+
authorizationList: [AuthorizationTuple] | [];
|
|
1048
|
+
};
|
|
1049
|
+
/**
|
|
1050
|
+
* Relay result
|
|
1051
|
+
*/
|
|
1052
|
+
type RelayResult = {
|
|
1053
|
+
txId: string;
|
|
1054
|
+
status: 'QUEUED' | 'PENDING';
|
|
1055
|
+
txHash?: `0x${string}`;
|
|
1056
|
+
} | {
|
|
1057
|
+
txId: string;
|
|
1058
|
+
status: 'CONFIRMED';
|
|
1059
|
+
txHash: `0x${string}`;
|
|
1060
|
+
} | {
|
|
1061
|
+
txId: string;
|
|
1062
|
+
status: 'FAILED';
|
|
1063
|
+
txHash?: `0x${string}`;
|
|
1064
|
+
};
|
|
1065
|
+
|
|
1066
|
+
declare function computeCallsHash(calls: Call[]): `0x${string}`;
|
|
1067
|
+
|
|
1068
|
+
declare const DOMAIN: (chainId: number, verifyingContract: `0x${string}`) => {
|
|
1069
|
+
name: string;
|
|
1070
|
+
version: string;
|
|
1071
|
+
chainId: number;
|
|
1072
|
+
verifyingContract: `0x${string}`;
|
|
1073
|
+
};
|
|
1074
|
+
declare const EIP712_DOMAIN: (chainId: number, verifyingContract: `0x${string}`) => {
|
|
1075
|
+
name: string;
|
|
1076
|
+
version: string;
|
|
1077
|
+
chainId: number;
|
|
1078
|
+
verifyingContract: `0x${string}`;
|
|
1079
|
+
};
|
|
1080
|
+
declare const TYPES: {
|
|
1081
|
+
readonly Call: readonly [{
|
|
1082
|
+
readonly name: "target";
|
|
1083
|
+
readonly type: "address";
|
|
1084
|
+
}, {
|
|
1085
|
+
readonly name: "data";
|
|
1086
|
+
readonly type: "bytes";
|
|
1087
|
+
}, {
|
|
1088
|
+
readonly name: "value";
|
|
1089
|
+
readonly type: "uint256";
|
|
1090
|
+
}, {
|
|
1091
|
+
readonly name: "gasLimit";
|
|
1092
|
+
readonly type: "uint256";
|
|
1093
|
+
}];
|
|
1094
|
+
readonly SessionAuth: readonly [{
|
|
1095
|
+
readonly name: "chainId";
|
|
1096
|
+
readonly type: "uint256";
|
|
1097
|
+
}, {
|
|
1098
|
+
readonly name: "sessionKey";
|
|
1099
|
+
readonly type: "address";
|
|
1100
|
+
}, {
|
|
1101
|
+
readonly name: "sessionId";
|
|
1102
|
+
readonly type: "uint64";
|
|
1103
|
+
}, {
|
|
1104
|
+
readonly name: "nonce";
|
|
1105
|
+
readonly type: "uint64";
|
|
1106
|
+
}, {
|
|
1107
|
+
readonly name: "expiresAt";
|
|
1108
|
+
readonly type: "uint64";
|
|
1109
|
+
}, {
|
|
1110
|
+
readonly name: "policyId";
|
|
1111
|
+
readonly type: "bytes32";
|
|
1112
|
+
}, {
|
|
1113
|
+
readonly name: "policySnapshotHash";
|
|
1114
|
+
readonly type: "bytes32";
|
|
1115
|
+
}, {
|
|
1116
|
+
readonly name: "gasLimitMax";
|
|
1117
|
+
readonly type: "uint256";
|
|
1118
|
+
}, {
|
|
1119
|
+
readonly name: "maxFeePerGas";
|
|
1120
|
+
readonly type: "uint256";
|
|
1121
|
+
}, {
|
|
1122
|
+
readonly name: "maxPriorityFeePerGas";
|
|
1123
|
+
readonly type: "uint256";
|
|
1124
|
+
}, {
|
|
1125
|
+
readonly name: "totalGasCap";
|
|
1126
|
+
readonly type: "uint256";
|
|
1127
|
+
}];
|
|
1128
|
+
readonly SignedBatch: readonly [{
|
|
1129
|
+
readonly name: "auth";
|
|
1130
|
+
readonly type: "SessionAuth";
|
|
1131
|
+
}, {
|
|
1132
|
+
readonly name: "calls";
|
|
1133
|
+
readonly type: "Call[]";
|
|
1134
|
+
}, {
|
|
1135
|
+
readonly name: "revertOnFail";
|
|
1136
|
+
readonly type: "bool";
|
|
1137
|
+
}, {
|
|
1138
|
+
readonly name: "callsHash";
|
|
1139
|
+
readonly type: "bytes32";
|
|
1140
|
+
}];
|
|
1141
|
+
};
|
|
1142
|
+
type SessionAuthExtended = SessionAuth;
|
|
1143
|
+
/**
|
|
1144
|
+
* Build EIP-712 signed batch message
|
|
1145
|
+
*/
|
|
1146
|
+
declare function buildSignedBatchMessage(input: {
|
|
1147
|
+
auth: SessionAuthExtended;
|
|
1148
|
+
calls: Call[];
|
|
1149
|
+
revertOnFail?: boolean;
|
|
1150
|
+
invokerAddress: `0x${string}`;
|
|
1151
|
+
}): {
|
|
1152
|
+
domain: ReturnType<typeof DOMAIN>;
|
|
1153
|
+
primaryType: "SignedBatch";
|
|
1154
|
+
message: {
|
|
1155
|
+
auth: any;
|
|
1156
|
+
calls: Call[];
|
|
1157
|
+
revertOnFail: boolean;
|
|
1158
|
+
callsHash: `0x${string}`;
|
|
1159
|
+
};
|
|
1160
|
+
callsHash: `0x${string}`;
|
|
1161
|
+
};
|
|
1162
|
+
/**
|
|
1163
|
+
* Sign EIP-712 session
|
|
1164
|
+
*/
|
|
1165
|
+
declare function signSession(input: {
|
|
1166
|
+
signer: SignerPort;
|
|
1167
|
+
from: `0x${string}`;
|
|
1168
|
+
auth: SessionAuthExtended;
|
|
1169
|
+
calls: Call[];
|
|
1170
|
+
invokerAddress: `0x${string}`;
|
|
1171
|
+
}): Promise<{
|
|
1172
|
+
sessionSig: `0x${string}`;
|
|
1173
|
+
callsHash: `0x${string}`;
|
|
1174
|
+
}>;
|
|
1175
|
+
|
|
1176
|
+
/**
|
|
1177
|
+
* EIP-7702 authorization tuple signing
|
|
1178
|
+
*/
|
|
1179
|
+
|
|
1180
|
+
/**
|
|
1181
|
+
* Sign EIP-7702 authorization tuple
|
|
1182
|
+
*
|
|
1183
|
+
* Authorization tuple signs: chainId || address || nonce
|
|
1184
|
+
* This is separate from session signature
|
|
1185
|
+
*
|
|
1186
|
+
* @param input - Signing parameters
|
|
1187
|
+
* @returns Authorization tuple with signature
|
|
1188
|
+
*/
|
|
1189
|
+
declare function signAuthorization(input: {
|
|
1190
|
+
signer: SignerPort;
|
|
1191
|
+
chainId: number;
|
|
1192
|
+
address: `0x${string}`;
|
|
1193
|
+
nonce: bigint;
|
|
1194
|
+
}): Promise<AuthorizationTuple>;
|
|
1195
|
+
|
|
1196
|
+
/**
|
|
1197
|
+
* Authorization nonce utilities for EIP-7702
|
|
1198
|
+
*/
|
|
1199
|
+
/**
|
|
1200
|
+
* Relay mode for nonce calculation
|
|
1201
|
+
*/
|
|
1202
|
+
type RelayMode = 'self' | 'sponsored';
|
|
1203
|
+
/**
|
|
1204
|
+
* Get authorization nonce based on relay mode
|
|
1205
|
+
*
|
|
1206
|
+
* Rules (EIP-7702):
|
|
1207
|
+
* - self: getTransactionCount(latest) + 1
|
|
1208
|
+
* - sponsored: getTransactionCount(latest)
|
|
1209
|
+
*
|
|
1210
|
+
* Note: 'latest' is safer than 'pending' for sponsored mode to avoid race conditions.
|
|
1211
|
+
* The nonce MUST exactly match the user EOA's current account nonce for Authorization validation.
|
|
1212
|
+
*
|
|
1213
|
+
* @param client - Extended RPC client with getTransactionCount method
|
|
1214
|
+
* @param from - EOA address
|
|
1215
|
+
* @param mode - Relay mode
|
|
1216
|
+
* @returns Authorization nonce
|
|
1217
|
+
*/
|
|
1218
|
+
declare function getAuthNonce(client: ExtendedRPCClient, from: `0x${string}`, mode: RelayMode): Promise<bigint>;
|
|
1219
|
+
/**
|
|
1220
|
+
* Extended RPC client interface with transaction count method
|
|
1221
|
+
* This should be implemented by the actual RPC client in React SDK
|
|
1222
|
+
*/
|
|
1223
|
+
interface ExtendedRPCClient {
|
|
1224
|
+
/**
|
|
1225
|
+
* Low-level eth_call (optional, not required here)
|
|
1226
|
+
*/
|
|
1227
|
+
call?: (args: {
|
|
1228
|
+
to: `0x${string}`;
|
|
1229
|
+
data: `0x${string}`;
|
|
1230
|
+
}) => Promise<`0x${string}`>;
|
|
1231
|
+
/**
|
|
1232
|
+
* Get transaction count for an address
|
|
1233
|
+
*
|
|
1234
|
+
* @param address - Address to query
|
|
1235
|
+
* @param blockTag - Block tag ('pending', 'latest', etc.)
|
|
1236
|
+
* @returns Transaction count as bigint
|
|
1237
|
+
*/
|
|
1238
|
+
getTransactionCount(address: `0x${string}`, blockTag?: 'pending' | 'latest'): Promise<bigint>;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
/**
|
|
1242
|
+
* Passkey provider implementation
|
|
1243
|
+
* Uses PRF/HKDF to unwrap master seed and derive secp256k1 key
|
|
1244
|
+
*/
|
|
1245
|
+
|
|
1246
|
+
/**
|
|
1247
|
+
* Options for passkey provider
|
|
1248
|
+
*/
|
|
1249
|
+
type PasskeyProviderOptions = {
|
|
1250
|
+
/** PRF input parameters */
|
|
1251
|
+
prfInput: PrfInput;
|
|
1252
|
+
/** Encrypted master seed blob */
|
|
1253
|
+
encryptedBlob: {
|
|
1254
|
+
cipher: Uint8Array;
|
|
1255
|
+
nonce: Uint8Array;
|
|
1256
|
+
};
|
|
1257
|
+
/** Additional Authenticated Data for unwrapping */
|
|
1258
|
+
aad?: Uint8Array;
|
|
1259
|
+
};
|
|
1260
|
+
/**
|
|
1261
|
+
* Create passkey provider
|
|
1262
|
+
*
|
|
1263
|
+
* @param passkey - Passkey provider port (for WebAuthn PRF authentication)
|
|
1264
|
+
* @param options - Provider options (PRF input, encrypted blob)
|
|
1265
|
+
* @returns Wallet provider port
|
|
1266
|
+
*
|
|
1267
|
+
* @example
|
|
1268
|
+
* ```ts
|
|
1269
|
+
* const provider = createPasskeyProvider(passkeyAdapter, {
|
|
1270
|
+
* prfInput: { origin, projectId, credentialId },
|
|
1271
|
+
* encryptedBlob: { cipher, nonce }
|
|
1272
|
+
* });
|
|
1273
|
+
* await provider.ensureSession({ interactive: true, force: true });
|
|
1274
|
+
* const address = await provider.getAddress();
|
|
1275
|
+
* ```
|
|
1276
|
+
*/
|
|
1277
|
+
declare function createPasskeyProvider(passkey: PasskeyProviderPort, options: PasskeyProviderOptions): WalletProviderPort;
|
|
1278
|
+
|
|
1279
|
+
/**
|
|
1280
|
+
* MPC provider implementation
|
|
1281
|
+
* Delegates to backend proxy via transport
|
|
1282
|
+
*/
|
|
1283
|
+
|
|
1284
|
+
/**
|
|
1285
|
+
* Create MPC provider
|
|
1286
|
+
*
|
|
1287
|
+
* @param transport - MPC transport interface (backend proxy)
|
|
1288
|
+
* @returns Wallet provider port
|
|
1289
|
+
*
|
|
1290
|
+
* @example
|
|
1291
|
+
* ```ts
|
|
1292
|
+
* const transport = createMpcTransport({ backendUrl: '...' });
|
|
1293
|
+
* const provider = createMpcProvider(transport);
|
|
1294
|
+
* await provider.ensureSession();
|
|
1295
|
+
* const address = await provider.getAddress();
|
|
1296
|
+
* ```
|
|
1297
|
+
*/
|
|
1298
|
+
declare function createMpcProvider(transport: MpcTransport): WalletProviderPort;
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
* Provider-based signer selection (simplified, no 7212 probing)
|
|
1302
|
+
* Always uses secp256k1 path today.
|
|
1303
|
+
*/
|
|
1304
|
+
|
|
1305
|
+
/**
|
|
1306
|
+
* Context for signer selection
|
|
1307
|
+
*/
|
|
1308
|
+
type SelectSignerContext = {
|
|
1309
|
+
/** Wallet provider */
|
|
1310
|
+
provider: WalletProviderPort;
|
|
1311
|
+
/** Chain ID */
|
|
1312
|
+
chainId: number;
|
|
1313
|
+
};
|
|
1314
|
+
/**
|
|
1315
|
+
* Select appropriate signer based on provider
|
|
1316
|
+
*
|
|
1317
|
+
* Routing logic:
|
|
1318
|
+
* 1. Provider-backed signer (delegates to provider, secp256k1)
|
|
1319
|
+
*
|
|
1320
|
+
* Note: 7702 authorization always uses secp256k1
|
|
1321
|
+
*
|
|
1322
|
+
* @param ctx - Selection context
|
|
1323
|
+
* @returns Signer port
|
|
1324
|
+
* @throws Error if provider cannot provide required signing capability
|
|
1325
|
+
*
|
|
1326
|
+
* @example
|
|
1327
|
+
* ```ts
|
|
1328
|
+
* const signer = await selectSigner({
|
|
1329
|
+
* provider: passkeyProvider,
|
|
1330
|
+
* chainId: 10,
|
|
1331
|
+
* client: rpcClient
|
|
1332
|
+
* });
|
|
1333
|
+
* const sig = await signer.signMessage(msgHash);
|
|
1334
|
+
* ```
|
|
1335
|
+
*/
|
|
1336
|
+
declare function selectSigner(ctx: SelectSignerContext): Promise<SignerPort>;
|
|
1337
|
+
|
|
1338
|
+
export { type AesGcmParams, type AuthorizationTuple, type Call, ChainNotAddedError, DEFAULT_EVM_PATH, DOMAIN, type DeriveArgs, EIP1193ErrorCode, type EIP1193Provider, EIP712_DOMAIN, ERR_AAD_MISMATCH, ERR_CHAIN_MISMATCH, ERR_CRYPTO_FAIL, ERR_INVALID_PARAM, ERR_LOW_ENTROPY, ERR_NONCE_SIZE, type EvmKeypair, type ExtendedRPCClient, ExternalWalletSigner, type KeyStorageType, type Sig$1 as LegacySig, type MasterKeyProvider, type MasterSeed, type MasterSeedHandle, type MpcTransport, PasskeyP256Signer, type PasskeyProviderOptions, type PasskeyProviderPort, type PrecheckInput, type PrecheckQuote, type PrfInput, type RelayInput, type RelayMode, type RelayResult, RequestPendingError, type RpcLike, Secp256k1SoftwareSigner, type SelectSignerContext, type SessionAuth, type Sig, type SignerContext, type SignerKind, type SignerPort, TYPES, type TypedDataInput, UnauthorizedError, UnsupportedMethodError, type UploadBlobOptions, type UploadViaPresignOptions, UserRejectedError, VolrError, WalletError, type WalletProviderPort, type WrapKey, WrongNetworkError, ZERO_ADDRESS, ZERO_HASH, aesGcmDecrypt, aesGcmEncrypt, buildSignedBatchMessage, computeCallsHash, createMasterKeyProvider, createMpcProvider, createPasskeyProvider, deriveEvmKey, deriveWrapKey, evmSign, evmVerify, getAuthNonce, getRandomBytes, hkdfSha256, normalizeWalletError, sealMasterSeed, selectSigner, selectSigner$1 as selectSignerLegacy, signAuthorization, signSession, toChecksumAddress, unsealMasterSeed, uploadBlob, uploadBlobViaPresign, zeroize };
|