@veil-cash/sdk 0.2.0 → 0.3.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/src/keypair.ts CHANGED
@@ -4,9 +4,22 @@
4
4
  */
5
5
 
6
6
  import { ethers } from 'ethers';
7
+ import { privateKeyToAccount } from 'viem/accounts';
7
8
  import { poseidonHash, toFixedHex } from './utils.js';
8
9
  import type { EncryptedMessage } from './types.js';
9
10
 
11
+ /**
12
+ * Canonical message signed by a wallet to derive a Veil keypair.
13
+ * Must match the frontend's SIGNED_MESSAGE in data/wallet/config.ts.
14
+ */
15
+ export const VEIL_SIGNED_MESSAGE = "Sign this message to create your Veil Wallet private key. This will be used to decrypt your balances. Ensure you are signing this message on the Veil Cash website.";
16
+
17
+ /**
18
+ * Any async function that performs EIP-191 personal_sign and returns a 0x-prefixed signature.
19
+ * Used with Keypair.fromSigner() to support external signing services.
20
+ */
21
+ export type MessageSigner = (message: string) => Promise<string>;
22
+
10
23
  // eth-sig-util for x25519 encryption
11
24
  // eslint-disable-next-line @typescript-eslint/no-require-imports
12
25
  const ethSigUtil = require('eth-sig-util');
@@ -125,6 +138,72 @@ export class Keypair {
125
138
  });
126
139
  }
127
140
 
