@vaultkey/sdk 1.0.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/.eslintrc.cjs +10 -0
- package/LICENSE +21 -0
- package/README.md +337 -0
- package/dist/index.d.mts +512 -0
- package/dist/index.d.ts +512 -0
- package/dist/index.js +486 -0
- package/dist/index.mjs +454 -0
- package/examples/usage.ts +221 -0
- package/package.json +41 -0
- package/tsup.config.ts +9 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
// src/signing.ts
|
|
2
|
+
var Signing = class {
|
|
3
|
+
constructor(client, walletId) {
|
|
4
|
+
this.client = client;
|
|
5
|
+
this.walletId = walletId;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Sign an EVM message or typed data (EIP-712).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const { data } = await vaultkey.wallets.signing("wallet_id").evmMessage({
|
|
12
|
+
* payload: { message: "Hello from VaultKey" },
|
|
13
|
+
* });
|
|
14
|
+
* // Poll: await vaultkey.jobs.get(data.jobId)
|
|
15
|
+
*/
|
|
16
|
+
async evmMessage(params) {
|
|
17
|
+
const { idempotencyKey, ...rest } = params;
|
|
18
|
+
return this.client.post(
|
|
19
|
+
`/wallets/${this.walletId}/sign/message/evm`,
|
|
20
|
+
{
|
|
21
|
+
payload: rest.payload,
|
|
22
|
+
idempotency_key: idempotencyKey
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Sign a Solana message.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const { data } = await vaultkey.wallets.signing("wallet_id").solanaMessage({
|
|
31
|
+
* payload: { message: "Hello from VaultKey" },
|
|
32
|
+
* });
|
|
33
|
+
*/
|
|
34
|
+
async solanaMessage(params) {
|
|
35
|
+
const { idempotencyKey, ...rest } = params;
|
|
36
|
+
return this.client.post(
|
|
37
|
+
`/wallets/${this.walletId}/sign/message/solana`,
|
|
38
|
+
{
|
|
39
|
+
payload: rest.payload,
|
|
40
|
+
idempotency_key: idempotencyKey
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// src/wallets.ts
|
|
47
|
+
var Wallets = class {
|
|
48
|
+
constructor(client) {
|
|
49
|
+
this.client = client;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create a new wallet for a user.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* const { data, error } = await vaultkey.wallets.create({
|
|
56
|
+
* userId: "user_123",
|
|
57
|
+
* chainType: "evm",
|
|
58
|
+
* label: "Primary wallet",
|
|
59
|
+
* });
|
|
60
|
+
*/
|
|
61
|
+
async create(params) {
|
|
62
|
+
const body = {
|
|
63
|
+
user_id: params.userId,
|
|
64
|
+
chain_type: params.chainType
|
|
65
|
+
};
|
|
66
|
+
if (params.label) body.label = params.label;
|
|
67
|
+
return this.client.post("/wallets", body);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Retrieve a wallet by its ID.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* const { data } = await vaultkey.wallets.get("wallet_abc123");
|
|
74
|
+
*/
|
|
75
|
+
async get(walletId) {
|
|
76
|
+
return this.client.get(`/wallets/${walletId}`);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* List all wallets belonging to a user.
|
|
80
|
+
* Results are paginated — use `nextCursor` to fetch the next page.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* const { data } = await vaultkey.wallets.listByUser("user_123");
|
|
84
|
+
* // Next page:
|
|
85
|
+
* const { data: page2 } = await vaultkey.wallets.listByUser("user_123", {
|
|
86
|
+
* after: data.nextCursor,
|
|
87
|
+
* });
|
|
88
|
+
*/
|
|
89
|
+
async listByUser(userId, pagination) {
|
|
90
|
+
const params = new URLSearchParams();
|
|
91
|
+
if (pagination?.after) params.set("after", pagination.after);
|
|
92
|
+
if (pagination?.limit) params.set("limit", String(pagination.limit));
|
|
93
|
+
const qs = params.toString();
|
|
94
|
+
return this.client.get(
|
|
95
|
+
`/users/${userId}/wallets${qs ? `?${qs}` : ""}`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Returns a signing interface scoped to the given wallet.
|
|
100
|
+
* Use this to sign EVM or Solana messages.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* const { data } = await vaultkey.wallets
|
|
104
|
+
* .signing("wallet_id")
|
|
105
|
+
* .evmMessage({ payload: { message: "Hello" } });
|
|
106
|
+
*/
|
|
107
|
+
signing(walletId) {
|
|
108
|
+
return new Signing(this.client, walletId);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get the native token balance for an EVM wallet.
|
|
112
|
+
* Provide chainName (preferred) or chainId.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* const { data } = await vaultkey.wallets.evmBalance("wallet_id", {
|
|
116
|
+
* chainName: "base",
|
|
117
|
+
* });
|
|
118
|
+
* console.log(data.balance); // "0.05"
|
|
119
|
+
*/
|
|
120
|
+
async evmBalance(walletId, chain) {
|
|
121
|
+
const params = new URLSearchParams();
|
|
122
|
+
if ("chainName" in chain && chain.chainName) {
|
|
123
|
+
params.set("chain_name", chain.chainName);
|
|
124
|
+
} else if ("chainId" in chain && chain.chainId) {
|
|
125
|
+
params.set("chain_id", chain.chainId);
|
|
126
|
+
}
|
|
127
|
+
return this.client.get(
|
|
128
|
+
`/wallets/${walletId}/balance/evm?${params.toString()}`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get the SOL balance for a Solana wallet.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* const { data } = await vaultkey.wallets.solanaBalance("wallet_id");
|
|
136
|
+
* console.log(data.balance); // "1.5"
|
|
137
|
+
*/
|
|
138
|
+
async solanaBalance(walletId) {
|
|
139
|
+
return this.client.get(
|
|
140
|
+
`/wallets/${walletId}/balance/solana`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Broadcast a pre-signed EVM transaction.
|
|
145
|
+
* Provide chainName (preferred) or chainId.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* const { data } = await vaultkey.wallets.broadcastEVM("wallet_id", {
|
|
149
|
+
* signedTx: "0x...",
|
|
150
|
+
* chainName: "base",
|
|
151
|
+
* });
|
|
152
|
+
* console.log(data.txHash);
|
|
153
|
+
*/
|
|
154
|
+
async broadcastEVM(walletId, params) {
|
|
155
|
+
const body = { signed_tx: params.signedTx };
|
|
156
|
+
if ("chainName" in params && params.chainName) {
|
|
157
|
+
body.chain_name = params.chainName;
|
|
158
|
+
} else if ("chainId" in params && params.chainId) {
|
|
159
|
+
body.chain_id = params.chainId;
|
|
160
|
+
}
|
|
161
|
+
return this.client.post(
|
|
162
|
+
`/wallets/${walletId}/broadcast`,
|
|
163
|
+
body
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Broadcast a pre-signed Solana transaction.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* const { data } = await vaultkey.wallets.broadcastSolana("wallet_id", {
|
|
171
|
+
* signedTx: "base58encodedtx...",
|
|
172
|
+
* });
|
|
173
|
+
* console.log(data.signature);
|
|
174
|
+
*/
|
|
175
|
+
async broadcastSolana(walletId, params) {
|
|
176
|
+
return this.client.post(
|
|
177
|
+
`/wallets/${walletId}/broadcast`,
|
|
178
|
+
{ signed_tx: params.signedTx }
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Trigger a sweep — move all funds from this wallet to the configured
|
|
183
|
+
* master wallet. The operation is asynchronous; poll via `vaultkey.jobs.get`.
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* // EVM sweep
|
|
187
|
+
* const { data } = await vaultkey.wallets.sweep("wallet_id", {
|
|
188
|
+
* chainType: "evm",
|
|
189
|
+
* chainName: "base",
|
|
190
|
+
* });
|
|
191
|
+
*
|
|
192
|
+
* // Solana sweep
|
|
193
|
+
* const { data } = await vaultkey.wallets.sweep("wallet_id", {
|
|
194
|
+
* chainType: "solana",
|
|
195
|
+
* });
|
|
196
|
+
*/
|
|
197
|
+
async sweep(walletId, params) {
|
|
198
|
+
const body = {
|
|
199
|
+
chain_type: params.chainType
|
|
200
|
+
};
|
|
201
|
+
if (params.chainType === "evm") {
|
|
202
|
+
if (params.chainName) {
|
|
203
|
+
body.chain_name = params.chainName;
|
|
204
|
+
} else if (params.chainId) {
|
|
205
|
+
body.chain_id = params.chainId;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return this.client.post(`/wallets/${walletId}/sweep`, body);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// src/jobs.ts
|
|
213
|
+
var Jobs = class {
|
|
214
|
+
constructor(client) {
|
|
215
|
+
this.client = client;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Retrieve the current state of an async job.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* const { data, error } = await vaultkey.jobs.get("job_abc123");
|
|
222
|
+
* if (data?.status === "completed") {
|
|
223
|
+
* console.log("Done!", data.result);
|
|
224
|
+
* }
|
|
225
|
+
*/
|
|
226
|
+
async get(jobId) {
|
|
227
|
+
return this.client.get(`/jobs/${jobId}`);
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// src/stablecoin.ts
|
|
232
|
+
var Stablecoin = class {
|
|
233
|
+
constructor(client) {
|
|
234
|
+
this.client = client;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Transfer a stablecoin from a wallet.
|
|
238
|
+
*
|
|
239
|
+
* - For EVM: provide chainName (preferred) or chainId.
|
|
240
|
+
* - For Solana: omit chainName and chainId.
|
|
241
|
+
* - The operation is async — poll `vaultkey.jobs.get(data.jobId)`.
|
|
242
|
+
* - Use `idempotencyKey` to safely retry without double-sending.
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* // EVM (gasless)
|
|
246
|
+
* const { data } = await vaultkey.stablecoin.transfer("wallet_id", {
|
|
247
|
+
* token: "usdc",
|
|
248
|
+
* to: "0xRecipient",
|
|
249
|
+
* amount: "100.00",
|
|
250
|
+
* chainType: "evm",
|
|
251
|
+
* chainName: "base",
|
|
252
|
+
* gasless: true,
|
|
253
|
+
* speed: "fast",
|
|
254
|
+
* });
|
|
255
|
+
*
|
|
256
|
+
* // Solana
|
|
257
|
+
* const { data } = await vaultkey.stablecoin.transfer("wallet_id", {
|
|
258
|
+
* token: "usdc",
|
|
259
|
+
* to: "RecipientBase58",
|
|
260
|
+
* amount: "100.00",
|
|
261
|
+
* chainType: "solana",
|
|
262
|
+
* });
|
|
263
|
+
*/
|
|
264
|
+
async transfer(walletId, params) {
|
|
265
|
+
const body = {
|
|
266
|
+
token: params.token,
|
|
267
|
+
to: params.to,
|
|
268
|
+
amount: params.amount
|
|
269
|
+
};
|
|
270
|
+
if (params.chainType === "evm") {
|
|
271
|
+
if (params.chainName) {
|
|
272
|
+
body.chain_name = params.chainName;
|
|
273
|
+
} else if (params.chainId) {
|
|
274
|
+
body.chain_id = params.chainId;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (params.gasless !== void 0) body.gasless = params.gasless;
|
|
278
|
+
if (params.speed) body.speed = params.speed;
|
|
279
|
+
if (params.idempotencyKey) body.idempotency_key = params.idempotencyKey;
|
|
280
|
+
return this.client.post(
|
|
281
|
+
`/wallets/${walletId}/stablecoin/transfer/${params.chainType}`,
|
|
282
|
+
body
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Get the stablecoin balance for a wallet.
|
|
287
|
+
*
|
|
288
|
+
* - For EVM: provide chainName (preferred) or chainId.
|
|
289
|
+
* - For Solana: omit chainName and chainId.
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* const { data } = await vaultkey.stablecoin.balance("wallet_id", {
|
|
293
|
+
* token: "usdc",
|
|
294
|
+
* chainType: "evm",
|
|
295
|
+
* chainName: "polygon",
|
|
296
|
+
* });
|
|
297
|
+
* console.log(data.balance); // "50.00"
|
|
298
|
+
*/
|
|
299
|
+
async balance(walletId, params) {
|
|
300
|
+
const query = { token: params.token };
|
|
301
|
+
if (params.chainType === "evm") {
|
|
302
|
+
if (params.chainName) {
|
|
303
|
+
query.chain_name = params.chainName;
|
|
304
|
+
} else if (params.chainId) {
|
|
305
|
+
query.chain_id = params.chainId;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const qs = new URLSearchParams(
|
|
309
|
+
Object.entries(query).filter(([, v]) => v !== void 0)
|
|
310
|
+
).toString();
|
|
311
|
+
return this.client.get(
|
|
312
|
+
`/wallets/${walletId}/stablecoin/balance/${params.chainType}?${qs}`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// src/chains.ts
|
|
318
|
+
var Chains = class {
|
|
319
|
+
constructor(client) {
|
|
320
|
+
this.client = client;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* List all supported EVM chains for the current environment
|
|
324
|
+
* (testnet or mainnet, determined by your API key).
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* const { data, error } = await vaultkey.chains.list();
|
|
328
|
+
*/
|
|
329
|
+
async list() {
|
|
330
|
+
return this.client.get("/chains");
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// src/vaultkey.ts
|
|
335
|
+
var MAINNET_BASE_URL = "https://app.vaultkeys.com";
|
|
336
|
+
var TESTNET_BASE_URL = "https://testnet.vaultkeys.com";
|
|
337
|
+
function resolveBaseUrl(apiKey, override) {
|
|
338
|
+
if (override) return override;
|
|
339
|
+
return apiKey.startsWith("testnet_") ? TESTNET_BASE_URL : MAINNET_BASE_URL;
|
|
340
|
+
}
|
|
341
|
+
function isVaultKeyErrorResponse(resp) {
|
|
342
|
+
return typeof resp === "object" && resp !== null && "error" in resp && typeof resp.error === "object";
|
|
343
|
+
}
|
|
344
|
+
var VaultKey = class {
|
|
345
|
+
apiKey;
|
|
346
|
+
apiSecret;
|
|
347
|
+
baseUrl;
|
|
348
|
+
/** Wallet management — create, retrieve, and list wallets. */
|
|
349
|
+
wallets;
|
|
350
|
+
/** Async job polling — check the status of signing and sweep operations. */
|
|
351
|
+
jobs;
|
|
352
|
+
/** Stablecoin transfers and balance lookups (USDC, USDT). */
|
|
353
|
+
stablecoin;
|
|
354
|
+
/** Chain discovery — list supported EVM chains for the current environment. */
|
|
355
|
+
chains;
|
|
356
|
+
constructor(config = {}) {
|
|
357
|
+
const apiKey = config.apiKey ?? (typeof process !== "undefined" ? process.env?.VAULTKEY_API_KEY : void 0);
|
|
358
|
+
const apiSecret = config.apiSecret ?? (typeof process !== "undefined" ? process.env?.VAULTKEY_API_SECRET : void 0);
|
|
359
|
+
if (!apiKey) {
|
|
360
|
+
throw new Error(
|
|
361
|
+
'Missing API key. Pass it via config: new VaultKey({ apiKey: "vk_live_..." }) or set the VAULTKEY_API_KEY environment variable.'
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
if (!apiSecret) {
|
|
365
|
+
throw new Error(
|
|
366
|
+
'Missing API secret. Pass it via config: new VaultKey({ apiSecret: "..." }) or set the VAULTKEY_API_SECRET environment variable.'
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
this.apiKey = apiKey;
|
|
370
|
+
this.apiSecret = apiSecret;
|
|
371
|
+
this.baseUrl = `${resolveBaseUrl(apiKey, config.baseUrl)}/api/v1/sdk`;
|
|
372
|
+
this.wallets = new Wallets(this);
|
|
373
|
+
this.jobs = new Jobs(this);
|
|
374
|
+
this.stablecoin = new Stablecoin(this);
|
|
375
|
+
this.chains = new Chains(this);
|
|
376
|
+
}
|
|
377
|
+
async fetchRequest(path, options = {}) {
|
|
378
|
+
const headers = new Headers({
|
|
379
|
+
"Content-Type": "application/json",
|
|
380
|
+
"X-API-Key": this.apiKey,
|
|
381
|
+
"X-API-Secret": this.apiSecret,
|
|
382
|
+
...Object.fromEntries(new Headers(options.headers ?? {}))
|
|
383
|
+
});
|
|
384
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
385
|
+
...options,
|
|
386
|
+
headers
|
|
387
|
+
});
|
|
388
|
+
const defaultError = {
|
|
389
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
390
|
+
message: response.statusText
|
|
391
|
+
};
|
|
392
|
+
if (!response.ok) {
|
|
393
|
+
try {
|
|
394
|
+
const body = await response.json();
|
|
395
|
+
if (isVaultKeyErrorResponse(body)) {
|
|
396
|
+
return { data: null, error: body.error };
|
|
397
|
+
}
|
|
398
|
+
if (typeof body?.error === "string") {
|
|
399
|
+
return {
|
|
400
|
+
data: null,
|
|
401
|
+
error: { code: String(response.status), message: body.error }
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
return { data: null, error: defaultError };
|
|
405
|
+
} catch {
|
|
406
|
+
return { data: null, error: defaultError };
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
const data = await response.json();
|
|
410
|
+
return { data, error: null };
|
|
411
|
+
}
|
|
412
|
+
async get(path, options) {
|
|
413
|
+
return this.fetchRequest(path, {
|
|
414
|
+
method: "GET",
|
|
415
|
+
headers: options?.headers
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
async post(path, body, options) {
|
|
419
|
+
return this.fetchRequest(path, {
|
|
420
|
+
method: "POST",
|
|
421
|
+
body: JSON.stringify(body),
|
|
422
|
+
headers: options?.headers
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
async put(path, body, options) {
|
|
426
|
+
return this.fetchRequest(path, {
|
|
427
|
+
method: "PUT",
|
|
428
|
+
body: JSON.stringify(body),
|
|
429
|
+
headers: options?.headers
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
async patch(path, body, options) {
|
|
433
|
+
return this.fetchRequest(path, {
|
|
434
|
+
method: "PATCH",
|
|
435
|
+
body: JSON.stringify(body),
|
|
436
|
+
headers: options?.headers
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
async delete(path, body, options) {
|
|
440
|
+
return this.fetchRequest(path, {
|
|
441
|
+
method: "DELETE",
|
|
442
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
443
|
+
headers: options?.headers
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
export {
|
|
448
|
+
Chains,
|
|
449
|
+
Jobs,
|
|
450
|
+
Signing,
|
|
451
|
+
Stablecoin,
|
|
452
|
+
VaultKey,
|
|
453
|
+
Wallets
|
|
454
|
+
};
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VaultKey SDK — Example Usage
|
|
3
|
+
*
|
|
4
|
+
* Run with: npx tsx examples/usage.ts
|
|
5
|
+
* Make sure VAULTKEY_API_KEY and VAULTKEY_API_SECRET are set in your environment.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { VaultKey } from "@vaultkey/sdk";
|
|
9
|
+
|
|
10
|
+
const vk = new VaultKey({
|
|
11
|
+
apiKey: process.env.VAULTKEY_API_KEY,
|
|
12
|
+
apiSecret: process.env.VAULTKEY_API_SECRET,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Helper: poll a job until completed or failed
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
async function pollJob(jobId: string, intervalMs = 1000) {
|
|
20
|
+
console.log(` Polling job ${jobId}...`);
|
|
21
|
+
while (true) {
|
|
22
|
+
const { data, error } = await vk.jobs.get(jobId);
|
|
23
|
+
if (error) throw new Error(`Job poll failed: ${error.message}`);
|
|
24
|
+
console.log(` status: ${data.status}`);
|
|
25
|
+
if (data.status === "completed") return data;
|
|
26
|
+
if (data.status === "failed") throw new Error(`Job failed: ${data.error}`);
|
|
27
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// 1. Create wallets
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
async function createWallets() {
|
|
36
|
+
console.log("\n── Create Wallets ──────────────────────────────────────────");
|
|
37
|
+
|
|
38
|
+
const { data: evmWallet, error: evmErr } = await vk.wallets.create({
|
|
39
|
+
userId: "user_123",
|
|
40
|
+
chainType: "evm",
|
|
41
|
+
label: "Primary EVM wallet",
|
|
42
|
+
});
|
|
43
|
+
if (evmErr) throw new Error(evmErr.message);
|
|
44
|
+
console.log("EVM wallet created:", evmWallet.id, evmWallet.address);
|
|
45
|
+
|
|
46
|
+
const { data: solWallet, error: solErr } = await vk.wallets.create({
|
|
47
|
+
userId: "user_123",
|
|
48
|
+
chainType: "solana",
|
|
49
|
+
});
|
|
50
|
+
if (solErr) throw new Error(solErr.message);
|
|
51
|
+
console.log("Solana wallet created:", solWallet.id, solWallet.address);
|
|
52
|
+
|
|
53
|
+
return { evmWallet, solWallet };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// 2. List wallets for a user
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
async function listWallets(userId: string) {
|
|
61
|
+
console.log("\n── List Wallets ────────────────────────────────────────────");
|
|
62
|
+
|
|
63
|
+
const { data, error } = await vk.wallets.listByUser(userId);
|
|
64
|
+
if (error) throw new Error(error.message);
|
|
65
|
+
|
|
66
|
+
console.log(`Found ${data.wallets.length} wallet(s) for user ${userId}`);
|
|
67
|
+
for (const w of data.wallets) {
|
|
68
|
+
console.log(` ${w.id} ${w.chainType} ${w.address}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (data.hasMore) {
|
|
72
|
+
console.log(" (more pages available — use data.nextCursor to fetch)");
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// 3. Check balances
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
async function checkBalances(evmWalletId: string, solWalletId: string) {
|
|
81
|
+
console.log("\n── Balances ────────────────────────────────────────────────");
|
|
82
|
+
|
|
83
|
+
// EVM — prefer chain name over chain ID
|
|
84
|
+
const { data: evmBal, error: evmErr } = await vk.wallets.evmBalance(
|
|
85
|
+
evmWalletId,
|
|
86
|
+
{ chainName: "base-sepolia" }
|
|
87
|
+
);
|
|
88
|
+
if (evmErr) throw new Error(evmErr.message);
|
|
89
|
+
console.log(`EVM balance: ${evmBal.balance} ${evmBal.symbol} on ${evmBal.chainName}`);
|
|
90
|
+
|
|
91
|
+
// Solana
|
|
92
|
+
const { data: solBal, error: solErr } = await vk.wallets.solanaBalance(solWalletId);
|
|
93
|
+
if (solErr) throw new Error(solErr.message);
|
|
94
|
+
console.log(`Solana balance: ${solBal.balance} ${solBal.symbol}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// 4. Sign messages
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
async function signMessages(evmWalletId: string, solWalletId: string) {
|
|
102
|
+
console.log("\n── Sign Messages ───────────────────────────────────────────");
|
|
103
|
+
|
|
104
|
+
// EVM message signing
|
|
105
|
+
const { data: evmJob, error: evmErr } = await vk.wallets
|
|
106
|
+
.signing(evmWalletId)
|
|
107
|
+
.evmMessage({
|
|
108
|
+
payload: { message: "Hello from VaultKey" },
|
|
109
|
+
idempotencyKey: "sign-evm-001",
|
|
110
|
+
});
|
|
111
|
+
if (evmErr) throw new Error(evmErr.message);
|
|
112
|
+
console.log("EVM sign job created:", evmJob.jobId);
|
|
113
|
+
const evmResult = await pollJob(evmJob.jobId);
|
|
114
|
+
console.log("EVM sign result:", evmResult.result);
|
|
115
|
+
|
|
116
|
+
// Solana message signing
|
|
117
|
+
const { data: solJob, error: solErr } = await vk.wallets
|
|
118
|
+
.signing(solWalletId)
|
|
119
|
+
.solanaMessage({
|
|
120
|
+
payload: { data: Buffer.from("Hello from VaultKey").toString("base64") },
|
|
121
|
+
});
|
|
122
|
+
if (solErr) throw new Error(solErr.message);
|
|
123
|
+
console.log("Solana sign job created:", solJob.jobId);
|
|
124
|
+
await pollJob(solJob.jobId);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
// 5. Stablecoin transfer
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
async function stablecoinTransfer(evmWalletId: string) {
|
|
132
|
+
console.log("\n── Stablecoin Transfer ─────────────────────────────────────");
|
|
133
|
+
|
|
134
|
+
// EVM USDC transfer (gasless — relayer pays gas)
|
|
135
|
+
const { data, error } = await vk.stablecoin.transfer(evmWalletId, {
|
|
136
|
+
token: "usdc",
|
|
137
|
+
to: "0xRecipientAddress",
|
|
138
|
+
amount: "10.00",
|
|
139
|
+
chainType: "evm",
|
|
140
|
+
chainName: "base-sepolia",
|
|
141
|
+
gasless: true,
|
|
142
|
+
speed: "normal",
|
|
143
|
+
idempotencyKey: "transfer-usdc-001",
|
|
144
|
+
});
|
|
145
|
+
if (error) throw new Error(error.message);
|
|
146
|
+
console.log("Transfer job created:", data.jobId);
|
|
147
|
+
await pollJob(data.jobId);
|
|
148
|
+
|
|
149
|
+
// Check balance after
|
|
150
|
+
const { data: bal } = await vk.stablecoin.balance(evmWalletId, {
|
|
151
|
+
token: "usdc",
|
|
152
|
+
chainType: "evm",
|
|
153
|
+
chainName: "base-sepolia",
|
|
154
|
+
});
|
|
155
|
+
console.log(`USDC balance after transfer: ${bal.balance} ${bal.symbol}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
// 6. Sweep
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
async function sweep(evmWalletId: string, solWalletId: string) {
|
|
163
|
+
console.log("\n── Sweep ───────────────────────────────────────────────────");
|
|
164
|
+
|
|
165
|
+
// EVM sweep
|
|
166
|
+
const { data: evmJob, error: evmErr } = await vk.wallets.sweep(evmWalletId, {
|
|
167
|
+
chainType: "evm",
|
|
168
|
+
chainName: "base-sepolia",
|
|
169
|
+
});
|
|
170
|
+
if (evmErr) throw new Error(evmErr.message);
|
|
171
|
+
console.log("EVM sweep job:", evmJob.jobId);
|
|
172
|
+
await pollJob(evmJob.jobId);
|
|
173
|
+
|
|
174
|
+
// Solana sweep
|
|
175
|
+
const { data: solJob, error: solErr } = await vk.wallets.sweep(solWalletId, {
|
|
176
|
+
chainType: "solana",
|
|
177
|
+
});
|
|
178
|
+
if (solErr) throw new Error(solErr.message);
|
|
179
|
+
console.log("Solana sweep job:", solJob.jobId);
|
|
180
|
+
await pollJob(solJob.jobId);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
// 7. List supported chains
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
async function listChains() {
|
|
188
|
+
console.log("\n── Supported Chains ────────────────────────────────────────");
|
|
189
|
+
|
|
190
|
+
const { data: chains, error } = await vk.chains.list();
|
|
191
|
+
if (error) throw new Error(error.message);
|
|
192
|
+
|
|
193
|
+
for (const c of chains) {
|
|
194
|
+
const label = c.testnet ? "(testnet)" : "(mainnet)";
|
|
195
|
+
console.log(` ${c.name.padEnd(20)} chain_id=${c.chainId} ${c.nativeSymbol} ${label}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
// Run all examples
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
|
|
203
|
+
async function main() {
|
|
204
|
+
console.log("VaultKey SDK — Example Usage");
|
|
205
|
+
console.log("=".repeat(60));
|
|
206
|
+
|
|
207
|
+
const { evmWallet, solWallet } = await createWallets();
|
|
208
|
+
await listWallets("user_123");
|
|
209
|
+
await checkBalances(evmWallet.id, solWallet.id);
|
|
210
|
+
await signMessages(evmWallet.id, solWallet.id);
|
|
211
|
+
await stablecoinTransfer(evmWallet.id);
|
|
212
|
+
await sweep(evmWallet.id, solWallet.id);
|
|
213
|
+
await listChains();
|
|
214
|
+
|
|
215
|
+
console.log("\n✓ All examples completed.");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
main().catch((err) => {
|
|
219
|
+
console.error("Error:", err.message);
|
|
220
|
+
process.exit(1);
|
|
221
|
+
});
|