@buildersgarden/siwa 0.0.4 → 0.0.5
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/keystore.d.ts +2 -2
- package/dist/keystore.js +118 -97
- package/package.json +1 -1
package/dist/keystore.d.ts
CHANGED
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
* KEYSTORE_PATH — Path to encrypted keystore file (default: ./agent-keystore.json)
|
|
35
35
|
* AGENT_PRIVATE_KEY — Fallback for env backend only
|
|
36
36
|
*/
|
|
37
|
-
import { type WalletClient, type Account, type Chain, type Transport } from
|
|
38
|
-
export type KeystoreBackend =
|
|
37
|
+
import { type WalletClient, type Account, type Chain, type Transport } from "viem";
|
|
38
|
+
export type KeystoreBackend = "encrypted-file" | "env" | "proxy";
|
|
39
39
|
export interface KeystoreConfig {
|
|
40
40
|
backend?: KeystoreBackend;
|
|
41
41
|
keystorePath?: string;
|
package/dist/keystore.js
CHANGED
|
@@ -34,20 +34,20 @@
|
|
|
34
34
|
* KEYSTORE_PATH — Path to encrypted keystore file (default: ./agent-keystore.json)
|
|
35
35
|
* AGENT_PRIVATE_KEY — Fallback for env backend only
|
|
36
36
|
*/
|
|
37
|
-
import { createWalletClient, http, keccak256, toBytes, toHex, concat, } from
|
|
38
|
-
import { privateKeyToAccount, generatePrivateKey } from
|
|
39
|
-
import { hashAuthorization } from
|
|
40
|
-
import { scrypt } from
|
|
41
|
-
import { randomBytes } from
|
|
42
|
-
import { ctr } from
|
|
43
|
-
import * as fs from
|
|
44
|
-
import * as crypto from
|
|
45
|
-
import * as os from
|
|
46
|
-
import { computeHmac } from
|
|
37
|
+
import { createWalletClient, http, keccak256, toBytes, toHex, concat, } from "viem";
|
|
38
|
+
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts";
|
|
39
|
+
import { hashAuthorization } from "viem/experimental";
|
|
40
|
+
import { scrypt } from "@noble/hashes/scrypt";
|
|
41
|
+
import { randomBytes } from "@noble/hashes/utils";
|
|
42
|
+
import { ctr } from "@noble/ciphers/aes";
|
|
43
|
+
import * as fs from "fs";
|
|
44
|
+
import * as crypto from "crypto";
|
|
45
|
+
import * as os from "os";
|
|
46
|
+
import { computeHmac } from "./proxy-auth.js";
|
|
47
47
|
// ---------------------------------------------------------------------------
|
|
48
48
|
// Constants
|
|
49
49
|
// ---------------------------------------------------------------------------
|
|
50
|
-
const DEFAULT_KEYSTORE_PATH =
|
|
50
|
+
const DEFAULT_KEYSTORE_PATH = "./agent-keystore.json";
|
|
51
51
|
function generateUUID() {
|
|
52
52
|
const bytes = randomBytes(16);
|
|
53
53
|
bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4
|
|
@@ -65,13 +65,21 @@ async function encryptKeystore(privateKey, password) {
|
|
|
65
65
|
const p = 1;
|
|
66
66
|
const dklen = 32;
|
|
67
67
|
// Derive key using scrypt
|
|
68
|
-
const derivedKey = scrypt(new TextEncoder().encode(password), salt, {
|
|
68
|
+
const derivedKey = scrypt(new TextEncoder().encode(password), salt, {
|
|
69
|
+
N: n,
|
|
70
|
+
r,
|
|
71
|
+
p,
|
|
72
|
+
dkLen: dklen,
|
|
73
|
+
});
|
|
69
74
|
// Encrypt private key with AES-128-CTR
|
|
70
75
|
const encryptionKey = derivedKey.slice(0, 16);
|
|
71
76
|
const cipher = ctr(encryptionKey, iv);
|
|
72
77
|
const ciphertext = cipher.encrypt(privateKeyBytes);
|
|
73
78
|
// Calculate MAC: keccak256(derivedKey[16:32] + ciphertext)
|
|
74
|
-
const macData = concat([
|
|
79
|
+
const macData = concat([
|
|
80
|
+
toHex(derivedKey.slice(16, 32)),
|
|
81
|
+
toHex(ciphertext),
|
|
82
|
+
]);
|
|
75
83
|
const mac = keccak256(macData);
|
|
76
84
|
// Get address from private key
|
|
77
85
|
const account = privateKeyToAccount(privateKey);
|
|
@@ -82,8 +90,8 @@ async function encryptKeystore(privateKey, password) {
|
|
|
82
90
|
crypto: {
|
|
83
91
|
ciphertext: toHex(ciphertext).slice(2),
|
|
84
92
|
cipherparams: { iv: toHex(iv).slice(2) },
|
|
85
|
-
cipher:
|
|
86
|
-
kdf:
|
|
93
|
+
cipher: "aes-128-ctr",
|
|
94
|
+
kdf: "scrypt",
|
|
87
95
|
kdfparams: {
|
|
88
96
|
dklen,
|
|
89
97
|
salt: toHex(salt).slice(2),
|
|
@@ -102,10 +110,10 @@ async function decryptKeystore(json, password) {
|
|
|
102
110
|
throw new Error(`Unsupported keystore version: ${keystore.version}`);
|
|
103
111
|
}
|
|
104
112
|
const { crypto: cryptoData } = keystore;
|
|
105
|
-
if (cryptoData.kdf !==
|
|
113
|
+
if (cryptoData.kdf !== "scrypt") {
|
|
106
114
|
throw new Error(`Unsupported KDF: ${cryptoData.kdf}`);
|
|
107
115
|
}
|
|
108
|
-
if (cryptoData.cipher !==
|
|
116
|
+
if (cryptoData.cipher !== "aes-128-ctr") {
|
|
109
117
|
throw new Error(`Unsupported cipher: ${cryptoData.cipher}`);
|
|
110
118
|
}
|
|
111
119
|
const { kdfparams } = cryptoData;
|
|
@@ -113,12 +121,20 @@ async function decryptKeystore(json, password) {
|
|
|
113
121
|
const iv = toBytes(`0x${cryptoData.cipherparams.iv}`);
|
|
114
122
|
const ciphertext = toBytes(`0x${cryptoData.ciphertext}`);
|
|
115
123
|
// Derive key using scrypt
|
|
116
|
-
const derivedKey = scrypt(new TextEncoder().encode(password), salt, {
|
|
124
|
+
const derivedKey = scrypt(new TextEncoder().encode(password), salt, {
|
|
125
|
+
N: kdfparams.n,
|
|
126
|
+
r: kdfparams.r,
|
|
127
|
+
p: kdfparams.p,
|
|
128
|
+
dkLen: kdfparams.dklen,
|
|
129
|
+
});
|
|
117
130
|
// Verify MAC
|
|
118
|
-
const macData = concat([
|
|
131
|
+
const macData = concat([
|
|
132
|
+
toHex(derivedKey.slice(16, 32)),
|
|
133
|
+
toHex(ciphertext),
|
|
134
|
+
]);
|
|
119
135
|
const calculatedMac = keccak256(macData).slice(2);
|
|
120
136
|
if (calculatedMac.toLowerCase() !== cryptoData.mac.toLowerCase()) {
|
|
121
|
-
throw new Error(
|
|
137
|
+
throw new Error("Invalid password or corrupted keystore");
|
|
122
138
|
}
|
|
123
139
|
// Decrypt private key with AES-128-CTR
|
|
124
140
|
const encryptionKey = derivedKey.slice(0, 16);
|
|
@@ -133,15 +149,15 @@ async function proxyRequest(config, endpoint, body = {}) {
|
|
|
133
149
|
const url = config.proxyUrl || process.env.KEYRING_PROXY_URL;
|
|
134
150
|
const secret = config.proxySecret || process.env.KEYRING_PROXY_SECRET;
|
|
135
151
|
if (!url)
|
|
136
|
-
throw new Error(
|
|
152
|
+
throw new Error("Proxy backend requires KEYRING_PROXY_URL or config.proxyUrl");
|
|
137
153
|
if (!secret)
|
|
138
|
-
throw new Error(
|
|
139
|
-
const bodyStr = JSON.stringify(body, (_key, value) => typeof value ===
|
|
140
|
-
const hmacHeaders = computeHmac(secret,
|
|
154
|
+
throw new Error("Proxy backend requires KEYRING_PROXY_SECRET or config.proxySecret");
|
|
155
|
+
const bodyStr = JSON.stringify(body, (_key, value) => typeof value === "bigint" ? "0x" + value.toString(16) : value);
|
|
156
|
+
const hmacHeaders = computeHmac(secret, "POST", endpoint, bodyStr);
|
|
141
157
|
const res = await fetch(`${url}${endpoint}`, {
|
|
142
|
-
method:
|
|
158
|
+
method: "POST",
|
|
143
159
|
headers: {
|
|
144
|
-
|
|
160
|
+
"Content-Type": "application/json",
|
|
145
161
|
...hmacHeaders,
|
|
146
162
|
},
|
|
147
163
|
body: bodyStr,
|
|
@@ -158,16 +174,16 @@ async function proxyRequest(config, endpoint, body = {}) {
|
|
|
158
174
|
export async function detectBackend() {
|
|
159
175
|
// 0. Proxy backend (if URL is set)
|
|
160
176
|
if (process.env.KEYRING_PROXY_URL)
|
|
161
|
-
return
|
|
177
|
+
return "proxy";
|
|
162
178
|
// 1. Check for existing encrypted keystore file
|
|
163
179
|
if (fs.existsSync(process.env.KEYSTORE_PATH || DEFAULT_KEYSTORE_PATH)) {
|
|
164
|
-
return
|
|
180
|
+
return "encrypted-file";
|
|
165
181
|
}
|
|
166
182
|
// 2. Check for env var
|
|
167
183
|
if (process.env.AGENT_PRIVATE_KEY)
|
|
168
|
-
return
|
|
184
|
+
return "env";
|
|
169
185
|
// 3. Default to encrypted-file (will be created on first use)
|
|
170
|
-
return
|
|
186
|
+
return "encrypted-file";
|
|
171
187
|
}
|
|
172
188
|
// ---------------------------------------------------------------------------
|
|
173
189
|
// Encrypted V3 JSON Keystore backend
|
|
@@ -179,7 +195,7 @@ async function encryptedFileStore(privateKey, password, filePath) {
|
|
|
179
195
|
async function encryptedFileLoad(password, filePath) {
|
|
180
196
|
if (!fs.existsSync(filePath))
|
|
181
197
|
return null;
|
|
182
|
-
const json = fs.readFileSync(filePath,
|
|
198
|
+
const json = fs.readFileSync(filePath, "utf-8");
|
|
183
199
|
return decryptKeystore(json, password);
|
|
184
200
|
}
|
|
185
201
|
function encryptedFileExists(filePath) {
|
|
@@ -195,15 +211,12 @@ function encryptedFileExists(filePath) {
|
|
|
195
211
|
// ---------------------------------------------------------------------------
|
|
196
212
|
function deriveMachinePassword() {
|
|
197
213
|
const factors = [
|
|
198
|
-
process.env.USER || process.env.USERNAME ||
|
|
199
|
-
process.env.HOME || process.env.USERPROFILE ||
|
|
214
|
+
process.env.USER || process.env.USERNAME || "agent",
|
|
215
|
+
process.env.HOME || process.env.USERPROFILE || "/tmp",
|
|
200
216
|
os.hostname(),
|
|
201
217
|
os.platform(),
|
|
202
218
|
];
|
|
203
|
-
return crypto
|
|
204
|
-
.createHash('sha256')
|
|
205
|
-
.update(factors.join(':'))
|
|
206
|
-
.digest('hex');
|
|
219
|
+
return crypto.createHash("sha256").update(factors.join(":")).digest("hex");
|
|
207
220
|
}
|
|
208
221
|
// ---------------------------------------------------------------------------
|
|
209
222
|
// Public API — the ONLY way external code touches private keys
|
|
@@ -213,34 +226,36 @@ function deriveMachinePassword() {
|
|
|
213
226
|
* Returns only the public address — NEVER the private key.
|
|
214
227
|
*/
|
|
215
228
|
export async function createWallet(config = {}) {
|
|
216
|
-
const backend = config.backend || await detectBackend();
|
|
229
|
+
const backend = config.backend || (await detectBackend());
|
|
217
230
|
const keystorePath = config.keystorePath || process.env.KEYSTORE_PATH || DEFAULT_KEYSTORE_PATH;
|
|
218
|
-
if (backend ===
|
|
219
|
-
const data = await proxyRequest(config,
|
|
231
|
+
if (backend === "proxy") {
|
|
232
|
+
const data = await proxyRequest(config, "/create-wallet");
|
|
220
233
|
return { address: data.address, backend, keystorePath: undefined };
|
|
221
234
|
}
|
|
222
235
|
const privateKey = generatePrivateKey();
|
|
223
236
|
const account = privateKeyToAccount(privateKey);
|
|
224
237
|
const address = account.address;
|
|
225
238
|
switch (backend) {
|
|
226
|
-
case
|
|
227
|
-
const password = config.password ||
|
|
239
|
+
case "encrypted-file": {
|
|
240
|
+
const password = config.password ||
|
|
241
|
+
process.env.KEYSTORE_PASSWORD ||
|
|
242
|
+
deriveMachinePassword();
|
|
228
243
|
await encryptedFileStore(privateKey, password, keystorePath);
|
|
229
244
|
break;
|
|
230
245
|
}
|
|
231
|
-
case
|
|
246
|
+
case "env":
|
|
232
247
|
// For env backend, we print the key ONCE for the operator to capture.
|
|
233
248
|
// This is the ONLY time the raw key is ever exposed.
|
|
234
|
-
console.log(
|
|
249
|
+
console.log("=== ENV BACKEND (testing only) ===");
|
|
235
250
|
console.log(`Set this in your environment:`);
|
|
236
251
|
console.log(` export AGENT_PRIVATE_KEY="${privateKey}"`);
|
|
237
|
-
console.log(
|
|
252
|
+
console.log("=================================");
|
|
238
253
|
break;
|
|
239
254
|
}
|
|
240
255
|
return {
|
|
241
256
|
address,
|
|
242
257
|
backend,
|
|
243
|
-
keystorePath: backend ===
|
|
258
|
+
keystorePath: backend === "encrypted-file" ? keystorePath : undefined,
|
|
244
259
|
};
|
|
245
260
|
}
|
|
246
261
|
/**
|
|
@@ -249,44 +264,46 @@ export async function createWallet(config = {}) {
|
|
|
249
264
|
* Returns only the public address.
|
|
250
265
|
*/
|
|
251
266
|
export async function importWallet(privateKey, config = {}) {
|
|
252
|
-
const backend = config.backend || await detectBackend();
|
|
253
|
-
if (backend ===
|
|
254
|
-
throw new Error(
|
|
267
|
+
const backend = config.backend || (await detectBackend());
|
|
268
|
+
if (backend === "proxy") {
|
|
269
|
+
throw new Error("importWallet() is not supported via proxy. Import the wallet on the proxy server directly.");
|
|
255
270
|
}
|
|
256
271
|
const keystorePath = config.keystorePath || process.env.KEYSTORE_PATH || DEFAULT_KEYSTORE_PATH;
|
|
257
|
-
const hexKey = (privateKey.startsWith(
|
|
272
|
+
const hexKey = (privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`);
|
|
258
273
|
const account = privateKeyToAccount(hexKey);
|
|
259
274
|
const address = account.address;
|
|
260
275
|
switch (backend) {
|
|
261
|
-
case
|
|
262
|
-
const password = config.password ||
|
|
276
|
+
case "encrypted-file": {
|
|
277
|
+
const password = config.password ||
|
|
278
|
+
process.env.KEYSTORE_PASSWORD ||
|
|
279
|
+
deriveMachinePassword();
|
|
263
280
|
await encryptedFileStore(hexKey, password, keystorePath);
|
|
264
281
|
break;
|
|
265
282
|
}
|
|
266
|
-
case
|
|
283
|
+
case "env":
|
|
267
284
|
// Nothing to persist for env backend
|
|
268
285
|
break;
|
|
269
286
|
}
|
|
270
287
|
return {
|
|
271
288
|
address,
|
|
272
289
|
backend,
|
|
273
|
-
keystorePath: backend ===
|
|
290
|
+
keystorePath: backend === "encrypted-file" ? keystorePath : undefined,
|
|
274
291
|
};
|
|
275
292
|
}
|
|
276
293
|
/**
|
|
277
294
|
* Check if a wallet is available in any backend.
|
|
278
295
|
*/
|
|
279
296
|
export async function hasWallet(config = {}) {
|
|
280
|
-
const backend = config.backend || await detectBackend();
|
|
281
|
-
if (backend ===
|
|
282
|
-
const data = await proxyRequest(config,
|
|
297
|
+
const backend = config.backend || (await detectBackend());
|
|
298
|
+
if (backend === "proxy") {
|
|
299
|
+
const data = await proxyRequest(config, "/has-wallet");
|
|
283
300
|
return data.hasWallet;
|
|
284
301
|
}
|
|
285
302
|
const keystorePath = config.keystorePath || process.env.KEYSTORE_PATH || DEFAULT_KEYSTORE_PATH;
|
|
286
303
|
switch (backend) {
|
|
287
|
-
case
|
|
304
|
+
case "encrypted-file":
|
|
288
305
|
return encryptedFileExists(keystorePath);
|
|
289
|
-
case
|
|
306
|
+
case "env":
|
|
290
307
|
return !!process.env.AGENT_PRIVATE_KEY;
|
|
291
308
|
}
|
|
292
309
|
}
|
|
@@ -294,9 +311,9 @@ export async function hasWallet(config = {}) {
|
|
|
294
311
|
* Get the wallet's public address (no private key exposed).
|
|
295
312
|
*/
|
|
296
313
|
export async function getAddress(config = {}) {
|
|
297
|
-
const backend = config.backend || await detectBackend();
|
|
298
|
-
if (backend ===
|
|
299
|
-
const data = await proxyRequest(config,
|
|
314
|
+
const backend = config.backend || (await detectBackend());
|
|
315
|
+
if (backend === "proxy") {
|
|
316
|
+
const data = await proxyRequest(config, "/get-address");
|
|
300
317
|
return data.address;
|
|
301
318
|
}
|
|
302
319
|
const privateKey = await _loadPrivateKeyInternal(config);
|
|
@@ -311,14 +328,14 @@ export async function getAddress(config = {}) {
|
|
|
311
328
|
* Only the signature is returned.
|
|
312
329
|
*/
|
|
313
330
|
export async function signMessage(message, config = {}) {
|
|
314
|
-
const backend = config.backend || await detectBackend();
|
|
315
|
-
if (backend ===
|
|
316
|
-
const data = await proxyRequest(config,
|
|
331
|
+
const backend = config.backend || (await detectBackend());
|
|
332
|
+
if (backend === "proxy") {
|
|
333
|
+
const data = await proxyRequest(config, "/sign-message", { message });
|
|
317
334
|
return { signature: data.signature, address: data.address };
|
|
318
335
|
}
|
|
319
336
|
const privateKey = await _loadPrivateKeyInternal(config);
|
|
320
337
|
if (!privateKey)
|
|
321
|
-
throw new Error(
|
|
338
|
+
throw new Error("No wallet found. Run createWallet() first.");
|
|
322
339
|
const account = privateKeyToAccount(privateKey);
|
|
323
340
|
const signature = await account.signMessage({ message });
|
|
324
341
|
return { signature, address: account.address };
|
|
@@ -332,13 +349,13 @@ function parseBigIntFromJson(value) {
|
|
|
332
349
|
if (value === null || value === undefined)
|
|
333
350
|
return undefined;
|
|
334
351
|
let result;
|
|
335
|
-
if (typeof value ===
|
|
352
|
+
if (typeof value === "bigint") {
|
|
336
353
|
result = value;
|
|
337
354
|
}
|
|
338
|
-
else if (typeof value ===
|
|
355
|
+
else if (typeof value === "number") {
|
|
339
356
|
result = BigInt(value);
|
|
340
357
|
}
|
|
341
|
-
else if (typeof value ===
|
|
358
|
+
else if (typeof value === "string") {
|
|
342
359
|
// Handle hex strings (0x...) and decimal strings
|
|
343
360
|
result = BigInt(value);
|
|
344
361
|
}
|
|
@@ -355,11 +372,11 @@ function parseBigIntFromJson(value) {
|
|
|
355
372
|
function parseBigIntKeepZero(value) {
|
|
356
373
|
if (value === null || value === undefined)
|
|
357
374
|
return undefined;
|
|
358
|
-
if (typeof value ===
|
|
375
|
+
if (typeof value === "bigint")
|
|
359
376
|
return value;
|
|
360
|
-
if (typeof value ===
|
|
377
|
+
if (typeof value === "number")
|
|
361
378
|
return BigInt(value);
|
|
362
|
-
if (typeof value ===
|
|
379
|
+
if (typeof value === "string")
|
|
363
380
|
return BigInt(value);
|
|
364
381
|
return undefined;
|
|
365
382
|
}
|
|
@@ -369,14 +386,16 @@ function parseBigIntKeepZero(value) {
|
|
|
369
386
|
* Only the signed transaction is returned.
|
|
370
387
|
*/
|
|
371
388
|
export async function signTransaction(tx, config = {}) {
|
|
372
|
-
const backend = config.backend || await detectBackend();
|
|
373
|
-
if (backend ===
|
|
374
|
-
const data = await proxyRequest(config,
|
|
389
|
+
const backend = config.backend || (await detectBackend());
|
|
390
|
+
if (backend === "proxy") {
|
|
391
|
+
const data = await proxyRequest(config, "/sign-transaction", {
|
|
392
|
+
tx: tx,
|
|
393
|
+
});
|
|
375
394
|
return { signedTx: data.signedTx, address: data.address };
|
|
376
395
|
}
|
|
377
396
|
const privateKey = await _loadPrivateKeyInternal(config);
|
|
378
397
|
if (!privateKey)
|
|
379
|
-
throw new Error(
|
|
398
|
+
throw new Error("No wallet found. Run createWallet() first.");
|
|
380
399
|
const account = privateKeyToAccount(privateKey);
|
|
381
400
|
// Parse numeric fields from JSON representation (strings) to bigints.
|
|
382
401
|
// For 'value', zero is converted to undefined so viem encodes it as 0x80 (empty)
|
|
@@ -397,12 +416,12 @@ export async function signTransaction(tx, config = {}) {
|
|
|
397
416
|
};
|
|
398
417
|
// Handle EIP-1559 vs legacy transactions
|
|
399
418
|
if (tx.type === 2 || tx.maxFeePerGas !== undefined) {
|
|
400
|
-
viemTx.type =
|
|
419
|
+
viemTx.type = "eip1559";
|
|
401
420
|
viemTx.maxFeePerGas = maxFeePerGas;
|
|
402
421
|
viemTx.maxPriorityFeePerGas = maxPriorityFeePerGas;
|
|
403
422
|
}
|
|
404
423
|
else if (tx.gasPrice !== undefined) {
|
|
405
|
-
viemTx.type =
|
|
424
|
+
viemTx.type = "legacy";
|
|
406
425
|
viemTx.gasPrice = gasPrice;
|
|
407
426
|
}
|
|
408
427
|
if (tx.accessList) {
|
|
@@ -422,14 +441,14 @@ export async function signTransaction(tx, config = {}) {
|
|
|
422
441
|
* @returns Signed authorization tuple (address, nonce, chainId, yParity, r, s)
|
|
423
442
|
*/
|
|
424
443
|
export async function signAuthorization(auth, config = {}) {
|
|
425
|
-
const backend = config.backend || await detectBackend();
|
|
426
|
-
if (backend ===
|
|
427
|
-
const data = await proxyRequest(config,
|
|
444
|
+
const backend = config.backend || (await detectBackend());
|
|
445
|
+
if (backend === "proxy") {
|
|
446
|
+
const data = await proxyRequest(config, "/sign-authorization", { auth });
|
|
428
447
|
return data;
|
|
429
448
|
}
|
|
430
449
|
const privateKey = await _loadPrivateKeyInternal(config);
|
|
431
450
|
if (!privateKey)
|
|
432
|
-
throw new Error(
|
|
451
|
+
throw new Error("No wallet found. Run createWallet() first.");
|
|
433
452
|
const account = privateKeyToAccount(privateKey);
|
|
434
453
|
// EIP-7702 authorization signing using viem experimental
|
|
435
454
|
const chainId = auth.chainId ?? 1;
|
|
@@ -463,13 +482,13 @@ export async function signAuthorization(auth, config = {}) {
|
|
|
463
482
|
* Prefer signMessage() / signTransaction() when possible.
|
|
464
483
|
*/
|
|
465
484
|
export async function getWalletClient(rpcUrl, config = {}) {
|
|
466
|
-
const backend = config.backend || await detectBackend();
|
|
467
|
-
if (backend ===
|
|
468
|
-
throw new Error(
|
|
485
|
+
const backend = config.backend || (await detectBackend());
|
|
486
|
+
if (backend === "proxy") {
|
|
487
|
+
throw new Error("getWalletClient() is not supported via proxy. The private key cannot be serialized over HTTP. Use signMessage() or signTransaction() instead.");
|
|
469
488
|
}
|
|
470
489
|
const privateKey = await _loadPrivateKeyInternal(config);
|
|
471
490
|
if (!privateKey)
|
|
472
|
-
throw new Error(
|
|
491
|
+
throw new Error("No wallet found. Run createWallet() first.");
|
|
473
492
|
const account = privateKeyToAccount(privateKey);
|
|
474
493
|
return createWalletClient({
|
|
475
494
|
account,
|
|
@@ -481,20 +500,20 @@ export async function getWalletClient(rpcUrl, config = {}) {
|
|
|
481
500
|
* DESTRUCTIVE — the identity is lost if no backup exists.
|
|
482
501
|
*/
|
|
483
502
|
export async function deleteWallet(config = {}) {
|
|
484
|
-
const backend = config.backend || await detectBackend();
|
|
485
|
-
if (backend ===
|
|
486
|
-
throw new Error(
|
|
503
|
+
const backend = config.backend || (await detectBackend());
|
|
504
|
+
if (backend === "proxy") {
|
|
505
|
+
throw new Error("deleteWallet() is not supported via proxy. Delete the wallet on the proxy server directly.");
|
|
487
506
|
}
|
|
488
507
|
const keystorePath = config.keystorePath || process.env.KEYSTORE_PATH || DEFAULT_KEYSTORE_PATH;
|
|
489
508
|
switch (backend) {
|
|
490
|
-
case
|
|
509
|
+
case "encrypted-file":
|
|
491
510
|
if (fs.existsSync(keystorePath)) {
|
|
492
511
|
fs.unlinkSync(keystorePath);
|
|
493
512
|
return true;
|
|
494
513
|
}
|
|
495
514
|
return false;
|
|
496
|
-
case
|
|
497
|
-
console.warn(
|
|
515
|
+
case "env":
|
|
516
|
+
console.warn("Cannot delete env-based wallet. Unset AGENT_PRIVATE_KEY manually.");
|
|
498
517
|
return false;
|
|
499
518
|
}
|
|
500
519
|
}
|
|
@@ -502,19 +521,21 @@ export async function deleteWallet(config = {}) {
|
|
|
502
521
|
// Internal — loads the private key. NEVER exposed publicly.
|
|
503
522
|
// ---------------------------------------------------------------------------
|
|
504
523
|
async function _loadPrivateKeyInternal(config = {}) {
|
|
505
|
-
const backend = config.backend || await detectBackend();
|
|
524
|
+
const backend = config.backend || (await detectBackend());
|
|
506
525
|
const keystorePath = config.keystorePath || process.env.KEYSTORE_PATH || DEFAULT_KEYSTORE_PATH;
|
|
507
526
|
let privateKey = null;
|
|
508
527
|
switch (backend) {
|
|
509
|
-
case
|
|
510
|
-
const password = config.password ||
|
|
528
|
+
case "encrypted-file": {
|
|
529
|
+
const password = config.password ||
|
|
530
|
+
process.env.KEYSTORE_PASSWORD ||
|
|
531
|
+
deriveMachinePassword();
|
|
511
532
|
privateKey = await encryptedFileLoad(password, keystorePath);
|
|
512
533
|
break;
|
|
513
534
|
}
|
|
514
|
-
case
|
|
535
|
+
case "env": {
|
|
515
536
|
const envKey = process.env.AGENT_PRIVATE_KEY || null;
|
|
516
537
|
if (envKey) {
|
|
517
|
-
privateKey = (envKey.startsWith(
|
|
538
|
+
privateKey = (envKey.startsWith("0x") ? envKey : `0x${envKey}`);
|
|
518
539
|
}
|
|
519
540
|
break;
|
|
520
541
|
}
|