141
+ /**
142
+ * Derive a Keypair from an EIP-191 personal_sign signature.
143
+ * The private key is keccak256(signature) -- matching the frontend derivation.
144
+ *
145
+ * @param signature - Raw ECDSA signature (0x-prefixed, 132 hex chars)
146
+ * @returns Keypair derived from the signature
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * const keypair = Keypair.fromSignature(signature);
151
+ * ```
152
+ */
153
+ static fromSignature(signature: string): Keypair {
154
+ const privkey = ethers.keccak256(signature);
155
+ return new Keypair(privkey);
156
+ }
157
+
158
+ /**
159
+ * Derive a Keypair from an Ethereum wallet private key.
160
+ * Signs VEIL_SIGNED_MESSAGE with the wallet, then derives via keccak256(signature).
161
+ * Produces the same keypair as the frontend for the same wallet.
162
+ *
163
+ * @param walletPrivateKey - Ethereum EOA private key (0x-prefixed)
164
+ * @returns Promise resolving to the derived Keypair
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * const keypair = await Keypair.fromWalletKey('0xYOUR_WALLET_PRIVATE_KEY');
169
+ * console.log(keypair.depositKey()); // Same as frontend login with this wallet
170
+ * ```
171
+ */
172
+ static async fromWalletKey(walletPrivateKey: `0x${string}`): Promise<Keypair> {
173
+ const account = privateKeyToAccount(walletPrivateKey);
174
+ const signature = await account.signMessage({ message: VEIL_SIGNED_MESSAGE });
175
+ return Keypair.fromSignature(signature);
176
+ }
177
+
178
+ /**
179
+ * Derive a Keypair using any external signer that supports personal_sign (EIP-191).
180
+ * The signer function receives VEIL_SIGNED_MESSAGE and must return a 0x-prefixed signature.
181
+ * Works with any signing backend: Bankr, MPC wallets, custodial services, hardware wallets, etc.
182
+ *
183
+ * @param signer - Async function that signs a message and returns a 0x-prefixed signature
184
+ * @returns Promise resolving to the derived Keypair
185
+ *
186
+ * @example
187
+ * ```typescript
188
+ * // With Bankr
189
+ * const keypair = await Keypair.fromSigner(async (message) => {
190
+ * const res = await fetch('https://api.bankr.bot/agent/sign', {
191
+ * method: 'POST',
192
+ * headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
193
+ * body: JSON.stringify({ signatureType: 'personal_sign', message }),
194
+ * });
195
+ * return (await res.json()).signature;
196
+ * });
197
+ *
198
+ * // With any custom signer
199
+ * const keypair = await Keypair.fromSigner(async (msg) => myService.personalSign(msg));
200
+ * ```
201
+ */
202
+ static async fromSigner(signer: MessageSigner): Promise<Keypair> {
203
+ const signature = await signer(VEIL_SIGNED_MESSAGE);
204
+ return Keypair.fromSignature(signature);
205
+ }
206
+
128
207
  /**
129
208
  * Sign a message using the private key
130
209
  * @param commitment - Commitment hash
package/src/relay.ts CHANGED
@@ -98,8 +98,8 @@ export async function submitRelay(options: SubmitRelayOptions): Promise<RelayRes
98
98
  throw new RelayError('Invalid type. Must be "withdraw" or "transfer"', 400);
99
99
  }
100
100
 
101
- if (pool !== 'eth' && pool !== 'usdc') {
102
- throw new RelayError('Invalid pool. Must be "eth" or "usdc"', 400);
101
+ if (pool !== 'eth' && pool !== 'usdc' && pool !== 'cbbtc') {
102
+ throw new RelayError('Invalid pool. Must be "eth", "usdc", or "cbbtc"', 400);
103
103
  }
104
104
 
105
105
  if (!proofArgs || !extData) {
package/src/transfer.ts CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { createPublicClient, http, parseUnits } from 'viem';
7
7
  import { base } from 'viem/chains';
8
- import { getAddresses, POOL_CONFIG } from './addresses.js';
8
+ import { getAddresses, getPoolAddress, POOL_CONFIG } from './addresses.js';
9
9
  import { POOL_ABI, ENTRY_ABI } from './abi.js';
10
10
  import { Keypair } from './keypair.js';
11
11
  import { Utxo } from './utxo.js';
@@ -17,6 +17,7 @@ import type {
17
17
  BuildTransferProofOptions,
18
18
  ProofBuildResult,
19
19
  TransferResult,
20
+ RelayPool,
20
21
  } from './types.js';
21
22
 
22
23
  /**
@@ -137,13 +138,13 @@ export async function buildTransferProof(
137
138
  amount,
138
139
  recipientAddress,
139
140
  senderKeypair,
141
+ pool = 'eth',
140
142
  rpcUrl,
141
143
  onProgress,
142
144
  } = options;
143
145
 
144
- const addresses = getAddresses();
145
- const poolConfig = POOL_CONFIG.eth;
146
- const poolAddress = addresses.ethPool;
146
+ const poolConfig = POOL_CONFIG[pool];
147
+ const poolAddress = getPoolAddress(pool);
147
148
 
148
149
  // 1. Check recipient is registered and get their deposit key
149
150
  onProgress?.('Checking recipient registration...');
@@ -160,6 +161,7 @@ export async function buildTransferProof(
160
161
  onProgress?.('Fetching your UTXOs...');
161
162
  const balanceResult = await getPrivateBalance({
162
163
  keypair: senderKeypair,
164
+ pool,
163
165
  rpcUrl,
164
166
  onProgress,
165
167
  });
@@ -283,7 +285,7 @@ export async function buildTransferProof(
283
285
  export async function transfer(
284
286
  options: BuildTransferProofOptions
285
287
  ): Promise<TransferResult> {
286
- const { amount, recipientAddress, onProgress } = options;
288
+ const { amount, recipientAddress, pool = 'eth', onProgress } = options;
287
289
 
288
290
  // Build the proof
289
291
  const proof = await buildTransferProof(options);
@@ -292,7 +294,7 @@ export async function transfer(
292
294
  onProgress?.('Submitting to relay...');
293
295
  const relayResult = await submitRelay({
294
296
  type: 'transfer',
295
- pool: 'eth',
297
+ pool,
296
298
  proofArgs: proof.proofArgs,
297
299
  extData: proof.extData,
298
300
  metadata: {
@@ -334,19 +336,20 @@ export async function transfer(
334
336
  export async function mergeUtxos(options: {
335
337
  amount: string;
336
338
  keypair: Keypair;
339
+ pool?: RelayPool;
337
340
  rpcUrl?: string;
338
341
  onProgress?: (stage: string, detail?: string) => void;
339
342
  }): Promise<TransferResult> {
340
- const { amount, keypair, rpcUrl, onProgress } = options;
343
+ const { amount, keypair, pool = 'eth', rpcUrl, onProgress } = options;
341
344
 
342
- const addresses = getAddresses();
343
- const poolConfig = POOL_CONFIG.eth;
344
- const poolAddress = addresses.ethPool;
345
+ const poolConfig = POOL_CONFIG[pool];
346
+ const poolAddress = getPoolAddress(pool);
345
347
 
346
348
  // 1. Get sender's unspent UTXOs
347
349
  onProgress?.('Fetching your UTXOs...');
348
350
  const balanceResult = await getPrivateBalance({
349
351
  keypair,
352
+ pool,
350
353
  rpcUrl,
351
354
  onProgress,
352
355
  });
@@ -435,7 +438,7 @@ export async function mergeUtxos(options: {
435
438
  onProgress?.('Submitting to relay...');
436
439
  const relayResult = await submitRelay({
437
440
  type: 'transfer',
438
- pool: 'eth',
441
+ pool,
439
442
  proofArgs: {
440
443
  proof: result.args.proof,
441
444
  root: result.args.root,
package/src/types.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * Supported tokens
7
7
  */
