zk-agent-cli 0.1.0-beta.4 → 0.1.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -74,10 +74,215 @@ function isAgentError(error) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// ../agent-core/src/storage.ts
|
|
77
|
-
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
77
|
+
import { createCipheriv, createDecipheriv, randomBytes as randomBytes2 } from "node:crypto";
|
|
78
78
|
import fs from "node:fs";
|
|
79
79
|
import os from "node:os";
|
|
80
80
|
import path from "node:path";
|
|
81
|
+
|
|
82
|
+
// ../agent-session-protocol/src/constants.ts
|
|
83
|
+
var PROTOCOL_VERSION = "zk-agent-session-v1";
|
|
84
|
+
|
|
85
|
+
// ../agent-session-protocol/src/encoding.ts
|
|
86
|
+
function bytesToHex(bytes) {
|
|
87
|
+
return Array.from(bytes).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
88
|
+
}
|
|
89
|
+
function hexToBytes(hex) {
|
|
90
|
+
if (hex.length % 2 !== 0) throw new Error("Invalid hex string");
|
|
91
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
92
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
93
|
+
bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
|
|
94
|
+
}
|
|
95
|
+
return bytes;
|
|
96
|
+
}
|
|
97
|
+
function b64urlEncode(bytes) {
|
|
98
|
+
let binary = "";
|
|
99
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
100
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
101
|
+
}
|
|
102
|
+
function b64urlDecode(value) {
|
|
103
|
+
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
104
|
+
const padding = normalized.length % 4 === 0 ? "" : "=".repeat(4 - normalized.length % 4);
|
|
105
|
+
const binary = atob(normalized + padding);
|
|
106
|
+
const bytes = new Uint8Array(binary.length);
|
|
107
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
108
|
+
bytes[index] = binary.charCodeAt(index);
|
|
109
|
+
}
|
|
110
|
+
return bytes;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ../agent-session-protocol/src/crypto.ts
|
|
114
|
+
import { xchacha20poly1305 } from "@noble/ciphers/chacha";
|
|
115
|
+
import { x25519 } from "@noble/curves/ed25519";
|
|
116
|
+
import { secp256k1 } from "@noble/curves/secp256k1";
|
|
117
|
+
import { hkdf } from "@noble/hashes/hkdf";
|
|
118
|
+
import { sha256 } from "@noble/hashes/sha2";
|
|
119
|
+
import { keccak_256 } from "@noble/hashes/sha3";
|
|
120
|
+
import { randomBytes } from "@noble/hashes/utils";
|
|
121
|
+
function deriveEthereumAddressFromPrivateKey(privateKeyHex) {
|
|
122
|
+
const normalized = privateKeyHex.startsWith("0x") ? privateKeyHex.slice(2) : privateKeyHex;
|
|
123
|
+
if (!/^[0-9a-fA-F]{64}$/.test(normalized)) {
|
|
124
|
+
throw new Error("sessionPrivateKey must be a 32-byte hex string");
|
|
125
|
+
}
|
|
126
|
+
const publicKey = secp256k1.getPublicKey(hexToBytes(normalized), false);
|
|
127
|
+
const digest = keccak_256(publicKey.slice(1));
|
|
128
|
+
return `0x${bytesToHex(digest.slice(-20))}`;
|
|
129
|
+
}
|
|
130
|
+
function generateX25519Keypair() {
|
|
131
|
+
const secretKey = randomBytes(32);
|
|
132
|
+
const publicKey = x25519.getPublicKey(secretKey);
|
|
133
|
+
return { secretKey, publicKey };
|
|
134
|
+
}
|
|
135
|
+
function computeCodeHash(requestId, code) {
|
|
136
|
+
if (!requestId) throw new Error("requestId must not be empty");
|
|
137
|
+
return sha256(new TextEncoder().encode(requestId + code));
|
|
138
|
+
}
|
|
139
|
+
function deriveEncryptionKey(sharedSecret, code, cliPublicKeyHex, walletPublicKeyHex) {
|
|
140
|
+
const salt = sha256(new TextEncoder().encode(code));
|
|
141
|
+
const info = new TextEncoder().encode(cliPublicKeyHex + walletPublicKeyHex + PROTOCOL_VERSION);
|
|
142
|
+
return hkdf(sha256, sharedSecret, salt, info, 32);
|
|
143
|
+
}
|
|
144
|
+
function decryptSession(encrypted, cliSecretKey, code, requestId) {
|
|
145
|
+
if (!requestId) throw new Error("requestId must not be empty");
|
|
146
|
+
const cliPublicKey = x25519.getPublicKey(cliSecretKey);
|
|
147
|
+
const walletPublicKey = hexToBytes(encrypted.wallet_pk_hex);
|
|
148
|
+
const sharedSecret = x25519.getSharedSecret(cliSecretKey, walletPublicKey);
|
|
149
|
+
const cliPublicKeyHex = bytesToHex(cliPublicKey);
|
|
150
|
+
const encryptionKey = deriveEncryptionKey(
|
|
151
|
+
sharedSecret,
|
|
152
|
+
code,
|
|
153
|
+
cliPublicKeyHex,
|
|
154
|
+
encrypted.wallet_pk_hex
|
|
155
|
+
);
|
|
156
|
+
const expectedHash = bytesToHex(computeCodeHash(requestId, code));
|
|
157
|
+
if (expectedHash !== encrypted.code_hash_hex) {
|
|
158
|
+
throw new Error("Invalid code: hash mismatch");
|
|
159
|
+
}
|
|
160
|
+
const nonce = hexToBytes(encrypted.nonce_hex);
|
|
161
|
+
const aad = new Uint8Array([...cliPublicKey, ...walletPublicKey]);
|
|
162
|
+
const ciphertext = b64urlDecode(encrypted.ciphertext_b64url);
|
|
163
|
+
const cipher = xchacha20poly1305(encryptionKey, nonce, aad);
|
|
164
|
+
const plaintext = cipher.decrypt(ciphertext);
|
|
165
|
+
return JSON.parse(new TextDecoder().decode(plaintext));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ../agent-session-protocol/src/connector.ts
|
|
169
|
+
var textEncoder = new TextEncoder();
|
|
170
|
+
var textDecoder = new TextDecoder();
|
|
171
|
+
function isAddress(value) {
|
|
172
|
+
return /^0x[a-fA-F0-9]{40}$/.test(value);
|
|
173
|
+
}
|
|
174
|
+
function encodeSessionApprovalRequest(request) {
|
|
175
|
+
return b64urlEncode(textEncoder.encode(JSON.stringify(request)));
|
|
176
|
+
}
|
|
177
|
+
function buildApprovedSessionPayload(input) {
|
|
178
|
+
if (!isAddress(input.walletAddress)) throw new Error("walletAddress must be a valid address");
|
|
179
|
+
const derivedOwnerAddress = input.sessionPrivateKey ? deriveEthereumAddressFromPrivateKey(input.sessionPrivateKey) : void 0;
|
|
180
|
+
const ownerAddress = input.ownerAddress || derivedOwnerAddress;
|
|
181
|
+
if (ownerAddress && !isAddress(ownerAddress)) {
|
|
182
|
+
throw new Error("ownerAddress must be a valid address");
|
|
183
|
+
}
|
|
184
|
+
if (input.sessionAddress && !isAddress(input.sessionAddress)) {
|
|
185
|
+
throw new Error("sessionAddress must be a valid address");
|
|
186
|
+
}
|
|
187
|
+
if (input.sessionPrivateKey && !/^0x[0-9a-fA-F]{64}$/.test(input.sessionPrivateKey)) {
|
|
188
|
+
throw new Error("sessionPrivateKey must be a 32-byte hex string");
|
|
189
|
+
}
|
|
190
|
+
if (input.validatorAddress && !isAddress(input.validatorAddress)) {
|
|
191
|
+
throw new Error("validatorAddress must be a valid address");
|
|
192
|
+
}
|
|
193
|
+
if (input.paymasterAddress && !isAddress(input.paymasterAddress)) {
|
|
194
|
+
throw new Error("paymasterAddress must be a valid address");
|
|
195
|
+
}
|
|
196
|
+
if (input.paymasterToken && !isAddress(input.paymasterToken)) {
|
|
197
|
+
throw new Error("paymasterToken must be a valid address");
|
|
198
|
+
}
|
|
199
|
+
if (input.request.requestedAccountKind === "smart-account" && !ownerAddress) {
|
|
200
|
+
throw new Error(
|
|
201
|
+
"Smart-account approval requires ownerAddress or a sessionPrivateKey that can be used to derive it"
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
const signerType = input.signerType || (input.sessionPrivateKey ? "local" : "connector");
|
|
205
|
+
return {
|
|
206
|
+
version: 1,
|
|
207
|
+
provider: input.request.provider,
|
|
208
|
+
chain: input.request.chain,
|
|
209
|
+
chainId: input.request.chainId,
|
|
210
|
+
walletAddress: input.walletAddress,
|
|
211
|
+
account: {
|
|
212
|
+
kind: input.request.requestedAccountKind,
|
|
213
|
+
address: input.walletAddress,
|
|
214
|
+
ownerAddress,
|
|
215
|
+
sessionAddress: input.sessionAddress,
|
|
216
|
+
validatorAddress: input.validatorAddress,
|
|
217
|
+
signerType
|
|
218
|
+
},
|
|
219
|
+
sessionScope: input.request.requestedSessionScope,
|
|
220
|
+
capabilities: input.request.requestedCapabilities,
|
|
221
|
+
sessionExpiresAt: input.request.expiresAt,
|
|
222
|
+
paymaster: {
|
|
223
|
+
mode: input.request.requestedPaymasterMode,
|
|
224
|
+
address: input.paymasterAddress || null,
|
|
225
|
+
token: input.paymasterToken
|
|
226
|
+
},
|
|
227
|
+
sessionPublicKey: input.request.sessionPublicKey,
|
|
228
|
+
sessionPrivateKey: input.sessionPrivateKey,
|
|
229
|
+
sessionAddress: input.sessionAddress,
|
|
230
|
+
permissions: input.request.policies,
|
|
231
|
+
connectorUrl: input.connectorUrl || input.request.connectorUrl,
|
|
232
|
+
connectorOrigin: input.connectorOrigin,
|
|
233
|
+
paymasterAddress: input.paymasterAddress || null,
|
|
234
|
+
metadata: input.metadata
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ../agent-core/src/wallet-session.ts
|
|
239
|
+
function isHexPrivateKey(value) {
|
|
240
|
+
return /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
241
|
+
}
|
|
242
|
+
function deriveLocalExecutionSignerAddress(privateKey) {
|
|
243
|
+
if (!privateKey || !isHexPrivateKey(privateKey)) return void 0;
|
|
244
|
+
try {
|
|
245
|
+
return deriveEthereumAddressFromPrivateKey(privateKey);
|
|
246
|
+
} catch {
|
|
247
|
+
return void 0;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function buildLocalExecutionAuthority(input) {
|
|
251
|
+
if (!input.privateKey) return void 0;
|
|
252
|
+
return {
|
|
253
|
+
privateKey: input.privateKey,
|
|
254
|
+
signerAddress: deriveLocalExecutionSignerAddress(input.privateKey),
|
|
255
|
+
signerType: input.signerType || "local",
|
|
256
|
+
source: input.source,
|
|
257
|
+
attachedAt: input.attachedAt
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function resolveLocalExecutionPrivateKey(wallet) {
|
|
261
|
+
return wallet.localExecutionAuthority?.privateKey || wallet.sessionPayload?.sessionPrivateKey;
|
|
262
|
+
}
|
|
263
|
+
function migrateWalletSessionRecord(wallet) {
|
|
264
|
+
const legacyPrivateKey = wallet.sessionPayload?.sessionPrivateKey;
|
|
265
|
+
const existingAuthority = wallet.localExecutionAuthority;
|
|
266
|
+
if (!existingAuthority && !legacyPrivateKey) {
|
|
267
|
+
return wallet;
|
|
268
|
+
}
|
|
269
|
+
const privateKey = existingAuthority?.privateKey || legacyPrivateKey;
|
|
270
|
+
if (!privateKey) {
|
|
271
|
+
return wallet;
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
...wallet,
|
|
275
|
+
localExecutionAuthority: {
|
|
276
|
+
privateKey,
|
|
277
|
+
signerAddress: existingAuthority?.signerAddress || deriveLocalExecutionSignerAddress(privateKey),
|
|
278
|
+
signerType: existingAuthority?.signerType || wallet.sessionPayload?.account?.signerType || "local",
|
|
279
|
+
source: existingAuthority?.source || (legacyPrivateKey ? "legacy-session-payload" : void 0),
|
|
280
|
+
attachedAt: existingAuthority?.attachedAt || wallet.createdAt
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// ../agent-core/src/storage.ts
|
|
81
286
|
function normalizeOptionalPath(value) {
|
|
82
287
|
const trimmed = value?.trim();
|
|
83
288
|
return trimmed ? path.resolve(trimmed) : null;
|
|
@@ -126,13 +331,13 @@ function getEncryptionKey() {
|
|
|
126
331
|
const storageDirectory = ensureStorageDir();
|
|
127
332
|
const encryptionKeyFile = path.join(storageDirectory, ".encryption-key");
|
|
128
333
|
if (fs.existsSync(encryptionKeyFile)) return fs.readFileSync(encryptionKeyFile);
|
|
129
|
-
const key =
|
|
334
|
+
const key = randomBytes2(32);
|
|
130
335
|
fs.writeFileSync(encryptionKeyFile, key, { mode: 384 });
|
|
131
336
|
return key;
|
|
132
337
|
}
|
|
133
338
|
function encrypt(plaintext) {
|
|
134
339
|
const key = getEncryptionKey();
|
|
135
|
-
const iv =
|
|
340
|
+
const iv = randomBytes2(16);
|
|
136
341
|
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
137
342
|
let encrypted = cipher.update(plaintext, "utf8", "hex");
|
|
138
343
|
encrypted += cipher.final("hex");
|
|
@@ -181,12 +386,15 @@ async function loadProjectConfig() {
|
|
|
181
386
|
}
|
|
182
387
|
async function saveWalletSession(record) {
|
|
183
388
|
const storageDirectory = ensureStorageDir();
|
|
184
|
-
writeEncryptedJson(
|
|
389
|
+
writeEncryptedJson(
|
|
390
|
+
path.join(storageDirectory, "wallets", `${record.walletName}.json`),
|
|
391
|
+
migrateWalletSessionRecord(record)
|
|
392
|
+
);
|
|
185
393
|
}
|
|
186
394
|
async function loadWalletSession(walletName) {
|
|
187
395
|
const filePath = storagePath("wallets", `${walletName}.json`);
|
|
188
396
|
if (!fs.existsSync(filePath)) return null;
|
|
189
|
-
return readEncryptedJson(filePath);
|
|
397
|
+
return migrateWalletSessionRecord(readEncryptedJson(filePath));
|
|
190
398
|
}
|
|
191
399
|
async function listWalletNames() {
|
|
192
400
|
const storageDirectory = ensureStorageDir();
|
|
@@ -2225,22 +2433,33 @@ function buildWalletPreparationActions(input) {
|
|
|
2225
2433
|
const { wallet, inspection, funding } = input;
|
|
2226
2434
|
const exclude = new Set(input.excludeActionIds || []);
|
|
2227
2435
|
const actions = [];
|
|
2228
|
-
|
|
2436
|
+
const approvalReady = inspection.approvalReady ?? Boolean(wallet.sessionPayload);
|
|
2437
|
+
const localExecutionReady = inspection.localExecutionKeyStored ?? inspection.sessionPrivateKeyStored;
|
|
2438
|
+
if (!exclude.has("reapprove") && !approvalReady) {
|
|
2229
2439
|
actions.push({
|
|
2230
2440
|
id: "reapprove",
|
|
2231
2441
|
priority: "required",
|
|
2232
|
-
title: "Restore
|
|
2233
|
-
reason: "
|
|
2442
|
+
title: "Restore approved session metadata",
|
|
2443
|
+
reason: localExecutionReady ? "A local execution signer is already present, but approved session metadata is missing or not usable yet." : "Approved session metadata is missing, so this wallet cannot execute local write actions yet.",
|
|
2234
2444
|
command: `zk-agent wallet reapprove --name ${wallet.walletName} --await-local`
|
|
2235
2445
|
});
|
|
2236
2446
|
}
|
|
2447
|
+
if (!exclude.has("attach-signer") && approvalReady && !localExecutionReady) {
|
|
2448
|
+
actions.push({
|
|
2449
|
+
id: "attach-signer",
|
|
2450
|
+
priority: "required",
|
|
2451
|
+
title: "Attach a local execution signer",
|
|
2452
|
+
reason: "Approved session metadata exists, but no local execution signer is stored for write actions yet.",
|
|
2453
|
+
command: `zk-agent wallet signer attach --name ${wallet.walletName} --private-key <hex>`
|
|
2454
|
+
});
|
|
2455
|
+
}
|
|
2237
2456
|
if (!exclude.has("signer-mismatch") && inspection.signerMatchesStoredIdentity === false) {
|
|
2238
2457
|
actions.push({
|
|
2239
2458
|
id: "signer-mismatch",
|
|
2240
2459
|
priority: "required",
|
|
2241
2460
|
title: "Repair the signer/address mismatch",
|
|
2242
2461
|
reason: "The stored local signer does not match the wallet identity currently recorded for this session.",
|
|
2243
|
-
command: `zk-agent wallet
|
|
2462
|
+
command: `zk-agent wallet signer attach --name ${wallet.walletName} --private-key <hex>`
|
|
2244
2463
|
});
|
|
2245
2464
|
}
|
|
2246
2465
|
if (!exclude.has("deploy") && inspection.accountKind === "smart-account" && inspection.deploymentStatus === "not-deployed") {
|
|
@@ -2784,7 +3003,7 @@ async function loadWorkflowRuntimeState(wallet, intent, provider4, protocol, toC
|
|
|
2784
3003
|
}
|
|
2785
3004
|
function manualBlockingActionIds(plan) {
|
|
2786
3005
|
return plan.steps.filter(
|
|
2787
|
-
(step) => step.kind === "prerequisite" && step.priority === "required" && (step.id === "reapprove" || step.id === "signer-mismatch" || step.id === "deploy")
|
|
3006
|
+
(step) => step.kind === "prerequisite" && step.priority === "required" && (step.id === "reapprove" || step.id === "attach-signer" || step.id === "signer-mismatch" || step.id === "deploy")
|
|
2788
3007
|
).map((step) => step.id);
|
|
2789
3008
|
}
|
|
2790
3009
|
function buildWorkflowGoalCommand(goal, wallet) {
|
|
@@ -3120,7 +3339,7 @@ function mergeNotes2(...groups) {
|
|
|
3120
3339
|
}
|
|
3121
3340
|
function manualBlockingActionIds2(plan) {
|
|
3122
3341
|
return plan.steps.filter(
|
|
3123
|
-
(step) => step.kind === "prerequisite" && step.priority === "required" && (step.id === "reapprove" || step.id === "signer-mismatch" || step.id === "deploy")
|
|
3342
|
+
(step) => step.kind === "prerequisite" && step.priority === "required" && (step.id === "reapprove" || step.id === "attach-signer" || step.id === "signer-mismatch" || step.id === "deploy")
|
|
3124
3343
|
).map((step) => step.id);
|
|
3125
3344
|
}
|
|
3126
3345
|
async function inspectWorkflowStatus(input, deps) {
|
|
@@ -4159,28 +4378,28 @@ function getL1ExplorerUrl(l1ChainId, txHash) {
|
|
|
4159
4378
|
return void 0;
|
|
4160
4379
|
}
|
|
4161
4380
|
}
|
|
4162
|
-
function
|
|
4381
|
+
function isHexPrivateKey2(value) {
|
|
4163
4382
|
return /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
4164
4383
|
}
|
|
4165
4384
|
function deriveSignerAddress(privateKey) {
|
|
4166
|
-
if (!privateKey || !
|
|
4385
|
+
if (!privateKey || !isHexPrivateKey2(privateKey)) return void 0;
|
|
4167
4386
|
return new Wallet(privateKey).address;
|
|
4168
4387
|
}
|
|
4169
4388
|
function requireWritableSession(wallet) {
|
|
4170
|
-
const privateKey = wallet
|
|
4389
|
+
const privateKey = resolveLocalExecutionPrivateKey(wallet);
|
|
4171
4390
|
if (!privateKey) {
|
|
4172
4391
|
throw new AgentError(
|
|
4173
4392
|
"WRITABLE_SESSION_REQUIRED",
|
|
4174
|
-
"Writable local execution requires a stored
|
|
4393
|
+
"Writable local execution requires a stored local execution key.",
|
|
4175
4394
|
{
|
|
4176
4395
|
walletName: wallet.walletName
|
|
4177
4396
|
}
|
|
4178
4397
|
);
|
|
4179
4398
|
}
|
|
4180
|
-
if (!
|
|
4399
|
+
if (!isHexPrivateKey2(privateKey)) {
|
|
4181
4400
|
throw new AgentError(
|
|
4182
4401
|
"WRITABLE_SESSION_INVALID",
|
|
4183
|
-
"Stored
|
|
4402
|
+
"Stored local execution key is not a valid 32-byte hex key.",
|
|
4184
4403
|
{
|
|
4185
4404
|
walletName: wallet.walletName
|
|
4186
4405
|
}
|
|
@@ -4193,7 +4412,7 @@ function resolveWritableSignerAddress(wallet, privateKey) {
|
|
|
4193
4412
|
if (!derivedSignerAddress) {
|
|
4194
4413
|
throw new AgentError(
|
|
4195
4414
|
"WRITABLE_SESSION_INVALID",
|
|
4196
|
-
"Stored
|
|
4415
|
+
"Stored local execution key is not a valid 32-byte hex key.",
|
|
4197
4416
|
{
|
|
4198
4417
|
walletName: wallet.walletName
|
|
4199
4418
|
}
|
|
@@ -4204,7 +4423,7 @@ function resolveWritableSignerAddress(wallet, privateKey) {
|
|
|
4204
4423
|
if (derivedSignerAddress.toLowerCase() !== executionAddress.toLowerCase()) {
|
|
4205
4424
|
throw new AgentError(
|
|
4206
4425
|
"EOA_SIGNER_MISMATCH",
|
|
4207
|
-
"Stored
|
|
4426
|
+
"Stored local execution key does not match the EOA execution address.",
|
|
4208
4427
|
{
|
|
4209
4428
|
walletName: wallet.walletName,
|
|
4210
4429
|
executionAddress,
|
|
@@ -4228,7 +4447,7 @@ function resolveWritableSignerAddress(wallet, privateKey) {
|
|
|
4228
4447
|
if (derivedSignerAddress.toLowerCase() !== ownerAddress.toLowerCase()) {
|
|
4229
4448
|
throw new AgentError(
|
|
4230
4449
|
"SMART_ACCOUNT_SIGNER_MISMATCH",
|
|
4231
|
-
"Stored
|
|
4450
|
+
"Stored local execution key does not match the smart-account ownerAddress.",
|
|
4232
4451
|
{
|
|
4233
4452
|
walletName: wallet.walletName,
|
|
4234
4453
|
ownerAddress,
|
|
@@ -4255,7 +4474,7 @@ async function buildSigner(wallet, provider4) {
|
|
|
4255
4474
|
if (derivedSignerAddress && derivedSignerAddress.toLowerCase() !== executionAddress.toLowerCase()) {
|
|
4256
4475
|
throw new AgentError(
|
|
4257
4476
|
"EOA_SIGNER_MISMATCH",
|
|
4258
|
-
"Stored
|
|
4477
|
+
"Stored local execution key does not match the EOA execution address.",
|
|
4259
4478
|
{
|
|
4260
4479
|
walletName: wallet.walletName,
|
|
4261
4480
|
executionAddress,
|
|
@@ -4279,7 +4498,7 @@ async function buildSigner(wallet, provider4) {
|
|
|
4279
4498
|
if (derivedSignerAddress && derivedSignerAddress.toLowerCase() !== ownerAddress.toLowerCase()) {
|
|
4280
4499
|
throw new AgentError(
|
|
4281
4500
|
"SMART_ACCOUNT_SIGNER_MISMATCH",
|
|
4282
|
-
"Stored
|
|
4501
|
+
"Stored local execution key does not match the smart-account ownerAddress.",
|
|
4283
4502
|
{
|
|
4284
4503
|
walletName: wallet.walletName,
|
|
4285
4504
|
ownerAddress,
|
|
@@ -6064,163 +6283,6 @@ var ZkSyncDefiProvider = class {
|
|
|
6064
6283
|
|
|
6065
6284
|
// ../provider-zksync-wallet/src/provider.ts
|
|
6066
6285
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
6067
|
-
|
|
6068
|
-
// ../agent-session-protocol/src/constants.ts
|
|
6069
|
-
var PROTOCOL_VERSION = "zk-agent-session-v1";
|
|
6070
|
-
|
|
6071
|
-
// ../agent-session-protocol/src/encoding.ts
|
|
6072
|
-
function bytesToHex(bytes) {
|
|
6073
|
-
return Array.from(bytes).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
6074
|
-
}
|
|
6075
|
-
function hexToBytes(hex) {
|
|
6076
|
-
if (hex.length % 2 !== 0) throw new Error("Invalid hex string");
|
|
6077
|
-
const bytes = new Uint8Array(hex.length / 2);
|
|
6078
|
-
for (let index = 0; index < bytes.length; index += 1) {
|
|
6079
|
-
bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
|
|
6080
|
-
}
|
|
6081
|
-
return bytes;
|
|
6082
|
-
}
|
|
6083
|
-
function b64urlEncode(bytes) {
|
|
6084
|
-
let binary = "";
|
|
6085
|
-
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
6086
|
-
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
6087
|
-
}
|
|
6088
|
-
function b64urlDecode(value) {
|
|
6089
|
-
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
6090
|
-
const padding = normalized.length % 4 === 0 ? "" : "=".repeat(4 - normalized.length % 4);
|
|
6091
|
-
const binary = atob(normalized + padding);
|
|
6092
|
-
const bytes = new Uint8Array(binary.length);
|
|
6093
|
-
for (let index = 0; index < binary.length; index += 1) {
|
|
6094
|
-
bytes[index] = binary.charCodeAt(index);
|
|
6095
|
-
}
|
|
6096
|
-
return bytes;
|
|
6097
|
-
}
|
|
6098
|
-
|
|
6099
|
-
// ../agent-session-protocol/src/crypto.ts
|
|
6100
|
-
import { xchacha20poly1305 } from "@noble/ciphers/chacha";
|
|
6101
|
-
import { x25519 } from "@noble/curves/ed25519";
|
|
6102
|
-
import { secp256k1 } from "@noble/curves/secp256k1";
|
|
6103
|
-
import { hkdf } from "@noble/hashes/hkdf";
|
|
6104
|
-
import { sha256 } from "@noble/hashes/sha2";
|
|
6105
|
-
import { keccak_256 } from "@noble/hashes/sha3";
|
|
6106
|
-
import { randomBytes as randomBytes2 } from "@noble/hashes/utils";
|
|
6107
|
-
function deriveEthereumAddressFromPrivateKey(privateKeyHex) {
|
|
6108
|
-
const normalized = privateKeyHex.startsWith("0x") ? privateKeyHex.slice(2) : privateKeyHex;
|
|
6109
|
-
if (!/^[0-9a-fA-F]{64}$/.test(normalized)) {
|
|
6110
|
-
throw new Error("sessionPrivateKey must be a 32-byte hex string");
|
|
6111
|
-
}
|
|
6112
|
-
const publicKey = secp256k1.getPublicKey(hexToBytes(normalized), false);
|
|
6113
|
-
const digest = keccak_256(publicKey.slice(1));
|
|
6114
|
-
return `0x${bytesToHex(digest.slice(-20))}`;
|
|
6115
|
-
}
|
|
6116
|
-
function generateX25519Keypair() {
|
|
6117
|
-
const secretKey = randomBytes2(32);
|
|
6118
|
-
const publicKey = x25519.getPublicKey(secretKey);
|
|
6119
|
-
return { secretKey, publicKey };
|
|
6120
|
-
}
|
|
6121
|
-
function computeCodeHash(requestId, code) {
|
|
6122
|
-
if (!requestId) throw new Error("requestId must not be empty");
|
|
6123
|
-
return sha256(new TextEncoder().encode(requestId + code));
|
|
6124
|
-
}
|
|
6125
|
-
function deriveEncryptionKey(sharedSecret, code, cliPublicKeyHex, walletPublicKeyHex) {
|
|
6126
|
-
const salt = sha256(new TextEncoder().encode(code));
|
|
6127
|
-
const info = new TextEncoder().encode(cliPublicKeyHex + walletPublicKeyHex + PROTOCOL_VERSION);
|
|
6128
|
-
return hkdf(sha256, sharedSecret, salt, info, 32);
|
|
6129
|
-
}
|
|
6130
|
-
function decryptSession(encrypted, cliSecretKey, code, requestId) {
|
|
6131
|
-
if (!requestId) throw new Error("requestId must not be empty");
|
|
6132
|
-
const cliPublicKey = x25519.getPublicKey(cliSecretKey);
|
|
6133
|
-
const walletPublicKey = hexToBytes(encrypted.wallet_pk_hex);
|
|
6134
|
-
const sharedSecret = x25519.getSharedSecret(cliSecretKey, walletPublicKey);
|
|
6135
|
-
const cliPublicKeyHex = bytesToHex(cliPublicKey);
|
|
6136
|
-
const encryptionKey = deriveEncryptionKey(
|
|
6137
|
-
sharedSecret,
|
|
6138
|
-
code,
|
|
6139
|
-
cliPublicKeyHex,
|
|
6140
|
-
encrypted.wallet_pk_hex
|
|
6141
|
-
);
|
|
6142
|
-
const expectedHash = bytesToHex(computeCodeHash(requestId, code));
|
|
6143
|
-
if (expectedHash !== encrypted.code_hash_hex) {
|
|
6144
|
-
throw new Error("Invalid code: hash mismatch");
|
|
6145
|
-
}
|
|
6146
|
-
const nonce = hexToBytes(encrypted.nonce_hex);
|
|
6147
|
-
const aad = new Uint8Array([...cliPublicKey, ...walletPublicKey]);
|
|
6148
|
-
const ciphertext = b64urlDecode(encrypted.ciphertext_b64url);
|
|
6149
|
-
const cipher = xchacha20poly1305(encryptionKey, nonce, aad);
|
|
6150
|
-
const plaintext = cipher.decrypt(ciphertext);
|
|
6151
|
-
return JSON.parse(new TextDecoder().decode(plaintext));
|
|
6152
|
-
}
|
|
6153
|
-
|
|
6154
|
-
// ../agent-session-protocol/src/connector.ts
|
|
6155
|
-
var textEncoder = new TextEncoder();
|
|
6156
|
-
var textDecoder = new TextDecoder();
|
|
6157
|
-
function isAddress(value) {
|
|
6158
|
-
return /^0x[a-fA-F0-9]{40}$/.test(value);
|
|
6159
|
-
}
|
|
6160
|
-
function encodeSessionApprovalRequest(request) {
|
|
6161
|
-
return b64urlEncode(textEncoder.encode(JSON.stringify(request)));
|
|
6162
|
-
}
|
|
6163
|
-
function buildApprovedSessionPayload(input) {
|
|
6164
|
-
if (!isAddress(input.walletAddress)) throw new Error("walletAddress must be a valid address");
|
|
6165
|
-
const derivedOwnerAddress = input.sessionPrivateKey ? deriveEthereumAddressFromPrivateKey(input.sessionPrivateKey) : void 0;
|
|
6166
|
-
const ownerAddress = input.ownerAddress || derivedOwnerAddress;
|
|
6167
|
-
if (ownerAddress && !isAddress(ownerAddress)) {
|
|
6168
|
-
throw new Error("ownerAddress must be a valid address");
|
|
6169
|
-
}
|
|
6170
|
-
if (input.sessionAddress && !isAddress(input.sessionAddress)) {
|
|
6171
|
-
throw new Error("sessionAddress must be a valid address");
|
|
6172
|
-
}
|
|
6173
|
-
if (input.sessionPrivateKey && !/^0x[0-9a-fA-F]{64}$/.test(input.sessionPrivateKey)) {
|
|
6174
|
-
throw new Error("sessionPrivateKey must be a 32-byte hex string");
|
|
6175
|
-
}
|
|
6176
|
-
if (input.validatorAddress && !isAddress(input.validatorAddress)) {
|
|
6177
|
-
throw new Error("validatorAddress must be a valid address");
|
|
6178
|
-
}
|
|
6179
|
-
if (input.paymasterAddress && !isAddress(input.paymasterAddress)) {
|
|
6180
|
-
throw new Error("paymasterAddress must be a valid address");
|
|
6181
|
-
}
|
|
6182
|
-
if (input.paymasterToken && !isAddress(input.paymasterToken)) {
|
|
6183
|
-
throw new Error("paymasterToken must be a valid address");
|
|
6184
|
-
}
|
|
6185
|
-
if (input.request.requestedAccountKind === "smart-account" && !ownerAddress) {
|
|
6186
|
-
throw new Error(
|
|
6187
|
-
"Smart-account approval requires ownerAddress or a sessionPrivateKey that can be used to derive it"
|
|
6188
|
-
);
|
|
6189
|
-
}
|
|
6190
|
-
return {
|
|
6191
|
-
version: 1,
|
|
6192
|
-
provider: input.request.provider,
|
|
6193
|
-
chain: input.request.chain,
|
|
6194
|
-
chainId: input.request.chainId,
|
|
6195
|
-
walletAddress: input.walletAddress,
|
|
6196
|
-
account: {
|
|
6197
|
-
kind: input.request.requestedAccountKind,
|
|
6198
|
-
address: input.walletAddress,
|
|
6199
|
-
ownerAddress,
|
|
6200
|
-
sessionAddress: input.sessionAddress,
|
|
6201
|
-
validatorAddress: input.validatorAddress,
|
|
6202
|
-
signerType: input.signerType || "connector"
|
|
6203
|
-
},
|
|
6204
|
-
sessionScope: input.request.requestedSessionScope,
|
|
6205
|
-
capabilities: input.request.requestedCapabilities,
|
|
6206
|
-
sessionExpiresAt: input.request.expiresAt,
|
|
6207
|
-
paymaster: {
|
|
6208
|
-
mode: input.request.requestedPaymasterMode,
|
|
6209
|
-
address: input.paymasterAddress || null,
|
|
6210
|
-
token: input.paymasterToken
|
|
6211
|
-
},
|
|
6212
|
-
sessionPublicKey: input.request.sessionPublicKey,
|
|
6213
|
-
sessionPrivateKey: input.sessionPrivateKey,
|
|
6214
|
-
sessionAddress: input.sessionAddress,
|
|
6215
|
-
permissions: input.request.policies,
|
|
6216
|
-
connectorUrl: input.connectorUrl || input.request.connectorUrl,
|
|
6217
|
-
connectorOrigin: input.connectorOrigin,
|
|
6218
|
-
paymasterAddress: input.paymasterAddress || null,
|
|
6219
|
-
metadata: input.metadata
|
|
6220
|
-
};
|
|
6221
|
-
}
|
|
6222
|
-
|
|
6223
|
-
// ../provider-zksync-wallet/src/provider.ts
|
|
6224
6286
|
import { ethers as ethers2 } from "ethers";
|
|
6225
6287
|
import { ContractFactory, ECDSASmartAccount as ECDSASmartAccount2, Provider as Provider2, Wallet as Wallet2, utils as utils2 } from "zksync-ethers";
|
|
6226
6288
|
|
|
@@ -6251,7 +6313,7 @@ function isAddress2(value) {
|
|
|
6251
6313
|
function isHexData(value) {
|
|
6252
6314
|
return /^0x([a-fA-F0-9]{2})*$/.test(value);
|
|
6253
6315
|
}
|
|
6254
|
-
function
|
|
6316
|
+
function isHexPrivateKey3(value) {
|
|
6255
6317
|
return /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
6256
6318
|
}
|
|
6257
6319
|
function padHex(value, length = 64) {
|
|
@@ -6428,6 +6490,8 @@ function buildInspectionResult(wallet, codeLength, derivedSignerAddress, blocker
|
|
|
6428
6490
|
paymasterMode: wallet.paymasterMode,
|
|
6429
6491
|
deploymentStatus,
|
|
6430
6492
|
codeLength,
|
|
6493
|
+
approvalReady: Boolean(wallet.sessionPayload),
|
|
6494
|
+
localExecutionKeyStored: Boolean(resolveLocalExecutionPrivateKey(wallet)),
|
|
6431
6495
|
sessionPrivateKeyStored: Boolean(wallet.sessionPayload?.sessionPrivateKey),
|
|
6432
6496
|
derivedSignerAddress,
|
|
6433
6497
|
signerMatchesStoredIdentity,
|
|
@@ -6837,7 +6901,7 @@ async function preparePaymasterTransaction(wallet, tx, paymaster) {
|
|
|
6837
6901
|
}
|
|
6838
6902
|
}
|
|
6839
6903
|
function deriveSignerAddress2(privateKey) {
|
|
6840
|
-
if (!privateKey || !
|
|
6904
|
+
if (!privateKey || !isHexPrivateKey3(privateKey)) return void 0;
|
|
6841
6905
|
return new Wallet2(privateKey).address;
|
|
6842
6906
|
}
|
|
6843
6907
|
function normalizeSalt(value) {
|
|
@@ -6897,7 +6961,7 @@ async function resolveSmartAccountDeploymentContext(input) {
|
|
|
6897
6961
|
if (deployerAddress.toLowerCase() !== ownerAddress.toLowerCase()) {
|
|
6898
6962
|
throw new AgentError(
|
|
6899
6963
|
"SMART_ACCOUNT_SIGNER_MISMATCH",
|
|
6900
|
-
"Stored
|
|
6964
|
+
"Stored local execution key does not match the smart-account ownerAddress.",
|
|
6901
6965
|
{
|
|
6902
6966
|
walletName: wallet.walletName,
|
|
6903
6967
|
ownerAddress,
|
|
@@ -6994,7 +7058,7 @@ async function resolveSmartAccountDeploymentContext(input) {
|
|
|
6994
7058
|
async function inspectWalletRecord(wallet) {
|
|
6995
7059
|
const executionAddress = resolveExecutionAddress2(wallet);
|
|
6996
7060
|
const ownerAddress = resolveOwnerAddress2(wallet);
|
|
6997
|
-
const sessionPrivateKey = wallet
|
|
7061
|
+
const sessionPrivateKey = resolveLocalExecutionPrivateKey(wallet);
|
|
6998
7062
|
const derivedSignerAddress = deriveSignerAddress2(sessionPrivateKey);
|
|
6999
7063
|
const provider4 = getProvider(wallet.chain);
|
|
7000
7064
|
const code = await provider4.getCode(executionAddress);
|
|
@@ -7006,14 +7070,14 @@ async function inspectWalletRecord(wallet) {
|
|
|
7006
7070
|
}
|
|
7007
7071
|
if (!sessionPrivateKey) {
|
|
7008
7072
|
blockers.push(
|
|
7009
|
-
"Writable local execution requires a stored
|
|
7073
|
+
"Writable local execution requires a stored local execution key. Attach one with wallet signer attach, re-approve locally with --session-private-key, or import a writable session."
|
|
7010
7074
|
);
|
|
7011
|
-
} else if (!
|
|
7012
|
-
blockers.push("Stored
|
|
7075
|
+
} else if (!isHexPrivateKey3(sessionPrivateKey)) {
|
|
7076
|
+
blockers.push("Stored local execution key is not a valid 32-byte hex key.");
|
|
7013
7077
|
}
|
|
7014
7078
|
if (wallet.accountKind === "eoa") {
|
|
7015
7079
|
if (derivedSignerAddress && derivedSignerAddress.toLowerCase() !== executionAddress.toLowerCase()) {
|
|
7016
|
-
blockers.push("Stored
|
|
7080
|
+
blockers.push("Stored local execution key does not match the EOA execution address.");
|
|
7017
7081
|
}
|
|
7018
7082
|
if (codeLength > 0) {
|
|
7019
7083
|
notes.push("EOA wallet record points to an address with deployed bytecode.");
|
|
@@ -7024,7 +7088,7 @@ async function inspectWalletRecord(wallet) {
|
|
|
7024
7088
|
blockers.push("Smart-account session is missing ownerAddress metadata.");
|
|
7025
7089
|
}
|
|
7026
7090
|
if (ownerAddress && derivedSignerAddress && derivedSignerAddress.toLowerCase() !== ownerAddress.toLowerCase()) {
|
|
7027
|
-
blockers.push("Stored
|
|
7091
|
+
blockers.push("Stored local execution key does not match the smart-account ownerAddress.");
|
|
7028
7092
|
}
|
|
7029
7093
|
if (codeLength === 0) {
|
|
7030
7094
|
blockers.push(
|
|
@@ -7050,18 +7114,18 @@ async function assertWalletReadyForWrite(wallet) {
|
|
|
7050
7114
|
{ inspection }
|
|
7051
7115
|
);
|
|
7052
7116
|
}
|
|
7053
|
-
const sessionPrivateKey = wallet
|
|
7054
|
-
if (!sessionPrivateKey || !
|
|
7117
|
+
const sessionPrivateKey = resolveLocalExecutionPrivateKey(wallet);
|
|
7118
|
+
if (!sessionPrivateKey || !isHexPrivateKey3(sessionPrivateKey)) {
|
|
7055
7119
|
throw new AgentError(
|
|
7056
7120
|
"WRITABLE_SESSION_REQUIRED",
|
|
7057
|
-
"Writable local execution requires a valid stored
|
|
7121
|
+
"Writable local execution requires a valid stored local execution key.",
|
|
7058
7122
|
{ inspection }
|
|
7059
7123
|
);
|
|
7060
7124
|
}
|
|
7061
7125
|
if (wallet.accountKind === "eoa") {
|
|
7062
7126
|
throw new AgentError(
|
|
7063
7127
|
"EOA_SIGNER_MISMATCH",
|
|
7064
|
-
"Stored
|
|
7128
|
+
"Stored local execution key does not match the EOA execution address.",
|
|
7065
7129
|
{ inspection }
|
|
7066
7130
|
);
|
|
7067
7131
|
}
|
|
@@ -7075,7 +7139,7 @@ async function assertWalletReadyForWrite(wallet) {
|
|
|
7075
7139
|
if (inspection.signerMatchesStoredIdentity === false) {
|
|
7076
7140
|
throw new AgentError(
|
|
7077
7141
|
"SMART_ACCOUNT_SIGNER_MISMATCH",
|
|
7078
|
-
"Stored
|
|
7142
|
+
"Stored local execution key does not match the smart-account ownerAddress.",
|
|
7079
7143
|
{ inspection }
|
|
7080
7144
|
);
|
|
7081
7145
|
}
|
|
@@ -7096,14 +7160,14 @@ async function assertWalletReadyForWrite(wallet) {
|
|
|
7096
7160
|
);
|
|
7097
7161
|
}
|
|
7098
7162
|
function requireWritableSession2(wallet) {
|
|
7099
|
-
const privateKey = wallet
|
|
7163
|
+
const privateKey = resolveLocalExecutionPrivateKey(wallet);
|
|
7100
7164
|
if (!privateKey) {
|
|
7101
7165
|
throw new Error(
|
|
7102
|
-
"Writable session requires
|
|
7166
|
+
"Writable session requires a stored local execution key. Re-approve locally with --session-private-key or import a writable testnet session."
|
|
7103
7167
|
);
|
|
7104
7168
|
}
|
|
7105
|
-
if (!
|
|
7106
|
-
throw new Error("Stored
|
|
7169
|
+
if (!isHexPrivateKey3(privateKey)) {
|
|
7170
|
+
throw new Error("Stored local execution key is not a valid 32-byte hex key");
|
|
7107
7171
|
}
|
|
7108
7172
|
return privateKey;
|
|
7109
7173
|
}
|
|
@@ -7354,6 +7418,13 @@ var ZkSyncWalletProvider = class {
|
|
|
7354
7418
|
}
|
|
7355
7419
|
const chain = resolveChain(payload.chainId);
|
|
7356
7420
|
const walletAddress = payload.account?.address || payload.walletAddress;
|
|
7421
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7422
|
+
const localExecutionAuthority = buildLocalExecutionAuthority({
|
|
7423
|
+
privateKey: payload.sessionPrivateKey,
|
|
7424
|
+
signerType: payload.account?.signerType,
|
|
7425
|
+
source: payload.sessionPrivateKey ? payload.account?.signerType === "local" ? "explicit-local-approval" : "approved-payload" : void 0,
|
|
7426
|
+
attachedAt: createdAt
|
|
7427
|
+
});
|
|
7357
7428
|
return {
|
|
7358
7429
|
walletName,
|
|
7359
7430
|
walletAddress,
|
|
@@ -7367,7 +7438,8 @@ var ZkSyncWalletProvider = class {
|
|
|
7367
7438
|
sessionScope: payload.sessionScope || summarizeScope(chain.key, chain.chainId),
|
|
7368
7439
|
capabilities: payload.capabilities,
|
|
7369
7440
|
paymasterMode: resolvePaymasterMode(payload),
|
|
7370
|
-
createdAt
|
|
7441
|
+
createdAt,
|
|
7442
|
+
localExecutionAuthority,
|
|
7371
7443
|
sessionPayload: payload
|
|
7372
7444
|
};
|
|
7373
7445
|
}
|
|
@@ -7692,6 +7764,9 @@ function buildWalletListRecommendedCommand() {
|
|
|
7692
7764
|
function buildWalletStatusRecommendedCommand(walletName) {
|
|
7693
7765
|
return `zk-agent wallet status --name ${walletName}`;
|
|
7694
7766
|
}
|
|
7767
|
+
function buildWalletSignerAttachRecommendedCommand(walletName, privateKeyRef = "<hex>") {
|
|
7768
|
+
return `zk-agent wallet signer attach --name ${walletName} --private-key ${privateKeyRef}`;
|
|
7769
|
+
}
|
|
7695
7770
|
function buildAssetsRecommendedCommand(walletName) {
|
|
7696
7771
|
return `zk-agent assets --wallet ${walletName}`;
|
|
7697
7772
|
}
|
|
@@ -7760,6 +7835,10 @@ function buildWorkflowListRecommendedCommand() {
|
|
|
7760
7835
|
function buildWorkflowAutoRecommendedCommand(walletName) {
|
|
7761
7836
|
return `zk-agent workflow auto --wallet ${walletName} --intent <intent> [goal flags] --create-checkpoint --execute-when-ready`;
|
|
7762
7837
|
}
|
|
7838
|
+
function buildWorkflowPayRecommendedCommand(walletName, paymasterMode) {
|
|
7839
|
+
const command = `zk-agent workflow pay --wallet ${walletName} --to <address> --amount <amount>`;
|
|
7840
|
+
return appendPaymasterMode(command, paymasterMode);
|
|
7841
|
+
}
|
|
7763
7842
|
function buildWorkflowShowRecommendedCommand(requestId) {
|
|
7764
7843
|
return `zk-agent workflow show --request-id ${requestId}`;
|
|
7765
7844
|
}
|
|
@@ -10505,11 +10584,15 @@ function createNextCommand(deps) {
|
|
|
10505
10584
|
nativeSymbol: nativeBalance?.symbol,
|
|
10506
10585
|
funding
|
|
10507
10586
|
});
|
|
10587
|
+
const workflowPay = buildWorkflowPayRecommendedCommand(
|
|
10588
|
+
wallet.walletName,
|
|
10589
|
+
paymasterMode
|
|
10590
|
+
);
|
|
10508
10591
|
const workflowAuto = appendPaymasterMode2(
|
|
10509
10592
|
buildWorkflowAutoRecommendedCommand(wallet.walletName),
|
|
10510
10593
|
paymasterMode
|
|
10511
10594
|
);
|
|
10512
|
-
const nextCommand = summary.recommendedCommand ||
|
|
10595
|
+
const nextCommand = summary.recommendedCommand || workflowPay;
|
|
10513
10596
|
const agentFollowup = buildAgentFollowup(agentProfile, {
|
|
10514
10597
|
walletName: wallet.walletName,
|
|
10515
10598
|
walletExists: true
|
|
@@ -10521,6 +10604,7 @@ function createNextCommand(deps) {
|
|
|
10521
10604
|
discoverOwnedTokens: buildOwnedTokensRecommendedCommand(wallet.walletName),
|
|
10522
10605
|
discoverTokens: buildTokensRecommendedCommand(wallet.chain),
|
|
10523
10606
|
inspectToken: buildResolveTokenRecommendedCommand(wallet.chain),
|
|
10607
|
+
workflowPay,
|
|
10524
10608
|
workflowAuto,
|
|
10525
10609
|
nextAction: nextCommand,
|
|
10526
10610
|
inspectDefaults: buildDefaultsRecommendedCommand()
|
|
@@ -10530,7 +10614,7 @@ function createNextCommand(deps) {
|
|
|
10530
10614
|
...walletNextLines(summary),
|
|
10531
10615
|
...agentProfileLines(agentProfile),
|
|
10532
10616
|
...agentFollowupLines(agentFollowup),
|
|
10533
|
-
...summary.recommendedCommand ? [] : [["next",
|
|
10617
|
+
...summary.recommendedCommand ? [] : [["next", workflowPay]],
|
|
10534
10618
|
["discover assets", recommendedCommands.discoverAssets],
|
|
10535
10619
|
["discover owned tokens", recommendedCommands.discoverOwnedTokens],
|
|
10536
10620
|
["discover tokens", recommendedCommands.discoverTokens],
|
|
@@ -11824,16 +11908,19 @@ function readRequestBody(request, limitBytes = RELAY_BODY_LIMIT_BYTES) {
|
|
|
11824
11908
|
});
|
|
11825
11909
|
}
|
|
11826
11910
|
function writeJson2(response, statusCode, payload) {
|
|
11827
|
-
response
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
response.end(JSON.stringify(payload, null, 2));
|
|
11911
|
+
writeBody(response, statusCode, "application/json; charset=utf-8", JSON.stringify(payload, null, 2), {
|
|
11912
|
+
"Access-Control-Allow-Origin": "*",
|
|
11913
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
11914
|
+
"Access-Control-Allow-Headers": "Content-Type"
|
|
11915
|
+
});
|
|
11833
11916
|
}
|
|
11834
|
-
function
|
|
11917
|
+
function writeBody(response, statusCode, contentType, value, extraHeaders = {}) {
|
|
11835
11918
|
response.statusCode = statusCode;
|
|
11836
11919
|
response.setHeader("Content-Type", contentType);
|
|
11920
|
+
response.setHeader("Content-Length", Buffer.isBuffer(value) ? String(value.byteLength) : String(Buffer.byteLength(value)));
|
|
11921
|
+
for (const [key, headerValue] of Object.entries(extraHeaders)) {
|
|
11922
|
+
response.setHeader(key, headerValue);
|
|
11923
|
+
}
|
|
11837
11924
|
response.end(value);
|
|
11838
11925
|
}
|
|
11839
11926
|
function resolveConnectorUiDistRoot() {
|
|
@@ -11861,8 +11948,8 @@ function contentTypeFor(filePath) {
|
|
|
11861
11948
|
function normalizeRelayBaseUrl(baseUrl) {
|
|
11862
11949
|
return baseUrl.replace(/\/+$/, "");
|
|
11863
11950
|
}
|
|
11864
|
-
function resolveRelayPublicBaseUrl(
|
|
11865
|
-
return publicOrigin?.trim() ? normalizeRelayBaseUrl(new URL(publicOrigin.trim()).toString()) : normalizeRelayBaseUrl(
|
|
11951
|
+
function resolveRelayPublicBaseUrl(bindBaseUrl, publicOrigin) {
|
|
11952
|
+
return publicOrigin?.trim() ? normalizeRelayBaseUrl(new URL(publicOrigin.trim()).toString()) : normalizeRelayBaseUrl(bindBaseUrl);
|
|
11866
11953
|
}
|
|
11867
11954
|
function relayCapabilities(connectorUiAvailable) {
|
|
11868
11955
|
return [
|
|
@@ -11874,14 +11961,14 @@ function relayCapabilities(connectorUiAvailable) {
|
|
|
11874
11961
|
...connectorUiAvailable ? ["connector-ui"] : []
|
|
11875
11962
|
];
|
|
11876
11963
|
}
|
|
11877
|
-
function relayHealthResponse(
|
|
11964
|
+
function relayHealthResponse(bindBaseUrl, publicBaseUrl, connectorUiAvailable) {
|
|
11878
11965
|
return {
|
|
11879
11966
|
ok: true,
|
|
11880
11967
|
service: RELAY_SERVICE,
|
|
11881
11968
|
protocol: RELAY_PROTOCOL,
|
|
11882
11969
|
schema_version: RELAY_SCHEMA_VERSION,
|
|
11883
11970
|
relay_mode: "local-file",
|
|
11884
|
-
origin: normalizeRelayBaseUrl(
|
|
11971
|
+
origin: normalizeRelayBaseUrl(bindBaseUrl),
|
|
11885
11972
|
public_origin: normalizeRelayBaseUrl(publicBaseUrl),
|
|
11886
11973
|
connector_ui_available: connectorUiAvailable,
|
|
11887
11974
|
capabilities: relayCapabilities(connectorUiAvailable)
|
|
@@ -11954,13 +12041,13 @@ async function fetchRelayApproval(baseUrl, requestId) {
|
|
|
11954
12041
|
}
|
|
11955
12042
|
async function startRelayServer(options) {
|
|
11956
12043
|
const uiDistRoot = resolveConnectorUiDistRoot();
|
|
12044
|
+
let bindBaseUrl = "";
|
|
11957
12045
|
const server = createServer(async (request, response) => {
|
|
11958
12046
|
try {
|
|
11959
|
-
const requestUrl = new URL(request.url || "/",
|
|
12047
|
+
const requestUrl = new URL(request.url || "/", "http://localhost");
|
|
11960
12048
|
const pathname = requestUrl.pathname;
|
|
11961
12049
|
const method = request.method || "GET";
|
|
11962
|
-
const
|
|
11963
|
-
const publicBaseUrl = resolveRelayPublicBaseUrl(actualBaseUrl, options.publicOrigin);
|
|
12050
|
+
const publicBaseUrl = resolveRelayPublicBaseUrl(bindBaseUrl, options.publicOrigin);
|
|
11964
12051
|
if (method === "OPTIONS") {
|
|
11965
12052
|
writeJson2(response, 204, {});
|
|
11966
12053
|
return;
|
|
@@ -11969,7 +12056,7 @@ async function startRelayServer(options) {
|
|
|
11969
12056
|
writeJson2(
|
|
11970
12057
|
response,
|
|
11971
12058
|
200,
|
|
11972
|
-
relayHealthResponse(
|
|
12059
|
+
relayHealthResponse(bindBaseUrl, publicBaseUrl, Boolean(uiDistRoot))
|
|
11973
12060
|
);
|
|
11974
12061
|
return;
|
|
11975
12062
|
}
|
|
@@ -12044,12 +12131,12 @@ async function startRelayServer(options) {
|
|
|
12044
12131
|
return;
|
|
12045
12132
|
}
|
|
12046
12133
|
if (fs7.existsSync(filePath) && fs7.statSync(filePath).isFile()) {
|
|
12047
|
-
|
|
12134
|
+
writeBody(response, 200, contentTypeFor(filePath), fs7.readFileSync(filePath));
|
|
12048
12135
|
return;
|
|
12049
12136
|
}
|
|
12050
12137
|
const indexPath = path6.join(uiDistRoot, "index.html");
|
|
12051
12138
|
if (fs7.existsSync(indexPath)) {
|
|
12052
|
-
|
|
12139
|
+
writeBody(response, 200, "text/html; charset=utf-8", fs7.readFileSync(indexPath));
|
|
12053
12140
|
return;
|
|
12054
12141
|
}
|
|
12055
12142
|
}
|
|
@@ -12074,9 +12161,10 @@ async function startRelayServer(options) {
|
|
|
12074
12161
|
});
|
|
12075
12162
|
});
|
|
12076
12163
|
});
|
|
12164
|
+
bindBaseUrl = `http://${address.address}:${address.port}`;
|
|
12077
12165
|
return {
|
|
12078
12166
|
connectorUiAvailable: Boolean(uiDistRoot),
|
|
12079
|
-
origin:
|
|
12167
|
+
origin: bindBaseUrl,
|
|
12080
12168
|
port: address.port,
|
|
12081
12169
|
close: async () => await new Promise((resolve, reject) => {
|
|
12082
12170
|
server.close((error) => {
|
|
@@ -12853,6 +12941,7 @@ var WALLET_HELP_COMMAND_ORDER = [
|
|
|
12853
12941
|
"rename",
|
|
12854
12942
|
"remove",
|
|
12855
12943
|
"request",
|
|
12944
|
+
"signer",
|
|
12856
12945
|
"paymaster",
|
|
12857
12946
|
"smart-account"
|
|
12858
12947
|
];
|
|
@@ -12872,6 +12961,11 @@ var WALLET_SMART_ACCOUNT_HELP_COMMAND_ORDER = [
|
|
|
12872
12961
|
"sed-lite",
|
|
12873
12962
|
"daily-spend-limit"
|
|
12874
12963
|
];
|
|
12964
|
+
var WALLET_SIGNER_HELP_COMMAND_ORDER = [
|
|
12965
|
+
"show",
|
|
12966
|
+
"attach",
|
|
12967
|
+
"remove"
|
|
12968
|
+
];
|
|
12875
12969
|
function applyCommandOrder(command, orderedNames) {
|
|
12876
12970
|
const order = new Map(orderedNames.map((name, index) => [name, index]));
|
|
12877
12971
|
const sortedCommands = [...command.commands].sort((left, right) => {
|
|
@@ -12892,6 +12986,10 @@ function sanitizeSessionPayload(payload) {
|
|
|
12892
12986
|
function stripSensitiveWalletRecord(wallet) {
|
|
12893
12987
|
return {
|
|
12894
12988
|
...wallet,
|
|
12989
|
+
localExecutionAuthority: wallet.localExecutionAuthority ? {
|
|
12990
|
+
...wallet.localExecutionAuthority,
|
|
12991
|
+
privateKey: void 0
|
|
12992
|
+
} : wallet.localExecutionAuthority,
|
|
12895
12993
|
sessionPayload: wallet.sessionPayload ? {
|
|
12896
12994
|
...wallet.sessionPayload,
|
|
12897
12995
|
sessionPrivateKey: void 0
|
|
@@ -12982,6 +13080,7 @@ function isRecord5(value) {
|
|
|
12982
13080
|
function cloneWalletSessionRecord(wallet) {
|
|
12983
13081
|
return {
|
|
12984
13082
|
...wallet,
|
|
13083
|
+
localExecutionAuthority: wallet.localExecutionAuthority ? { ...wallet.localExecutionAuthority } : wallet.localExecutionAuthority,
|
|
12985
13084
|
validationHookAddresses: wallet.validationHookAddresses ? [...wallet.validationHookAddresses] : wallet.validationHookAddresses,
|
|
12986
13085
|
sessionScope: wallet.sessionScope ? {
|
|
12987
13086
|
...wallet.sessionScope,
|
|
@@ -13032,6 +13131,12 @@ function parseWalletExportRecord(value) {
|
|
|
13032
13131
|
if (wallet.validatorAddress && !isAddress3(wallet.validatorAddress)) {
|
|
13033
13132
|
throw new Error("Restore payload validatorAddress must be a valid 20-byte hex address.");
|
|
13034
13133
|
}
|
|
13134
|
+
if (wallet.localExecutionAuthority?.signerAddress && !isAddress3(wallet.localExecutionAuthority.signerAddress)) {
|
|
13135
|
+
throw new Error("Restore payload localExecutionAuthority.signerAddress must be a valid 20-byte hex address.");
|
|
13136
|
+
}
|
|
13137
|
+
if (wallet.localExecutionAuthority?.privateKey && !/^0x[a-fA-F0-9]{64}$/.test(wallet.localExecutionAuthority.privateKey)) {
|
|
13138
|
+
throw new Error("Restore payload localExecutionAuthority.privateKey must be a valid 32-byte hex string.");
|
|
13139
|
+
}
|
|
13035
13140
|
if (wallet.validationHookAddresses && wallet.validationHookAddresses.some((hookAddress) => !isAddress3(hookAddress))) {
|
|
13036
13141
|
throw new Error("Restore payload validationHookAddresses must contain valid 20-byte hex addresses.");
|
|
13037
13142
|
}
|
|
@@ -13064,7 +13169,7 @@ function parseWalletExportRecord(value) {
|
|
|
13064
13169
|
}
|
|
13065
13170
|
return {
|
|
13066
13171
|
...candidate,
|
|
13067
|
-
wallet: cloneWalletSessionRecord(wallet)
|
|
13172
|
+
wallet: migrateWalletSessionRecord(cloneWalletSessionRecord(wallet))
|
|
13068
13173
|
};
|
|
13069
13174
|
}
|
|
13070
13175
|
function normalizeHexString2(value, label) {
|
|
@@ -13075,6 +13180,13 @@ function normalizeHexString2(value, label) {
|
|
|
13075
13180
|
}
|
|
13076
13181
|
return prefixed;
|
|
13077
13182
|
}
|
|
13183
|
+
function normalizePrivateKey(value, label) {
|
|
13184
|
+
const normalized = normalizeHexString2(value, label);
|
|
13185
|
+
if (!/^0x[a-fA-F0-9]{64}$/.test(normalized)) {
|
|
13186
|
+
throw new Error(`${label} must be a valid 32-byte hex string.`);
|
|
13187
|
+
}
|
|
13188
|
+
return normalized;
|
|
13189
|
+
}
|
|
13078
13190
|
function parseArtifactInput(value) {
|
|
13079
13191
|
const raw = parseJsonInput(value);
|
|
13080
13192
|
if (!isRecord5(raw)) throw new Error("Artifact must be a JSON object");
|
|
@@ -13783,6 +13895,12 @@ function inspectionLines(inspection) {
|
|
|
13783
13895
|
inspection.signerMatchesStoredIdentity ? "yes" : "no"
|
|
13784
13896
|
]);
|
|
13785
13897
|
}
|
|
13898
|
+
if (typeof inspection.approvalReady === "boolean") {
|
|
13899
|
+
lines.push(["approval", inspection.approvalReady ? "present" : "missing"]);
|
|
13900
|
+
}
|
|
13901
|
+
if (typeof inspection.localExecutionKeyStored === "boolean") {
|
|
13902
|
+
lines.push(["local signer", inspection.localExecutionKeyStored ? "stored" : "missing"]);
|
|
13903
|
+
}
|
|
13786
13904
|
lines.push(["session key", inspection.sessionPrivateKeyStored ? "stored" : "missing"]);
|
|
13787
13905
|
if (inspection.paymasterMode) {
|
|
13788
13906
|
lines.push(["paymaster", inspection.paymasterMode]);
|
|
@@ -13881,6 +13999,7 @@ function walletExportLines(wallet, bundle) {
|
|
|
13881
13999
|
}
|
|
13882
14000
|
function walletRestoreLines(wallet, restoredFrom, syncResult) {
|
|
13883
14001
|
let nextCommand = buildWalletNextRecommendedCommand(wallet.walletName);
|
|
14002
|
+
let afterNextCommandLabel;
|
|
13884
14003
|
const lines = [
|
|
13885
14004
|
["wallet", wallet.walletName],
|
|
13886
14005
|
["address", wallet.walletAddress],
|
|
@@ -13891,12 +14010,15 @@ function walletRestoreLines(wallet, restoredFrom, syncResult) {
|
|
|
13891
14010
|
["source export", restoredFrom.exportedAt],
|
|
13892
14011
|
["sensitive data", restoredFrom.sensitiveDataIncluded ? "included in backup" : "not included in backup"]
|
|
13893
14012
|
];
|
|
13894
|
-
if (!wallet
|
|
14013
|
+
if (!resolveLocalExecutionPrivateKey(wallet)) {
|
|
14014
|
+
const attachSignerCommand = buildWalletSignerAttachRecommendedCommand(wallet.walletName);
|
|
14015
|
+
const reapproveCommand = buildWalletReapproveRecommendedCommand(wallet.walletName);
|
|
13895
14016
|
lines.push([
|
|
13896
14017
|
"note",
|
|
13897
|
-
`No
|
|
14018
|
+
wallet.sessionPayload ? `No local execution signer was present in the backup. The restored wallet can be inspected and synced, but local write execution will stay blocked until you attach a local signer or re-approve a writable session, for example: ${attachSignerCommand}` : `No local execution signer was present in the backup. The restored wallet can be inspected and synced, but local write execution will stay blocked until you re-import, attach a local signer, or re-approve a writable session, for example: ${reapproveCommand}`
|
|
13898
14019
|
]);
|
|
13899
|
-
nextCommand =
|
|
14020
|
+
nextCommand = wallet.sessionPayload ? attachSignerCommand : reapproveCommand;
|
|
14021
|
+
afterNextCommandLabel = wallet.sessionPayload ? "after signer attach" : "after reapprove";
|
|
13900
14022
|
}
|
|
13901
14023
|
if (syncResult) {
|
|
13902
14024
|
lines.push(["sync", "completed"]);
|
|
@@ -13920,7 +14042,10 @@ function walletRestoreLines(wallet, restoredFrom, syncResult) {
|
|
|
13920
14042
|
}
|
|
13921
14043
|
lines.push(["next", nextCommand]);
|
|
13922
14044
|
if (nextCommand !== buildWalletNextRecommendedCommand(wallet.walletName)) {
|
|
13923
|
-
lines.push([
|
|
14045
|
+
lines.push([
|
|
14046
|
+
afterNextCommandLabel || "after reapprove",
|
|
14047
|
+
buildWalletNextRecommendedCommand(wallet.walletName)
|
|
14048
|
+
]);
|
|
13924
14049
|
}
|
|
13925
14050
|
lines.push(["status command", buildWalletStatusRecommendedCommand(wallet.walletName)]);
|
|
13926
14051
|
return lines;
|
|
@@ -13932,6 +14057,17 @@ function preserveExistingWalletMetadata(importedWallet, existingWallet) {
|
|
|
13932
14057
|
if (existingWallet.chain !== importedWallet.chain || existingWallet.chainId !== importedWallet.chainId || existingWallet.walletAddress.toLowerCase() !== importedWallet.walletAddress.toLowerCase()) {
|
|
13933
14058
|
return importedWallet;
|
|
13934
14059
|
}
|
|
14060
|
+
const sameAccountKind = existingWallet.accountKind === importedWallet.accountKind;
|
|
14061
|
+
const sameSmartAccountOwner = existingWallet.accountKind !== "smart-account" || existingWallet.ownerAddress && importedWallet.ownerAddress && existingWallet.ownerAddress.toLowerCase() === importedWallet.ownerAddress.toLowerCase();
|
|
14062
|
+
const existingSessionPrivateKey = resolveLocalExecutionPrivateKey(existingWallet);
|
|
14063
|
+
const existingLocalExecutionAuthority = existingWallet.localExecutionAuthority || buildLocalExecutionAuthority({
|
|
14064
|
+
privateKey: existingSessionPrivateKey,
|
|
14065
|
+
signerType: existingWallet.sessionPayload?.account?.signerType,
|
|
14066
|
+
source: existingSessionPrivateKey ? "legacy-session-payload" : void 0,
|
|
14067
|
+
attachedAt: existingWallet.createdAt
|
|
14068
|
+
});
|
|
14069
|
+
const importedSessionPrivateKey = resolveLocalExecutionPrivateKey(importedWallet);
|
|
14070
|
+
const shouldPreserveWritableSession = sameAccountKind && sameSmartAccountOwner && Boolean(existingSessionPrivateKey) && !importedSessionPrivateKey;
|
|
13935
14071
|
const metadataUpdates = {};
|
|
13936
14072
|
if (existingWallet.smartAccountProfileId) {
|
|
13937
14073
|
const profileId = tryResolveBuiltinProfileId(existingWallet.smartAccountProfileId);
|
|
@@ -13948,7 +14084,13 @@ function preserveExistingWalletMetadata(importedWallet, existingWallet) {
|
|
|
13948
14084
|
if (existingWallet.validatorAddress) {
|
|
13949
14085
|
metadataUpdates.validatorAddress = existingWallet.validatorAddress;
|
|
13950
14086
|
}
|
|
13951
|
-
|
|
14087
|
+
const mergedWallet = shouldPreserveWritableSession && existingLocalExecutionAuthority ? {
|
|
14088
|
+
...importedWallet,
|
|
14089
|
+
localExecutionAuthority: {
|
|
14090
|
+
...existingLocalExecutionAuthority
|
|
14091
|
+
}
|
|
14092
|
+
} : importedWallet;
|
|
14093
|
+
return migrateWalletSessionRecord(applyWalletSyncMetadata(mergedWallet, metadataUpdates));
|
|
13952
14094
|
}
|
|
13953
14095
|
function linesForWriteResult2(result, nextCommand) {
|
|
13954
14096
|
const lines = [
|
|
@@ -14042,23 +14184,85 @@ function buildWalletApprovalLines(status, requestId, walletRecord) {
|
|
|
14042
14184
|
];
|
|
14043
14185
|
}
|
|
14044
14186
|
function buildWalletFollowUpRecommendedCommands(walletRecord) {
|
|
14187
|
+
const localExecutionReady = Boolean(resolveLocalExecutionPrivateKey(walletRecord));
|
|
14188
|
+
const approvalReady = Boolean(walletRecord.sessionPayload);
|
|
14045
14189
|
return {
|
|
14046
14190
|
next: buildWalletNextRecommendedCommand(walletRecord.walletName),
|
|
14047
14191
|
status: buildWalletStatusRecommendedCommand(walletRecord.walletName),
|
|
14048
|
-
...!
|
|
14192
|
+
...!localExecutionReady && approvalReady ? {
|
|
14193
|
+
attachSigner: buildWalletSignerAttachRecommendedCommand(walletRecord.walletName),
|
|
14194
|
+
reapprove: buildWalletReapproveRecommendedCommand(walletRecord.walletName)
|
|
14195
|
+
} : !localExecutionReady ? { reapprove: buildWalletReapproveRecommendedCommand(walletRecord.walletName) } : {}
|
|
14196
|
+
};
|
|
14197
|
+
}
|
|
14198
|
+
function walletSignerSummary(walletRecord) {
|
|
14199
|
+
const authority = walletRecord.localExecutionAuthority;
|
|
14200
|
+
const localExecutionPrivateKey = resolveLocalExecutionPrivateKey(walletRecord);
|
|
14201
|
+
return {
|
|
14202
|
+
approvalReady: Boolean(walletRecord.sessionPayload),
|
|
14203
|
+
localExecutionKeyStored: Boolean(localExecutionPrivateKey),
|
|
14204
|
+
legacySessionKeyStored: Boolean(walletRecord.sessionPayload?.sessionPrivateKey),
|
|
14205
|
+
signerAddress: authority?.signerAddress || deriveAddressFromPrivateKey(localExecutionPrivateKey),
|
|
14206
|
+
signerType: authority?.signerType || walletRecord.sessionPayload?.account?.signerType,
|
|
14207
|
+
source: authority?.source || (walletRecord.sessionPayload?.sessionPrivateKey ? "legacy-session-payload" : void 0),
|
|
14208
|
+
attachedAt: authority?.attachedAt
|
|
14209
|
+
};
|
|
14210
|
+
}
|
|
14211
|
+
function walletSignerLines(walletRecord) {
|
|
14212
|
+
const signer = walletSignerSummary(walletRecord);
|
|
14213
|
+
return [
|
|
14214
|
+
["wallet", walletRecord.walletName],
|
|
14215
|
+
["address", walletRecord.walletAddress],
|
|
14216
|
+
...displayOwnerAddress(walletRecord) ? [["owner", displayOwnerAddress(walletRecord)]] : [],
|
|
14217
|
+
["account", displayAccountKind(walletRecord)],
|
|
14218
|
+
["chain", `${walletRecord.chain} (${walletRecord.chainId})`],
|
|
14219
|
+
["approval", signer.approvalReady ? "present" : "missing"],
|
|
14220
|
+
["local signer", signer.localExecutionKeyStored ? "stored" : "missing"],
|
|
14221
|
+
["legacy payload mirror", signer.legacySessionKeyStored ? "present" : "missing"],
|
|
14222
|
+
...signer.signerType ? [["signer type", signer.signerType]] : [],
|
|
14223
|
+
...signer.signerAddress ? [["signer address", signer.signerAddress]] : [],
|
|
14224
|
+
...signer.source ? [["source", signer.source]] : [],
|
|
14225
|
+
...signer.attachedAt ? [["attached", signer.attachedAt]] : [],
|
|
14226
|
+
...walletFollowUpLines(walletRecord)
|
|
14227
|
+
];
|
|
14228
|
+
}
|
|
14229
|
+
function walletSignerAttachResult(walletRecord, note) {
|
|
14230
|
+
return {
|
|
14231
|
+
ok: true,
|
|
14232
|
+
wallet: sanitizeWalletRecord(walletRecord),
|
|
14233
|
+
signer: walletSignerSummary(walletRecord),
|
|
14234
|
+
...note ? { note } : {},
|
|
14235
|
+
nextAction: buildWalletFollowUpNextAction(walletRecord),
|
|
14236
|
+
recommendedCommands: buildWalletFollowUpRecommendedCommands(walletRecord)
|
|
14049
14237
|
};
|
|
14050
14238
|
}
|
|
14239
|
+
function normalizeSignerType(value) {
|
|
14240
|
+
if (value === "local" || value === "connector" || value === "external") {
|
|
14241
|
+
return value;
|
|
14242
|
+
}
|
|
14243
|
+
throw new Error(`Unsupported signer type: ${value}`);
|
|
14244
|
+
}
|
|
14245
|
+
function clearLegacySessionPrivateKey(payload) {
|
|
14246
|
+
return payload ? {
|
|
14247
|
+
...payload,
|
|
14248
|
+
sessionPrivateKey: void 0
|
|
14249
|
+
} : payload;
|
|
14250
|
+
}
|
|
14051
14251
|
function walletFollowUpLines(walletRecord) {
|
|
14052
14252
|
const recommendedCommands = buildWalletFollowUpRecommendedCommands(walletRecord);
|
|
14253
|
+
const nextCommand = recommendedCommands.attachSigner ?? recommendedCommands.reapprove ?? recommendedCommands.next;
|
|
14053
14254
|
return [
|
|
14054
|
-
["next",
|
|
14055
|
-
...recommendedCommands.
|
|
14255
|
+
["next", nextCommand],
|
|
14256
|
+
...recommendedCommands.attachSigner ? [
|
|
14257
|
+
["after signer attach", recommendedCommands.next],
|
|
14258
|
+
["or reapprove", recommendedCommands.reapprove]
|
|
14259
|
+
] : recommendedCommands.reapprove ? [["after reapprove", recommendedCommands.next]] : [],
|
|
14056
14260
|
["status command", recommendedCommands.status]
|
|
14057
14261
|
];
|
|
14058
14262
|
}
|
|
14059
14263
|
function buildWalletFollowUpNextAction(walletRecord) {
|
|
14060
14264
|
const recommendedCommands = buildWalletFollowUpRecommendedCommands(walletRecord);
|
|
14061
|
-
return recommendedCommands.reapprove ?? recommendedCommands.next;
|
|
14265
|
+
return recommendedCommands.attachSigner ?? recommendedCommands.reapprove ?? recommendedCommands.next;
|
|
14062
14266
|
}
|
|
14063
14267
|
function buildPendingRequestRecommendedCommands(walletName, requestId, relayUrl, paymasterMode) {
|
|
14064
14268
|
return {
|
|
@@ -14136,7 +14340,7 @@ async function createWalletReapprovalRequest(options) {
|
|
|
14136
14340
|
chain: options.walletRecord.chain,
|
|
14137
14341
|
connectorUrl,
|
|
14138
14342
|
accountKind: displayAccountKind(options.walletRecord),
|
|
14139
|
-
paymasterMode: displayPaymasterMode(options.walletRecord),
|
|
14343
|
+
paymasterMode: options.paymasterMode || displayPaymasterMode(options.walletRecord),
|
|
14140
14344
|
policies
|
|
14141
14345
|
});
|
|
14142
14346
|
await saveWalletRequest(request);
|
|
@@ -14604,6 +14808,9 @@ function createWalletCommand(deps) {
|
|
|
14604
14808
|
const resolvedDeps = resolveWalletCommandDeps(deps);
|
|
14605
14809
|
const wallet = new Command9("wallet").description("Manage wallet sessions");
|
|
14606
14810
|
const request = new Command9("request").description("Inspect and finalize pending wallet requests");
|
|
14811
|
+
const signer = new Command9("signer").description(
|
|
14812
|
+
"Inspect and manage the stored local execution signer for a wallet"
|
|
14813
|
+
);
|
|
14607
14814
|
const smartAccount = new Command9("smart-account").description(
|
|
14608
14815
|
"Predict and deploy zkSync smart-account contracts from a supplied artifact or built-in profile"
|
|
14609
14816
|
);
|
|
@@ -14634,8 +14841,11 @@ function createWalletCommand(deps) {
|
|
|
14634
14841
|
" zk-agent wallet create --await-local",
|
|
14635
14842
|
" zk-agent next",
|
|
14636
14843
|
"",
|
|
14637
|
-
" Restore
|
|
14844
|
+
" Restore approval metadata for an existing wallet:",
|
|
14638
14845
|
" zk-agent wallet reapprove --name main --await-local",
|
|
14846
|
+
"",
|
|
14847
|
+
" Attach a local signer when approval is still present:",
|
|
14848
|
+
" zk-agent wallet signer attach --name main --private-key <hex>",
|
|
14639
14849
|
" zk-agent next",
|
|
14640
14850
|
"",
|
|
14641
14851
|
" Wallet-layer inspection:",
|
|
@@ -14643,8 +14853,9 @@ function createWalletCommand(deps) {
|
|
|
14643
14853
|
" zk-agent wallet next --name main",
|
|
14644
14854
|
"",
|
|
14645
14855
|
" Remote approval path:",
|
|
14646
|
-
" zk-agent
|
|
14647
|
-
" zk-agent wallet
|
|
14856
|
+
" zk-agent relay inspect --relay-url <url>",
|
|
14857
|
+
" zk-agent wallet create --relay-url <url> --wait-relay --prompt-code",
|
|
14858
|
+
" zk-agent wallet reapprove --name main --relay-url <url> --wait-relay --prompt-code"
|
|
14648
14859
|
].join("\n")
|
|
14649
14860
|
);
|
|
14650
14861
|
request.addHelpText(
|
|
@@ -14661,6 +14872,21 @@ function createWalletCommand(deps) {
|
|
|
14661
14872
|
" zk-agent wallet request approve --request-id <id> --relay-url <url> --code <code> --wait"
|
|
14662
14873
|
].join("\n")
|
|
14663
14874
|
);
|
|
14875
|
+
signer.addHelpText(
|
|
14876
|
+
"after",
|
|
14877
|
+
[
|
|
14878
|
+
"",
|
|
14879
|
+
"Wallet signer path:",
|
|
14880
|
+
" Inspect the stored local execution signer state:",
|
|
14881
|
+
" zk-agent wallet signer show --name main",
|
|
14882
|
+
"",
|
|
14883
|
+
" Attach a local execution signer without rebuilding approval metadata:",
|
|
14884
|
+
" zk-agent wallet signer attach --name main --private-key <hex>",
|
|
14885
|
+
"",
|
|
14886
|
+
" Remove the stored local execution signer:",
|
|
14887
|
+
" zk-agent wallet signer remove --name main"
|
|
14888
|
+
].join("\n")
|
|
14889
|
+
);
|
|
14664
14890
|
smartAccount.addHelpText(
|
|
14665
14891
|
"after",
|
|
14666
14892
|
[
|
|
@@ -15029,7 +15255,56 @@ function createWalletCommand(deps) {
|
|
|
15029
15255
|
}
|
|
15030
15256
|
);
|
|
15031
15257
|
});
|
|
15032
|
-
|
|
15258
|
+
signer.command("show").description("Show the stored local execution signer state for a wallet").option("--name <name>", "Wallet name", "main").action(async (options) => {
|
|
15259
|
+
const walletRecord = await requireWalletRecord(options.name);
|
|
15260
|
+
printResult(walletSignerLines(walletRecord), {
|
|
15261
|
+
ok: true,
|
|
15262
|
+
wallet: sanitizeWalletRecord(walletRecord),
|
|
15263
|
+
signer: walletSignerSummary(walletRecord),
|
|
15264
|
+
nextAction: buildWalletFollowUpNextAction(walletRecord),
|
|
15265
|
+
recommendedCommands: buildWalletFollowUpRecommendedCommands(walletRecord)
|
|
15266
|
+
});
|
|
15267
|
+
});
|
|
15268
|
+
signer.command("attach").description("Attach a stored local execution signer to an existing wallet record").option("--name <name>", "Wallet name", "main").requiredOption("--private-key <hex>", "Local execution private key to store").action(async (options) => {
|
|
15269
|
+
const walletRecord = await requireWalletRecord(options.name);
|
|
15270
|
+
const privateKey = normalizePrivateKey(options.privateKey, "--private-key");
|
|
15271
|
+
const nextWallet = migrateWalletSessionRecord({
|
|
15272
|
+
...walletRecord,
|
|
15273
|
+
localExecutionAuthority: buildLocalExecutionAuthority({
|
|
15274
|
+
privateKey,
|
|
15275
|
+
signerType: "local",
|
|
15276
|
+
source: "explicit-local-approval",
|
|
15277
|
+
attachedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15278
|
+
}),
|
|
15279
|
+
sessionPayload: clearLegacySessionPrivateKey(walletRecord.sessionPayload)
|
|
15280
|
+
});
|
|
15281
|
+
const note = walletRecord.sessionPayload ? void 0 : "No approved session metadata is stored yet. Attach succeeded, but wallet reapprove is still required before local write execution becomes usable.";
|
|
15282
|
+
await saveWalletSession(nextWallet);
|
|
15283
|
+
printResult(
|
|
15284
|
+
walletSignerLines(nextWallet),
|
|
15285
|
+
walletSignerAttachResult(nextWallet, note)
|
|
15286
|
+
);
|
|
15287
|
+
});
|
|
15288
|
+
signer.command("remove").description("Remove the stored local execution signer from a wallet record").option("--name <name>", "Wallet name", "main").action(async (options) => {
|
|
15289
|
+
const walletRecord = await requireWalletRecord(options.name);
|
|
15290
|
+
const hadLocalExecutionKey = Boolean(resolveLocalExecutionPrivateKey(walletRecord));
|
|
15291
|
+
const nextWallet = migrateWalletSessionRecord({
|
|
15292
|
+
...walletRecord,
|
|
15293
|
+
localExecutionAuthority: void 0,
|
|
15294
|
+
sessionPayload: clearLegacySessionPrivateKey(walletRecord.sessionPayload)
|
|
15295
|
+
});
|
|
15296
|
+
const note = hadLocalExecutionKey ? "Removed the stored local execution signer. Local write execution now requires signer attach or reapproval." : "No stored local execution signer was present.";
|
|
15297
|
+
await saveWalletSession(nextWallet);
|
|
15298
|
+
printResult(
|
|
15299
|
+
walletSignerLines(nextWallet),
|
|
15300
|
+
walletSignerAttachResult(nextWallet, note)
|
|
15301
|
+
);
|
|
15302
|
+
});
|
|
15303
|
+
wallet.command("export").description("Export one stored wallet as a portable backup bundle for later restore").option("--name <name>", "Wallet name", "main").option(
|
|
15304
|
+
"--include-sensitive-data",
|
|
15305
|
+
"Include the stored local execution key and any legacy sessionPrivateKey mirror in the exported bundle",
|
|
15306
|
+
false
|
|
15307
|
+
).action(async (options) => {
|
|
15033
15308
|
const walletRecord = await requireWalletRecord(options.name);
|
|
15034
15309
|
const bundle = exportWalletRecord(walletRecord, Boolean(options.includeSensitiveData));
|
|
15035
15310
|
const recommendedCommands = {
|
|
@@ -15334,7 +15609,7 @@ function createWalletCommand(deps) {
|
|
|
15334
15609
|
}
|
|
15335
15610
|
);
|
|
15336
15611
|
});
|
|
15337
|
-
request.command("approve-local").description("Manually construct an approved session payload from CLI inputs and save the resulting wallet").requiredOption("--request-id <id>", "Wallet request id").requiredOption("--wallet-address <address>", "Approved execution address (EOA address or smart-account address)").option("--owner-address <address>", "Owner / signer address for smart-account sessions").option("--name <name>", "Override saved wallet name").option("--session-address <address>", "Optional session address").option("--session-private-key <hex>", "Optional local private key for writable
|
|
15612
|
+
request.command("approve-local").description("Manually construct an approved session payload from CLI inputs and save the resulting wallet").requiredOption("--request-id <id>", "Wallet request id").requiredOption("--wallet-address <address>", "Approved execution address (EOA address or smart-account address)").option("--owner-address <address>", "Owner / signer address for smart-account sessions").option("--name <name>", "Override saved wallet name").option("--session-address <address>", "Optional session address").option("--session-private-key <hex>", "Optional local execution private key for writable sessions").option("--validator-address <address>", "Optional validator address").option("--paymaster-address <address>", "Optional paymaster address").option("--paymaster-token <address>", "Optional ERC-20 token used by an approval-based paymaster").option("--signer-type <type>", "Optional signer type override: local, connector, or external").action(
|
|
15338
15613
|
async (options) => {
|
|
15339
15614
|
const walletRequest = await requireActiveWalletRequest(options.requestId);
|
|
15340
15615
|
assertRequestActive(walletRequest.expiresAt);
|
|
@@ -15354,7 +15629,7 @@ function createWalletCommand(deps) {
|
|
|
15354
15629
|
validatorAddress: options.validatorAddress,
|
|
15355
15630
|
paymasterAddress: options.paymasterAddress,
|
|
15356
15631
|
paymasterToken: options.paymasterToken,
|
|
15357
|
-
signerType: options.signerType,
|
|
15632
|
+
signerType: options.signerType ? normalizeSignerType(options.signerType) : void 0,
|
|
15358
15633
|
connectorOrigin: connectorOriginFromUrl(walletRequest.connectorUrl),
|
|
15359
15634
|
connectorUrl: walletRequest.connectorUrl
|
|
15360
15635
|
});
|
|
@@ -16940,10 +17215,12 @@ function createWalletCommand(deps) {
|
|
|
16940
17215
|
sedLite.addCommand(selectorAllowlistHook);
|
|
16941
17216
|
smartAccount.addCommand(sedLite);
|
|
16942
17217
|
smartAccount.addCommand(dailySpendLimit);
|
|
16943
|
-
wallet.addCommand(smartAccount);
|
|
16944
|
-
wallet.addCommand(paymaster);
|
|
16945
17218
|
wallet.addCommand(request);
|
|
17219
|
+
wallet.addCommand(signer);
|
|
17220
|
+
wallet.addCommand(paymaster);
|
|
17221
|
+
wallet.addCommand(smartAccount);
|
|
16946
17222
|
applyCommandOrder(request, WALLET_REQUEST_HELP_COMMAND_ORDER);
|
|
17223
|
+
applyCommandOrder(signer, WALLET_SIGNER_HELP_COMMAND_ORDER);
|
|
16947
17224
|
applyCommandOrder(smartAccount, WALLET_SMART_ACCOUNT_HELP_COMMAND_ORDER);
|
|
16948
17225
|
applyCommandOrder(wallet, WALLET_HELP_COMMAND_ORDER);
|
|
16949
17226
|
return wallet;
|
|
@@ -17375,7 +17652,7 @@ function prependWorkflowRequestId(requestId, lines) {
|
|
|
17375
17652
|
return [["workflow request", requestId], ...lines];
|
|
17376
17653
|
}
|
|
17377
17654
|
function workflowHasSessionApprovalBlocker(status) {
|
|
17378
|
-
return status.blockingActionIds.some((actionId) => actionId === "reapprove"
|
|
17655
|
+
return status.blockingActionIds.some((actionId) => actionId === "reapprove");
|
|
17379
17656
|
}
|
|
17380
17657
|
function workflowShouldEnsureWalletSession(options) {
|
|
17381
17658
|
return Boolean(options.ensureWalletSession || options.awaitLocal);
|
|
@@ -17449,10 +17726,14 @@ async function ensureWorkflowWalletSession(input, deps) {
|
|
|
17449
17726
|
goal: input.goal,
|
|
17450
17727
|
options: input.options
|
|
17451
17728
|
});
|
|
17729
|
+
const paymasterInput = resolveWorkflowPaymasterInput(input.options);
|
|
17452
17730
|
const reusableRequest = await deps.findReusableWalletRequest(input.wallet.walletName);
|
|
17453
17731
|
const walletRequest = reusableRequest || await deps.createWalletReapprovalRequest({
|
|
17454
17732
|
walletRecord: input.wallet,
|
|
17455
17733
|
connectorUrl: input.options.connectorUrl,
|
|
17734
|
+
...paymasterInput?.mode ? {
|
|
17735
|
+
paymasterMode: paymasterInput.mode
|
|
17736
|
+
} : {},
|
|
17456
17737
|
sessionPreset: policyRequestOptions.sessionPreset,
|
|
17457
17738
|
sessionHours: policyRequestOptions.sessionHours,
|
|
17458
17739
|
allowTransferTo: policyRequestOptions.allowTransferTo,
|
|
@@ -17805,6 +18086,17 @@ function buildWorkflowAutoCheckpoint(context, status) {
|
|
|
17805
18086
|
status
|
|
17806
18087
|
});
|
|
17807
18088
|
}
|
|
18089
|
+
function applyWorkflowPayDefaults(options) {
|
|
18090
|
+
return {
|
|
18091
|
+
...options,
|
|
18092
|
+
intent: "send-native",
|
|
18093
|
+
createCheckpoint: true,
|
|
18094
|
+
executeWhenReady: true,
|
|
18095
|
+
ensureWalletSession: true,
|
|
18096
|
+
sessionPreset: options.sessionPreset?.trim() || "intent",
|
|
18097
|
+
paymasterMode: options.paymasterMode?.trim() || "approval-based"
|
|
18098
|
+
};
|
|
18099
|
+
}
|
|
17808
18100
|
async function executeWorkflowAutoCommand(options, deps = resolveWorkflowCommandDeps(void 0)) {
|
|
17809
18101
|
const { provider: provider4, defiProvider: defiProvider2 } = deps;
|
|
17810
18102
|
const context = await resolveWorkflowAutoExecutionContext(options);
|
|
@@ -18359,6 +18651,9 @@ function buildWorkflowHelpText() {
|
|
|
18359
18651
|
" Guided default:",
|
|
18360
18652
|
" zk-agent workflow auto --wallet main --intent <intent> [goal flags] --create-checkpoint --execute-when-ready",
|
|
18361
18653
|
"",
|
|
18654
|
+
" Flagship native pay path:",
|
|
18655
|
+
" zk-agent workflow pay --wallet main --to <address> --amount <amount>",
|
|
18656
|
+
"",
|
|
18362
18657
|
" Checkpointed execution:",
|
|
18363
18658
|
" zk-agent workflow start --wallet main --intent <intent> [goal flags]",
|
|
18364
18659
|
" zk-agent workflow status --request-id <id>",
|
|
@@ -18374,6 +18669,7 @@ function buildWorkflowHelpText() {
|
|
|
18374
18669
|
}
|
|
18375
18670
|
var WORKFLOW_HELP_COMMAND_ORDER = [
|
|
18376
18671
|
"auto",
|
|
18672
|
+
"pay",
|
|
18377
18673
|
"start",
|
|
18378
18674
|
"status",
|
|
18379
18675
|
"next",
|
|
@@ -18597,6 +18893,23 @@ function createWorkflowCommand(deps) {
|
|
|
18597
18893
|
const execution = await executeWorkflowAutoCommand(options, resolvedDeps);
|
|
18598
18894
|
await printWorkflowAutoCommandResult(execution);
|
|
18599
18895
|
}));
|
|
18896
|
+
const pay = workflow.command("pay").description(
|
|
18897
|
+
"Guided flagship AA native-send path with checkpoint persistence, intent-scoped session recovery, and paymaster-aware defaults"
|
|
18898
|
+
).option("--wallet <name>", "Wallet name", "main").option(
|
|
18899
|
+
"--request-id <id>",
|
|
18900
|
+
"Load the workflow definition from a stored checkpoint, or reserve this id for the flagship pay path"
|
|
18901
|
+
);
|
|
18902
|
+
addWorkflowGoalOptions(pay, {
|
|
18903
|
+
includeExecutionFlags: true,
|
|
18904
|
+
includeFundingDispatch: true,
|
|
18905
|
+
includeLocalApproval: true
|
|
18906
|
+
}).action(withWorkflowInputErrorHandling(async (options) => {
|
|
18907
|
+
const execution = await executeWorkflowAutoCommand(
|
|
18908
|
+
applyWorkflowPayDefaults(options),
|
|
18909
|
+
resolvedDeps
|
|
18910
|
+
);
|
|
18911
|
+
await printWorkflowAutoCommandResult(execution);
|
|
18912
|
+
}));
|
|
18600
18913
|
const run = workflow.command("run").description("Run the requested workflow, or stop on the next required prerequisite or funding step first").option(
|
|
18601
18914
|
"--intent <intent>",
|
|
18602
18915
|
"send-native, send-token, call-write, swap, bridge, deposit, or withdraw"
|