@vapi-network/core 0.3.0 → 0.7.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/README.md +406 -5
- package/dist/account-add.d.ts +94 -0
- package/dist/account-add.d.ts.map +1 -0
- package/dist/accounts.d.ts.map +1 -1
- package/dist/agent-link.d.ts +112 -0
- package/dist/agent-link.d.ts.map +1 -0
- package/dist/agent-link.js +1781 -0
- package/dist/agent-link.js.map +6 -0
- package/dist/agent-profile.d.ts +56 -0
- package/dist/agent-profile.d.ts.map +1 -0
- package/dist/allocation.d.ts +33 -0
- package/dist/allocation.d.ts.map +1 -0
- package/dist/api-key.d.ts +99 -0
- package/dist/api-key.d.ts.map +1 -0
- package/dist/api-key.js +20086 -0
- package/dist/api-key.js.map +6 -0
- package/dist/audit.d.ts +5 -3
- package/dist/audit.d.ts.map +1 -1
- package/dist/authorization-state.d.ts +58 -0
- package/dist/authorization-state.d.ts.map +1 -0
- package/dist/backup.d.ts +186 -0
- package/dist/backup.d.ts.map +1 -0
- package/dist/backup.js +47542 -0
- package/dist/backup.js.map +6 -0
- package/dist/call-contracts-wire.d.ts +151 -11
- package/dist/call-contracts-wire.d.ts.map +1 -1
- package/dist/ceiling.d.ts +90 -0
- package/dist/ceiling.d.ts.map +1 -0
- package/dist/cloud-backup.d.ts +125 -0
- package/dist/cloud-backup.d.ts.map +1 -0
- package/dist/cloud-backup.js +48402 -0
- package/dist/cloud-backup.js.map +6 -0
- package/dist/config.d.ts +7 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/device.d.ts +15 -0
- package/dist/device.d.ts.map +1 -0
- package/dist/discovery.d.ts +20 -2
- package/dist/discovery.d.ts.map +1 -1
- package/dist/distribute.d.ts +60 -0
- package/dist/distribute.d.ts.map +1 -0
- package/dist/index.d.ts +18 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8106 -1393
- package/dist/index.js.map +3 -3
- package/dist/keystore.d.ts +24 -1
- package/dist/keystore.d.ts.map +1 -1
- package/dist/marketplace-contracts.d.ts +448 -15
- package/dist/marketplace-contracts.d.ts.map +1 -1
- package/dist/movement.d.ts +102 -0
- package/dist/movement.d.ts.map +1 -0
- package/dist/networks.d.ts +17 -2
- package/dist/networks.d.ts.map +1 -1
- package/dist/receipts.d.ts +42 -0
- package/dist/receipts.d.ts.map +1 -1
- package/dist/router-client.d.ts +117 -0
- package/dist/router-client.d.ts.map +1 -0
- package/dist/router-client.js +46147 -0
- package/dist/router-client.js.map +6 -0
- package/dist/secret-store.d.ts +17 -5
- package/dist/secret-store.d.ts.map +1 -1
- package/dist/secrets.d.ts +1 -0
- package/dist/secrets.d.ts.map +1 -1
- package/dist/secrets.js +597 -25
- package/dist/secrets.js.map +3 -3
- package/dist/siblings.d.ts +41 -0
- package/dist/siblings.d.ts.map +1 -0
- package/dist/spend-policy.d.ts +12 -0
- package/dist/spend-policy.d.ts.map +1 -1
- package/dist/status.d.ts +73 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/support-report.d.ts +1 -1
- package/dist/sweep.d.ts +4 -0
- package/dist/sweep.d.ts.map +1 -1
- package/dist/transfer.d.ts +62 -0
- package/dist/transfer.d.ts.map +1 -0
- package/dist/vault-key.d.ts +41 -0
- package/dist/vault-key.d.ts.map +1 -0
- package/dist/vault-migrate.d.ts +29 -0
- package/dist/vault-migrate.d.ts.map +1 -0
- package/dist/vault.d.ts +93 -0
- package/dist/vault.d.ts.map +1 -0
- package/dist/wallet-store.d.ts +141 -31
- package/dist/wallet-store.d.ts.map +1 -1
- package/dist/x402-networks.d.ts +6 -9
- package/dist/x402-networks.d.ts.map +1 -1
- package/dist/x402-pay.d.ts +136 -0
- package/dist/x402-pay.d.ts.map +1 -0
- package/dist/x402.d.ts +20 -1
- package/dist/x402.d.ts.map +1 -1
- package/package.json +21 -1
package/dist/secrets.js
CHANGED
|
@@ -2212,7 +2212,7 @@ function weierstrass(curveDef) {
|
|
|
2212
2212
|
function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
|
|
2213
2213
|
if (["recovered", "canonical"].some((k) => k in opts))
|
|
2214
2214
|
throw new Error("sign() legacy options not supported");
|
|
2215
|
-
const { hash: hash2, randomBytes:
|
|
2215
|
+
const { hash: hash2, randomBytes: randomBytes4 } = CURVE;
|
|
2216
2216
|
let { lowS, prehash, extraEntropy: ent } = opts;
|
|
2217
2217
|
if (lowS == null)
|
|
2218
2218
|
lowS = true;
|
|
@@ -2224,7 +2224,7 @@ function weierstrass(curveDef) {
|
|
|
2224
2224
|
const d = normPrivateKeyToScalar(privateKey);
|
|
2225
2225
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|
2226
2226
|
if (ent != null && ent !== false) {
|
|
2227
|
-
const e2 = ent === true ?
|
|
2227
|
+
const e2 = ent === true ? randomBytes4(Fp.BYTES) : ent;
|
|
2228
2228
|
seedArgs.push(ensureBytes("extraEntropy", e2));
|
|
2229
2229
|
}
|
|
2230
2230
|
const seed = concatBytes2(...seedArgs);
|
|
@@ -46551,6 +46551,7 @@ init_getAddress();
|
|
|
46551
46551
|
|
|
46552
46552
|
// src/x402-networks.ts
|
|
46553
46553
|
var BASE_MAINNET_CAIP2 = "eip155:8453";
|
|
46554
|
+
var ARC_MAINNET_CAIP2 = "eip155:5042";
|
|
46554
46555
|
var ARC_TESTNET_CAIP2 = "eip155:5042002";
|
|
46555
46556
|
var SOLANA_MAINNET_CAIP2 = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d";
|
|
46556
46557
|
var X402_SOLANA_MAINNET_CAIP2 = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
@@ -46569,8 +46570,13 @@ var arcTestnetUsdc = canonicalIdentity("0x36000000000000000000000000000000000000
|
|
|
46569
46570
|
name: "USDC",
|
|
46570
46571
|
version: "2"
|
|
46571
46572
|
});
|
|
46573
|
+
var arcMainnetUsdc = canonicalIdentity("0x3600000000000000000000000000000000000000", {
|
|
46574
|
+
name: "USDC",
|
|
46575
|
+
version: "2"
|
|
46576
|
+
});
|
|
46572
46577
|
var CANONICAL_X402_USDC_NETWORKS = Object.freeze({
|
|
46573
46578
|
[BASE_MAINNET_CAIP2]: baseMainnetUsdc,
|
|
46579
|
+
[ARC_MAINNET_CAIP2]: arcMainnetUsdc,
|
|
46574
46580
|
[ARC_TESTNET_CAIP2]: arcTestnetUsdc
|
|
46575
46581
|
});
|
|
46576
46582
|
function browserNetwork(identity) {
|
|
@@ -46582,6 +46588,7 @@ function browserNetwork(identity) {
|
|
|
46582
46588
|
}
|
|
46583
46589
|
var BROWSER_ENABLED_X402_NETWORK_CONFIG = Object.freeze({
|
|
46584
46590
|
[BASE_MAINNET_CAIP2]: browserNetwork(baseMainnetUsdc),
|
|
46591
|
+
[ARC_MAINNET_CAIP2]: browserNetwork(arcMainnetUsdc),
|
|
46585
46592
|
[ARC_TESTNET_CAIP2]: browserNetwork(arcTestnetUsdc)
|
|
46586
46593
|
});
|
|
46587
46594
|
|
|
@@ -46594,7 +46601,18 @@ var NETWORKS = {
|
|
|
46594
46601
|
usdc: CANONICAL_X402_USDC_NETWORKS[BASE_MAINNET_CAIP2].usdc,
|
|
46595
46602
|
gasToken: "ETH",
|
|
46596
46603
|
rpcEnv: "BASE_RPC_URL",
|
|
46597
|
-
publicRpcUrl: "https://mainnet.base.org"
|
|
46604
|
+
publicRpcUrl: "https://mainnet.base.org",
|
|
46605
|
+
explorerUrl: "https://basescan.org"
|
|
46606
|
+
},
|
|
46607
|
+
[ARC_MAINNET_CAIP2]: {
|
|
46608
|
+
family: "evm",
|
|
46609
|
+
chainId: 5042,
|
|
46610
|
+
name: "Arc mainnet",
|
|
46611
|
+
usdc: CANONICAL_X402_USDC_NETWORKS[ARC_MAINNET_CAIP2].usdc,
|
|
46612
|
+
gasToken: "USDC",
|
|
46613
|
+
rpcEnv: "ARC_RPC_URL",
|
|
46614
|
+
publicRpcUrl: "https://rpc.mainnet.arc.io",
|
|
46615
|
+
explorerUrl: "https://explorer.arc.io"
|
|
46598
46616
|
},
|
|
46599
46617
|
[ARC_TESTNET_CAIP2]: {
|
|
46600
46618
|
family: "evm",
|
|
@@ -46679,7 +46697,8 @@ var configSchema = external_exports.object({
|
|
|
46679
46697
|
}).optional()
|
|
46680
46698
|
})
|
|
46681
46699
|
),
|
|
46682
|
-
spendCaps: spendCapsSchema
|
|
46700
|
+
spendCaps: spendCapsSchema,
|
|
46701
|
+
device: external_exports.string().regex(/^[a-z0-9][a-z0-9-]{0,31}$/).optional()
|
|
46683
46702
|
}).superRefine((config2, context) => {
|
|
46684
46703
|
for (const [network, configured] of Object.entries(config2.networks)) {
|
|
46685
46704
|
if (isSolanaNetwork(network)) {
|
|
@@ -46693,7 +46712,14 @@ var configSchema = external_exports.object({
|
|
|
46693
46712
|
continue;
|
|
46694
46713
|
}
|
|
46695
46714
|
try {
|
|
46696
|
-
getAddress(configured.usdc);
|
|
46715
|
+
const usdc = getAddress(configured.usdc);
|
|
46716
|
+
if ((network === ARC_MAINNET_CAIP2 || network === ARC_TESTNET_CAIP2) && usdc !== getAddress(NETWORKS[network].usdc)) {
|
|
46717
|
+
context.addIssue({
|
|
46718
|
+
code: "custom",
|
|
46719
|
+
path: ["networks", network, "usdc"],
|
|
46720
|
+
message: "Expected the canonical Arc USDC predeploy."
|
|
46721
|
+
});
|
|
46722
|
+
}
|
|
46697
46723
|
} catch {
|
|
46698
46724
|
context.addIssue({
|
|
46699
46725
|
code: "custom",
|
|
@@ -46706,6 +46732,7 @@ var configSchema = external_exports.object({
|
|
|
46706
46732
|
function getVapiPaths(directory = process.env.VAPI_HOME?.trim() || join2(homedir(), ".vapi")) {
|
|
46707
46733
|
return {
|
|
46708
46734
|
directory,
|
|
46735
|
+
agentsDir: join2(directory, "agents"),
|
|
46709
46736
|
config: join2(directory, "config.json"),
|
|
46710
46737
|
keystore: join2(directory, "keystore.json"),
|
|
46711
46738
|
receipts: join2(directory, "receipts.jsonl"),
|
|
@@ -46912,7 +46939,34 @@ var KeystoreError = class extends Error {
|
|
|
46912
46939
|
this.name = "KeystoreError";
|
|
46913
46940
|
}
|
|
46914
46941
|
};
|
|
46915
|
-
|
|
46942
|
+
function encryptWithKey(key, plaintext) {
|
|
46943
|
+
if (key.byteLength !== KEY_LENGTH) {
|
|
46944
|
+
throw new Error(`Expected a ${KEY_LENGTH}-byte AES-256-GCM key.`);
|
|
46945
|
+
}
|
|
46946
|
+
const nonce = randomBytes2(12);
|
|
46947
|
+
const cipher = createCipheriv("aes-256-gcm", key, nonce, {
|
|
46948
|
+
authTagLength: AUTH_TAG_LENGTH
|
|
46949
|
+
});
|
|
46950
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
46951
|
+
return { nonce, ciphertext, tag: cipher.getAuthTag() };
|
|
46952
|
+
}
|
|
46953
|
+
function decryptWithKey(key, sealed) {
|
|
46954
|
+
if (key.byteLength !== KEY_LENGTH) {
|
|
46955
|
+
throw new Error(`Expected a ${KEY_LENGTH}-byte AES-256-GCM key.`);
|
|
46956
|
+
}
|
|
46957
|
+
if (sealed.nonce.byteLength !== 12) {
|
|
46958
|
+
throw new Error("Expected a 12-byte AES-GCM nonce.");
|
|
46959
|
+
}
|
|
46960
|
+
if (sealed.tag.byteLength !== AUTH_TAG_LENGTH) {
|
|
46961
|
+
throw new Error(`Expected a ${AUTH_TAG_LENGTH}-byte AES-GCM authentication tag.`);
|
|
46962
|
+
}
|
|
46963
|
+
const decipher = createDecipheriv("aes-256-gcm", key, sealed.nonce, {
|
|
46964
|
+
authTagLength: AUTH_TAG_LENGTH
|
|
46965
|
+
});
|
|
46966
|
+
decipher.setAuthTag(sealed.tag);
|
|
46967
|
+
return Buffer.concat([decipher.update(sealed.ciphertext), decipher.final()]);
|
|
46968
|
+
}
|
|
46969
|
+
async function deriveScryptKey(passphrase, salt) {
|
|
46916
46970
|
return await new Promise((resolve, reject) => {
|
|
46917
46971
|
scrypt(
|
|
46918
46972
|
passphrase,
|
|
@@ -46974,16 +47028,14 @@ async function sealPlaintext(plaintext, passphrase) {
|
|
|
46974
47028
|
throw new KeystoreError("Keystore passphrase cannot be empty.");
|
|
46975
47029
|
}
|
|
46976
47030
|
const salt = randomBytes2(32);
|
|
46977
|
-
const
|
|
46978
|
-
const key = await deriveKey(passphrase, salt);
|
|
47031
|
+
const key = await deriveScryptKey(passphrase, salt);
|
|
46979
47032
|
try {
|
|
46980
|
-
const
|
|
46981
|
-
const ciphertext = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
47033
|
+
const sealed = encryptWithKey(key, Buffer.from(plaintext, "utf8"));
|
|
46982
47034
|
return {
|
|
46983
47035
|
cipher: "aes-256-gcm",
|
|
46984
|
-
ciphertext: ciphertext.toString("base64"),
|
|
46985
|
-
iv:
|
|
46986
|
-
authTag:
|
|
47036
|
+
ciphertext: sealed.ciphertext.toString("base64"),
|
|
47037
|
+
iv: sealed.nonce.toString("base64"),
|
|
47038
|
+
authTag: sealed.tag.toString("base64"),
|
|
46987
47039
|
kdf: "scrypt",
|
|
46988
47040
|
salt: salt.toString("base64"),
|
|
46989
47041
|
kdfParams: {
|
|
@@ -47011,19 +47063,14 @@ async function decryptSecret(keystore, passphrase) {
|
|
|
47011
47063
|
const iv = Buffer.from(parsed.crypto.iv, "base64");
|
|
47012
47064
|
const authTag = Buffer.from(parsed.crypto.authTag, "base64");
|
|
47013
47065
|
const ciphertext = Buffer.from(parsed.crypto.ciphertext, "base64");
|
|
47014
|
-
const key = await
|
|
47066
|
+
const key = await deriveScryptKey(passphrase, salt);
|
|
47015
47067
|
let unlocked;
|
|
47016
47068
|
try {
|
|
47017
|
-
|
|
47018
|
-
|
|
47019
|
-
|
|
47020
|
-
|
|
47021
|
-
|
|
47022
|
-
});
|
|
47023
|
-
decipher.setAuthTag(authTag);
|
|
47024
|
-
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString(
|
|
47025
|
-
"utf8"
|
|
47026
|
-
);
|
|
47069
|
+
const plaintext = decryptWithKey(key, {
|
|
47070
|
+
nonce: iv,
|
|
47071
|
+
ciphertext,
|
|
47072
|
+
tag: authTag
|
|
47073
|
+
}).toString("utf8");
|
|
47027
47074
|
unlocked = parsed.version === 3 ? keysFromEntropy(parsed, plaintext) : {
|
|
47028
47075
|
keys: parsed.version === 1 ? { evm: `0x${plaintext}` } : encryptedKeysSchema.parse(JSON.parse(plaintext)).keys
|
|
47029
47076
|
};
|
|
@@ -47108,6 +47155,15 @@ async function refuseExistingKeystore(path) {
|
|
|
47108
47155
|
}
|
|
47109
47156
|
}
|
|
47110
47157
|
}
|
|
47158
|
+
function validatePrivateKey(value) {
|
|
47159
|
+
const normalized = value.trim().toLowerCase();
|
|
47160
|
+
if (!/^0x[0-9a-f]{64}$/u.test(normalized)) {
|
|
47161
|
+
throw new KeystoreError(
|
|
47162
|
+
"A private key is 0x followed by 64 hexadecimal characters. Check for a missing character."
|
|
47163
|
+
);
|
|
47164
|
+
}
|
|
47165
|
+
return normalized;
|
|
47166
|
+
}
|
|
47111
47167
|
async function readStoredKeystore(path) {
|
|
47112
47168
|
let raw;
|
|
47113
47169
|
try {
|
|
@@ -47204,10 +47260,526 @@ async function writeKeystore(path, keystore, exclusive) {
|
|
|
47204
47260
|
}
|
|
47205
47261
|
await rename2(temporaryPath, path);
|
|
47206
47262
|
}
|
|
47263
|
+
|
|
47264
|
+
// src/vault.ts
|
|
47265
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
47266
|
+
import { mkdir as mkdir3, open as open2, readFile as readFile3, rename as rename3, rm, stat as stat3, unlink, writeFile as writeFile3 } from "node:fs/promises";
|
|
47267
|
+
import { dirname as dirname3 } from "node:path";
|
|
47268
|
+
|
|
47269
|
+
// src/wallet-name.ts
|
|
47270
|
+
var WALLET_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
47271
|
+
var walletNameSchema = external_exports.string().regex(
|
|
47272
|
+
WALLET_NAME_PATTERN,
|
|
47273
|
+
"A wallet name is 1 to 32 characters of lowercase letters, digits and dashes, starting with a letter or digit."
|
|
47274
|
+
);
|
|
47275
|
+
|
|
47276
|
+
// src/vault.ts
|
|
47277
|
+
var VaultError = class extends Error {
|
|
47278
|
+
code;
|
|
47279
|
+
constructor(code, message, options) {
|
|
47280
|
+
super(message, options);
|
|
47281
|
+
this.name = "VaultError";
|
|
47282
|
+
this.code = code;
|
|
47283
|
+
}
|
|
47284
|
+
};
|
|
47285
|
+
var SEALED_FIELD_PATTERN = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/u;
|
|
47286
|
+
var WRAPPED_KEY_PATTERN = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/u;
|
|
47287
|
+
var ADDRESS_PATTERN = /^0x[0-9a-fA-F]{40}$/u;
|
|
47288
|
+
var VAULT_LOCK_TIMEOUT_MS = 2e3;
|
|
47289
|
+
var STALE_VAULT_LOCK_MS = 3e4;
|
|
47290
|
+
var sealedFieldSchema = external_exports.string().regex(SEALED_FIELD_PATTERN);
|
|
47291
|
+
var addressSchema = external_exports.string().regex(ADDRESS_PATTERN);
|
|
47292
|
+
var accountFields = {
|
|
47293
|
+
name: walletNameSchema,
|
|
47294
|
+
address: addressSchema,
|
|
47295
|
+
createdAt: external_exports.string()
|
|
47296
|
+
};
|
|
47297
|
+
var storedAccountSchema = external_exports.discriminatedUnion("kind", [
|
|
47298
|
+
external_exports.object({
|
|
47299
|
+
...accountFields,
|
|
47300
|
+
kind: external_exports.literal("derived"),
|
|
47301
|
+
index: external_exports.number().int().nonnegative().refine(Number.isSafeInteger, "Derived account index must be a safe integer.")
|
|
47302
|
+
}).strict(),
|
|
47303
|
+
external_exports.object({
|
|
47304
|
+
...accountFields,
|
|
47305
|
+
kind: external_exports.literal("imported"),
|
|
47306
|
+
key: sealedFieldSchema
|
|
47307
|
+
}).strict()
|
|
47308
|
+
]);
|
|
47309
|
+
var vaultFileSchema = external_exports.object({
|
|
47310
|
+
version: external_exports.literal(2),
|
|
47311
|
+
kdf: external_exports.literal("device-key"),
|
|
47312
|
+
cipher: external_exports.literal("aes-256-gcm"),
|
|
47313
|
+
phrase: sealedFieldSchema,
|
|
47314
|
+
accounts: external_exports.array(storedAccountSchema),
|
|
47315
|
+
nextDerivedIndex: external_exports.number().int().nonnegative().refine(Number.isSafeInteger, "Next derived account index must be a safe integer.").optional(),
|
|
47316
|
+
protected: external_exports.boolean(),
|
|
47317
|
+
wrappedKey: external_exports.string().regex(WRAPPED_KEY_PATTERN).optional()
|
|
47318
|
+
}).strict();
|
|
47319
|
+
var DeviceVault = class {
|
|
47320
|
+
file;
|
|
47321
|
+
path;
|
|
47322
|
+
key;
|
|
47323
|
+
balanceOf;
|
|
47324
|
+
now;
|
|
47325
|
+
constructor(options, file2) {
|
|
47326
|
+
this.path = options.path;
|
|
47327
|
+
this.key = options.key;
|
|
47328
|
+
this.balanceOf = options.balanceOf;
|
|
47329
|
+
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
47330
|
+
this.file = file2;
|
|
47331
|
+
}
|
|
47332
|
+
accounts() {
|
|
47333
|
+
return this.file.accounts.map(copyAccount);
|
|
47334
|
+
}
|
|
47335
|
+
async deriveAccount(name) {
|
|
47336
|
+
try {
|
|
47337
|
+
return await this.update(async (file2) => {
|
|
47338
|
+
assertAvailableName(file2, name);
|
|
47339
|
+
const index = nextDerivedIndex(file2);
|
|
47340
|
+
const plaintext = decryptField(this.key, file2.phrase);
|
|
47341
|
+
let seed;
|
|
47342
|
+
let address;
|
|
47343
|
+
try {
|
|
47344
|
+
seed = phraseToSeed(plaintext.toString("utf8"));
|
|
47345
|
+
address = privateKeyToAccount(
|
|
47346
|
+
deriveEvmPrivateKey(seed, `m/44'/60'/0'/0/${index}`)
|
|
47347
|
+
).address;
|
|
47348
|
+
} finally {
|
|
47349
|
+
seed?.fill(0);
|
|
47350
|
+
plaintext.fill(0);
|
|
47351
|
+
}
|
|
47352
|
+
const account = {
|
|
47353
|
+
name,
|
|
47354
|
+
kind: "derived",
|
|
47355
|
+
index,
|
|
47356
|
+
address,
|
|
47357
|
+
createdAt: this.now().toISOString()
|
|
47358
|
+
};
|
|
47359
|
+
file2.accounts.push(account);
|
|
47360
|
+
file2.nextDerivedIndex = index + 1;
|
|
47361
|
+
return copyAccount(account);
|
|
47362
|
+
});
|
|
47363
|
+
} catch (error61) {
|
|
47364
|
+
throw asVaultError(error61, "corrupt", "Unable to derive a vault account.");
|
|
47365
|
+
}
|
|
47366
|
+
}
|
|
47367
|
+
async importAccount(name, privateKey) {
|
|
47368
|
+
try {
|
|
47369
|
+
return await this.update(async (file2) => {
|
|
47370
|
+
assertAvailableName(file2, name);
|
|
47371
|
+
let validated;
|
|
47372
|
+
let address;
|
|
47373
|
+
try {
|
|
47374
|
+
validated = validatePrivateKey(privateKey);
|
|
47375
|
+
address = privateKeyToAccount(validated).address;
|
|
47376
|
+
} catch (error61) {
|
|
47377
|
+
throw new VaultError("bad_key", "The imported private key is not valid.", {
|
|
47378
|
+
cause: error61
|
|
47379
|
+
});
|
|
47380
|
+
}
|
|
47381
|
+
const plaintext = Buffer.from(validated.slice(2), "hex");
|
|
47382
|
+
let account;
|
|
47383
|
+
try {
|
|
47384
|
+
account = {
|
|
47385
|
+
name,
|
|
47386
|
+
kind: "imported",
|
|
47387
|
+
address,
|
|
47388
|
+
key: sealField(this.key, plaintext),
|
|
47389
|
+
createdAt: this.now().toISOString()
|
|
47390
|
+
};
|
|
47391
|
+
} finally {
|
|
47392
|
+
plaintext.fill(0);
|
|
47393
|
+
}
|
|
47394
|
+
file2.accounts.push(account);
|
|
47395
|
+
return copyAccount(account);
|
|
47396
|
+
});
|
|
47397
|
+
} catch (error61) {
|
|
47398
|
+
throw asVaultError(error61, "corrupt", "Unable to import a vault account.");
|
|
47399
|
+
}
|
|
47400
|
+
}
|
|
47401
|
+
async removeAccount(name) {
|
|
47402
|
+
try {
|
|
47403
|
+
await this.update(async (file2) => {
|
|
47404
|
+
const account = file2.accounts.find((candidate) => candidate.name === name);
|
|
47405
|
+
if (!account) {
|
|
47406
|
+
throw new VaultError("not_found", "No vault account has that name.");
|
|
47407
|
+
}
|
|
47408
|
+
if (!this.balanceOf) {
|
|
47409
|
+
throw new VaultError(
|
|
47410
|
+
"has_balance",
|
|
47411
|
+
"The account balance could not be verified, so the key was not removed."
|
|
47412
|
+
);
|
|
47413
|
+
}
|
|
47414
|
+
if (await this.balanceOf(account.address) > 0n) {
|
|
47415
|
+
throw new VaultError(
|
|
47416
|
+
"has_balance",
|
|
47417
|
+
"This account still has a balance. Run vapi sweep before removing it."
|
|
47418
|
+
);
|
|
47419
|
+
}
|
|
47420
|
+
file2.nextDerivedIndex = nextDerivedIndex(file2);
|
|
47421
|
+
const currentIndex = file2.accounts.findIndex((candidate) => candidate.name === name);
|
|
47422
|
+
if (currentIndex === -1) {
|
|
47423
|
+
throw new VaultError("not_found", "No vault account has that name.");
|
|
47424
|
+
}
|
|
47425
|
+
file2.accounts.splice(currentIndex, 1);
|
|
47426
|
+
});
|
|
47427
|
+
} catch (error61) {
|
|
47428
|
+
throw asVaultError(error61, "corrupt", "Unable to remove the vault account.");
|
|
47429
|
+
}
|
|
47430
|
+
}
|
|
47431
|
+
async rename(from, to) {
|
|
47432
|
+
try {
|
|
47433
|
+
await this.update(async (file2) => {
|
|
47434
|
+
const account = file2.accounts.find((candidate) => candidate.name === from);
|
|
47435
|
+
if (!account) {
|
|
47436
|
+
throw new VaultError("not_found", "No vault account has that name.");
|
|
47437
|
+
}
|
|
47438
|
+
if (from === to) return;
|
|
47439
|
+
assertAccountName(to);
|
|
47440
|
+
if (file2.accounts.some((candidate) => candidate.name === to)) {
|
|
47441
|
+
throw new VaultError("name_taken", "A vault account already has that name.");
|
|
47442
|
+
}
|
|
47443
|
+
account.name = to;
|
|
47444
|
+
});
|
|
47445
|
+
} catch (error61) {
|
|
47446
|
+
throw asVaultError(error61, "corrupt", "Unable to rename the vault account.");
|
|
47447
|
+
}
|
|
47448
|
+
}
|
|
47449
|
+
async signer(name) {
|
|
47450
|
+
try {
|
|
47451
|
+
const latest = await readVaultFile(this.path, this.key);
|
|
47452
|
+
this.file = latest;
|
|
47453
|
+
const stored = latest.accounts.find((account2) => account2.name === name);
|
|
47454
|
+
if (!stored) {
|
|
47455
|
+
throw new VaultError("not_found", "No vault account has that name.");
|
|
47456
|
+
}
|
|
47457
|
+
let account;
|
|
47458
|
+
if (stored.kind === "derived") {
|
|
47459
|
+
const plaintext = decryptField(this.key, latest.phrase);
|
|
47460
|
+
let seed;
|
|
47461
|
+
try {
|
|
47462
|
+
seed = phraseToSeed(plaintext.toString("utf8"));
|
|
47463
|
+
account = privateKeyToAccount(
|
|
47464
|
+
deriveEvmPrivateKey(seed, `m/44'/60'/0'/0/${stored.index}`)
|
|
47465
|
+
);
|
|
47466
|
+
} finally {
|
|
47467
|
+
seed?.fill(0);
|
|
47468
|
+
plaintext.fill(0);
|
|
47469
|
+
}
|
|
47470
|
+
} else {
|
|
47471
|
+
const plaintext = decryptField(this.key, stored.key);
|
|
47472
|
+
try {
|
|
47473
|
+
if (plaintext.byteLength !== 32) {
|
|
47474
|
+
throw new VaultError("corrupt", "An imported vault key has the wrong length.");
|
|
47475
|
+
}
|
|
47476
|
+
const privateKey = `0x${plaintext.toString("hex")}`;
|
|
47477
|
+
account = privateKeyToAccount(privateKey);
|
|
47478
|
+
} finally {
|
|
47479
|
+
plaintext.fill(0);
|
|
47480
|
+
}
|
|
47481
|
+
}
|
|
47482
|
+
if (account.address.toLowerCase() !== stored.address.toLowerCase()) {
|
|
47483
|
+
throw new VaultError("corrupt", "A vault key does not match its stored address.");
|
|
47484
|
+
}
|
|
47485
|
+
return account;
|
|
47486
|
+
} catch (error61) {
|
|
47487
|
+
throw asVaultError(error61, "corrupt", "Unable to open the vault account signer.");
|
|
47488
|
+
}
|
|
47489
|
+
}
|
|
47490
|
+
async phrase() {
|
|
47491
|
+
const latest = await readVaultFile(this.path, this.key);
|
|
47492
|
+
this.file = latest;
|
|
47493
|
+
const plaintext = decryptField(this.key, latest.phrase);
|
|
47494
|
+
try {
|
|
47495
|
+
try {
|
|
47496
|
+
return validateRecoveryPhrase(plaintext.toString("utf8"));
|
|
47497
|
+
} catch (error61) {
|
|
47498
|
+
throw new VaultError("corrupt", "The vault recovery phrase is not valid.", {
|
|
47499
|
+
cause: error61
|
|
47500
|
+
});
|
|
47501
|
+
}
|
|
47502
|
+
} finally {
|
|
47503
|
+
plaintext.fill(0);
|
|
47504
|
+
}
|
|
47505
|
+
}
|
|
47506
|
+
async save() {
|
|
47507
|
+
try {
|
|
47508
|
+
await this.update(async () => void 0);
|
|
47509
|
+
} catch (error61) {
|
|
47510
|
+
throw asVaultError(error61, "corrupt", "Unable to save the vault.");
|
|
47511
|
+
}
|
|
47512
|
+
}
|
|
47513
|
+
resealSecrets(file2) {
|
|
47514
|
+
const phrase = decryptField(this.key, file2.phrase);
|
|
47515
|
+
try {
|
|
47516
|
+
const accounts = file2.accounts.map((account) => {
|
|
47517
|
+
if (account.kind === "derived") return { ...account };
|
|
47518
|
+
const privateKey = decryptField(this.key, account.key);
|
|
47519
|
+
try {
|
|
47520
|
+
if (privateKey.byteLength !== 32) {
|
|
47521
|
+
throw new VaultError("corrupt", "An imported vault key has the wrong length.");
|
|
47522
|
+
}
|
|
47523
|
+
return { ...account, key: sealField(this.key, privateKey) };
|
|
47524
|
+
} finally {
|
|
47525
|
+
privateKey.fill(0);
|
|
47526
|
+
}
|
|
47527
|
+
});
|
|
47528
|
+
return {
|
|
47529
|
+
...file2,
|
|
47530
|
+
phrase: sealField(this.key, phrase),
|
|
47531
|
+
accounts
|
|
47532
|
+
};
|
|
47533
|
+
} finally {
|
|
47534
|
+
phrase.fill(0);
|
|
47535
|
+
}
|
|
47536
|
+
}
|
|
47537
|
+
async update(operation) {
|
|
47538
|
+
return await withVaultLock(this.path, async () => {
|
|
47539
|
+
const latest = await readVaultFile(this.path, this.key);
|
|
47540
|
+
const result = await operation(latest);
|
|
47541
|
+
const rewritten = this.resealSecrets(latest);
|
|
47542
|
+
await writeVaultFile(this.path, rewritten);
|
|
47543
|
+
this.file = rewritten;
|
|
47544
|
+
return result;
|
|
47545
|
+
});
|
|
47546
|
+
}
|
|
47547
|
+
};
|
|
47548
|
+
async function openVault(args) {
|
|
47549
|
+
assertVaultKey(args.key);
|
|
47550
|
+
return new DeviceVault(args, await readVaultFile(args.path, args.key));
|
|
47551
|
+
}
|
|
47552
|
+
async function exportVaultPhrase(args) {
|
|
47553
|
+
return await (await openVault(args)).phrase();
|
|
47554
|
+
}
|
|
47555
|
+
async function exportVaultAccountKey(args) {
|
|
47556
|
+
assertVaultKey(args.key);
|
|
47557
|
+
try {
|
|
47558
|
+
const file2 = await readVaultFile(args.path, args.key);
|
|
47559
|
+
const account = file2.accounts.find((candidate) => candidate.name === args.name);
|
|
47560
|
+
if (!account) {
|
|
47561
|
+
throw new VaultError("not_found", "No vault account has that name.");
|
|
47562
|
+
}
|
|
47563
|
+
let privateKey;
|
|
47564
|
+
if (account.kind === "derived") {
|
|
47565
|
+
const plaintext = decryptField(args.key, file2.phrase);
|
|
47566
|
+
let seed;
|
|
47567
|
+
try {
|
|
47568
|
+
seed = phraseToSeed(plaintext.toString("utf8"));
|
|
47569
|
+
privateKey = deriveEvmPrivateKey(seed, `m/44'/60'/0'/0/${account.index}`);
|
|
47570
|
+
} finally {
|
|
47571
|
+
seed?.fill(0);
|
|
47572
|
+
plaintext.fill(0);
|
|
47573
|
+
}
|
|
47574
|
+
} else {
|
|
47575
|
+
const plaintext = decryptField(args.key, account.key);
|
|
47576
|
+
try {
|
|
47577
|
+
if (plaintext.byteLength !== 32) {
|
|
47578
|
+
throw new VaultError("corrupt", "An imported vault key has the wrong length.");
|
|
47579
|
+
}
|
|
47580
|
+
privateKey = `0x${plaintext.toString("hex")}`;
|
|
47581
|
+
} finally {
|
|
47582
|
+
plaintext.fill(0);
|
|
47583
|
+
}
|
|
47584
|
+
}
|
|
47585
|
+
if (privateKeyToAccount(privateKey).address.toLowerCase() !== account.address.toLowerCase()) {
|
|
47586
|
+
throw new VaultError("corrupt", "A vault key does not match its stored address.");
|
|
47587
|
+
}
|
|
47588
|
+
return privateKey;
|
|
47589
|
+
} catch (error61) {
|
|
47590
|
+
throw asVaultError(error61, "corrupt", "Unable to export the vault account key.");
|
|
47591
|
+
}
|
|
47592
|
+
}
|
|
47593
|
+
function assertVaultKey(key) {
|
|
47594
|
+
if (key.byteLength !== 32) {
|
|
47595
|
+
throw new VaultError("bad_key", "A device vault key must contain exactly 32 bytes.");
|
|
47596
|
+
}
|
|
47597
|
+
}
|
|
47598
|
+
function assertAccountName(name) {
|
|
47599
|
+
const parsed = walletNameSchema.safeParse(name);
|
|
47600
|
+
if (!parsed.success) {
|
|
47601
|
+
throw new VaultError("name_taken", "The vault account name is not valid.", {
|
|
47602
|
+
cause: parsed.error
|
|
47603
|
+
});
|
|
47604
|
+
}
|
|
47605
|
+
}
|
|
47606
|
+
function assertAvailableName(file2, name) {
|
|
47607
|
+
assertAccountName(name);
|
|
47608
|
+
if (file2.accounts.some((account) => account.name === name)) {
|
|
47609
|
+
throw new VaultError("name_taken", "A vault account already has that name.");
|
|
47610
|
+
}
|
|
47611
|
+
}
|
|
47612
|
+
function nextDerivedIndex(file2) {
|
|
47613
|
+
let next = file2.nextDerivedIndex ?? 0;
|
|
47614
|
+
for (const account of file2.accounts) {
|
|
47615
|
+
if (account.kind === "derived") next = Math.max(next, account.index + 1);
|
|
47616
|
+
}
|
|
47617
|
+
return next;
|
|
47618
|
+
}
|
|
47619
|
+
function copyAccount(account) {
|
|
47620
|
+
if (account.kind === "derived") return { ...account };
|
|
47621
|
+
return {
|
|
47622
|
+
name: account.name,
|
|
47623
|
+
kind: account.kind,
|
|
47624
|
+
address: account.address,
|
|
47625
|
+
createdAt: account.createdAt
|
|
47626
|
+
};
|
|
47627
|
+
}
|
|
47628
|
+
function sealField(key, plaintext) {
|
|
47629
|
+
const { nonce, ciphertext, tag } = encryptWithKey(key, plaintext);
|
|
47630
|
+
return [nonce, ciphertext, tag].map((part) => part.toString("base64url")).join(".");
|
|
47631
|
+
}
|
|
47632
|
+
function openField(key, value) {
|
|
47633
|
+
const [nonce = "", ciphertext = "", tag = ""] = value.split(".");
|
|
47634
|
+
return decryptWithKey(key, {
|
|
47635
|
+
nonce: Buffer.from(nonce, "base64url"),
|
|
47636
|
+
ciphertext: Buffer.from(ciphertext, "base64url"),
|
|
47637
|
+
tag: Buffer.from(tag, "base64url")
|
|
47638
|
+
});
|
|
47639
|
+
}
|
|
47640
|
+
function decryptField(key, value) {
|
|
47641
|
+
try {
|
|
47642
|
+
return openField(key, value);
|
|
47643
|
+
} catch (error61) {
|
|
47644
|
+
throw new VaultError("bad_key", "Unable to decrypt the vault with this device key.", {
|
|
47645
|
+
cause: error61
|
|
47646
|
+
});
|
|
47647
|
+
}
|
|
47648
|
+
}
|
|
47649
|
+
function decryptPhraseForOpen(key, phrase) {
|
|
47650
|
+
try {
|
|
47651
|
+
return openField(key, phrase);
|
|
47652
|
+
} catch (error61) {
|
|
47653
|
+
throw new VaultError("bad_key", "Unable to decrypt the vault with this device key.", {
|
|
47654
|
+
cause: error61
|
|
47655
|
+
});
|
|
47656
|
+
}
|
|
47657
|
+
}
|
|
47658
|
+
function parseVaultFile(raw) {
|
|
47659
|
+
let value;
|
|
47660
|
+
try {
|
|
47661
|
+
value = JSON.parse(raw);
|
|
47662
|
+
} catch (error61) {
|
|
47663
|
+
throw new VaultError("corrupt", "The vault file is not valid JSON.", { cause: error61 });
|
|
47664
|
+
}
|
|
47665
|
+
const parsed = vaultFileSchema.safeParse(value);
|
|
47666
|
+
if (!parsed.success) {
|
|
47667
|
+
throw new VaultError("corrupt", "The vault file has an invalid structure.", {
|
|
47668
|
+
cause: parsed.error
|
|
47669
|
+
});
|
|
47670
|
+
}
|
|
47671
|
+
if (parsed.data.protected !== (parsed.data.wrappedKey !== void 0)) {
|
|
47672
|
+
throw new VaultError("corrupt", "The vault protection metadata is inconsistent.");
|
|
47673
|
+
}
|
|
47674
|
+
const names = /* @__PURE__ */ new Set();
|
|
47675
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
47676
|
+
for (const account of parsed.data.accounts) {
|
|
47677
|
+
if (names.has(account.name)) {
|
|
47678
|
+
throw new VaultError("corrupt", "The vault file contains duplicate account names.");
|
|
47679
|
+
}
|
|
47680
|
+
names.add(account.name);
|
|
47681
|
+
if (account.kind === "derived") {
|
|
47682
|
+
if (indexes.has(account.index)) {
|
|
47683
|
+
throw new VaultError("corrupt", "The vault file contains duplicate derived indexes.");
|
|
47684
|
+
}
|
|
47685
|
+
indexes.add(account.index);
|
|
47686
|
+
}
|
|
47687
|
+
}
|
|
47688
|
+
return parsed.data;
|
|
47689
|
+
}
|
|
47690
|
+
async function readVaultFile(path, key) {
|
|
47691
|
+
const file2 = await readVaultFileUnlocked(path);
|
|
47692
|
+
const plaintext = decryptPhraseForOpen(key, file2.phrase);
|
|
47693
|
+
try {
|
|
47694
|
+
try {
|
|
47695
|
+
validateRecoveryPhrase(plaintext.toString("utf8"));
|
|
47696
|
+
} catch (error61) {
|
|
47697
|
+
throw new VaultError("corrupt", "The vault recovery phrase is not valid.", {
|
|
47698
|
+
cause: error61
|
|
47699
|
+
});
|
|
47700
|
+
}
|
|
47701
|
+
} finally {
|
|
47702
|
+
plaintext.fill(0);
|
|
47703
|
+
}
|
|
47704
|
+
return file2;
|
|
47705
|
+
}
|
|
47706
|
+
async function readVaultFileUnlocked(path) {
|
|
47707
|
+
let raw;
|
|
47708
|
+
try {
|
|
47709
|
+
raw = await readFile3(path, "utf8");
|
|
47710
|
+
} catch (error61) {
|
|
47711
|
+
if (isMissingFile2(error61)) {
|
|
47712
|
+
throw new VaultError("not_found", "No vault file was found at the requested path.", {
|
|
47713
|
+
cause: error61
|
|
47714
|
+
});
|
|
47715
|
+
}
|
|
47716
|
+
throw new VaultError("corrupt", "Unable to read the vault file.", { cause: error61 });
|
|
47717
|
+
}
|
|
47718
|
+
return parseVaultFile(raw);
|
|
47719
|
+
}
|
|
47720
|
+
async function writeVaultFile(path, file2) {
|
|
47721
|
+
await mkdir3(dirname3(path), { recursive: true, mode: 448 });
|
|
47722
|
+
const temporaryPath = `${path}.${process.pid}.${randomBytes3(6).toString("hex")}.tmp`;
|
|
47723
|
+
try {
|
|
47724
|
+
await writeFile3(temporaryPath, `${JSON.stringify(file2, null, 2)}
|
|
47725
|
+
`, {
|
|
47726
|
+
encoding: "utf8",
|
|
47727
|
+
mode: 384,
|
|
47728
|
+
flag: "wx"
|
|
47729
|
+
});
|
|
47730
|
+
await rename3(temporaryPath, path);
|
|
47731
|
+
} catch (error61) {
|
|
47732
|
+
await rm(temporaryPath, { force: true }).catch(() => void 0);
|
|
47733
|
+
throw error61;
|
|
47734
|
+
}
|
|
47735
|
+
}
|
|
47736
|
+
async function withVaultLock(path, operation) {
|
|
47737
|
+
await mkdir3(dirname3(path), { recursive: true, mode: 448 });
|
|
47738
|
+
const lockPath = `${path}.lock`;
|
|
47739
|
+
const deadline = Date.now() + VAULT_LOCK_TIMEOUT_MS;
|
|
47740
|
+
let handle;
|
|
47741
|
+
while (!handle) {
|
|
47742
|
+
try {
|
|
47743
|
+
handle = await open2(lockPath, "wx", 384);
|
|
47744
|
+
} catch (error61) {
|
|
47745
|
+
if (!isExistingFile(error61)) throw error61;
|
|
47746
|
+
await removeStaleVaultLock(lockPath);
|
|
47747
|
+
if (Date.now() >= deadline) {
|
|
47748
|
+
throw new VaultError("corrupt", "Timed out waiting for the vault lock.");
|
|
47749
|
+
}
|
|
47750
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
47751
|
+
}
|
|
47752
|
+
}
|
|
47753
|
+
try {
|
|
47754
|
+
return await operation();
|
|
47755
|
+
} finally {
|
|
47756
|
+
await handle.close();
|
|
47757
|
+
await unlink(lockPath).catch(() => void 0);
|
|
47758
|
+
}
|
|
47759
|
+
}
|
|
47760
|
+
async function removeStaleVaultLock(path) {
|
|
47761
|
+
try {
|
|
47762
|
+
const metadata = await stat3(path);
|
|
47763
|
+
if (Date.now() - metadata.mtimeMs > STALE_VAULT_LOCK_MS) await unlink(path);
|
|
47764
|
+
} catch (error61) {
|
|
47765
|
+
if (!isMissingFile2(error61)) throw error61;
|
|
47766
|
+
}
|
|
47767
|
+
}
|
|
47768
|
+
function isMissingFile2(error61) {
|
|
47769
|
+
return typeof error61 === "object" && error61 !== null && "code" in error61 && Reflect.get(error61, "code") === "ENOENT";
|
|
47770
|
+
}
|
|
47771
|
+
function isExistingFile(error61) {
|
|
47772
|
+
return typeof error61 === "object" && error61 !== null && "code" in error61 && Reflect.get(error61, "code") === "EEXIST";
|
|
47773
|
+
}
|
|
47774
|
+
function asVaultError(error61, code, message) {
|
|
47775
|
+
return error61 instanceof VaultError ? error61 : new VaultError(code, message, { cause: error61 });
|
|
47776
|
+
}
|
|
47207
47777
|
export {
|
|
47208
47778
|
createKeystoreWithPhrase,
|
|
47209
47779
|
decryptPrivateKey,
|
|
47210
47780
|
exportKeystoreKeys,
|
|
47211
|
-
exportRecoveryPhrase
|
|
47781
|
+
exportRecoveryPhrase,
|
|
47782
|
+
exportVaultAccountKey,
|
|
47783
|
+
exportVaultPhrase
|
|
47212
47784
|
};
|
|
47213
47785
|
//# sourceMappingURL=secrets.js.map
|