8
- export type Token = 'ETH' | 'USDC';
8
+ export type Token = 'ETH' | 'USDC' | 'CBBTC';
9
9
 
10
10
  /**
11
11
  * Encrypted message format (x25519-xsalsa20-poly1305)
@@ -27,6 +27,9 @@ export interface NetworkAddresses {
27
27
  usdcPool: `0x${string}`;
28
28
  usdcQueue: `0x${string}`;
29
29
  usdcToken: `0x${string}`;
30
+ cbbtcPool: `0x${string}`;
31
+ cbbtcQueue: `0x${string}`;
32
+ cbbtcToken: `0x${string}`;
30
33
  chainId: number;
31
34
  relayUrl: string;
32
35
  }
@@ -118,7 +121,7 @@ export interface PrivateBalanceResult {
118
121
  /**
119
122
  * Pool type for relay operations
120
123
  */
121
- export type RelayPool = 'eth' | 'usdc';
124
+ export type RelayPool = 'eth' | 'usdc' | 'cbbtc';
122
125
 
123
126
  /**
124
127
  * Type of relay transaction
@@ -223,6 +226,8 @@ export interface BuildWithdrawProofOptions {
223
226
  recipient: `0x${string}`;
224
227
  /** User's keypair for signing */
225
228
  keypair: import('./keypair.js').Keypair;
229
+ /** Pool to withdraw from (default: 'eth') */
230
+ pool?: RelayPool;
226
231
  /** Optional RPC URL */
227
232
  rpcUrl?: string;
228
233
  /** Progress callback */
@@ -239,6 +244,8 @@ export interface BuildTransferProofOptions {
239
244
  recipientAddress: `0x${string}`;
240
245
  /** Sender's keypair */
241
246
  senderKeypair: import('./keypair.js').Keypair;
247
+ /** Pool to transfer in (default: 'eth') */
248
+ pool?: RelayPool;
242
249
  /** Optional RPC URL */
243
250
  rpcUrl?: string;
244
251
  /** Progress callback */
package/src/withdraw.ts CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { createPublicClient, http, parseUnits, formatUnits } from 'viem';
7
7
  import { base } from 'viem/chains';
8
- import { getAddresses, POOL_CONFIG } from './addresses.js';
8
+ import { getPoolAddress, POOL_CONFIG } from './addresses.js';
9
9
  import { POOL_ABI } from './abi.js';
10
10
  import { Keypair } from './keypair.js';
11
11
  import { Utxo } from './utxo.js';
@@ -143,18 +143,19 @@ export async function buildWithdrawProof(
143
143
  amount,
144
144
  recipient,
145
145
  keypair,
146
+ pool = 'eth',
146
147
  rpcUrl,
147
148
  onProgress,
148
149
  } = options;
149
150
 
150
- const addresses = getAddresses();
151
- const poolConfig = POOL_CONFIG.eth;
152
- const poolAddress = addresses.ethPool;
151
+ const poolConfig = POOL_CONFIG[pool];
152
+ const poolAddress = getPoolAddress(pool);
153
153
 
154
154
  // 1. Get user's unspent UTXOs
155
155
  onProgress?.('Fetching your UTXOs...');
156
156
  const balanceResult = await getPrivateBalance({
157
157
  keypair,
158
+ pool,
158
159
  rpcUrl,
159
160
  onProgress,
160
161
  });
@@ -269,7 +270,7 @@ export async function buildWithdrawProof(
269
270
  export async function withdraw(
270
271
  options: BuildWithdrawProofOptions
271
272
  ): Promise<WithdrawResult> {
272
- const { amount, recipient, onProgress } = options;
273
+ const { amount, recipient, pool = 'eth', onProgress } = options;
273
274
 
274
275
  // Build the proof
275
276
  const proof = await buildWithdrawProof(options);
@@ -278,7 +279,7 @@ export async function withdraw(
278
279
  onProgress?.('Submitting to relay...');
279
280
  const relayResult = await submitRelay({
280
281
  type: 'withdraw',
281
- pool: 'eth',
282
+ pool,
282
283
  proofArgs: proof.proofArgs,
283
284
  extData: proof.extData,
284
285
  metadata: {