@agentlayer.tech/wallet 0.1.30 → 0.1.33
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/.openclaw/extensions/agent-wallet/README.md +1 -2
- package/.openclaw/extensions/agent-wallet/dist/index.js +6 -340
- package/.openclaw/extensions/agent-wallet/index.ts +6 -340
- package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +0 -43
- package/.openclaw/extensions/agent-wallet/package.json +1 -1
- package/.openclaw/extensions/agent-wallet/skills/wallet-operator/SKILL.md +1 -3
- package/CHANGELOG.md +60 -0
- package/README.md +0 -5
- package/agent-wallet/.env.example +0 -12
- package/agent-wallet/README.md +0 -35
- package/agent-wallet/agent_wallet/btc_user_wallets.py +32 -1
- package/agent-wallet/agent_wallet/config.py +11 -7
- package/agent-wallet/agent_wallet/evm_user_wallets.py +2 -0
- package/agent-wallet/agent_wallet/openclaw_adapter.py +1 -655
- package/agent-wallet/agent_wallet/openclaw_cli.py +0 -7
- package/agent-wallet/agent_wallet/providers/evm_portfolio.py +18 -42
- package/agent-wallet/agent_wallet/providers/jupiter.py +1 -307
- package/agent-wallet/agent_wallet/providers/wdk_btc_local.py +31 -3
- package/agent-wallet/agent_wallet/providers/wdk_evm_local.py +37 -3
- package/agent-wallet/agent_wallet/transaction_policy.py +0 -262
- package/agent-wallet/agent_wallet/wallet_layer/base.py +0 -100
- package/agent-wallet/agent_wallet/wallet_layer/solana.py +1 -1118
- package/agent-wallet/openclaw.plugin.json +0 -4
- package/agent-wallet/pyproject.toml +1 -1
- package/agent-wallet/scripts/install_agent_wallet.py +113 -6
- package/agent-wallet/scripts/install_openclaw_local_config.py +7 -5
- package/agent-wallet/skills/wallet-operator/SKILL.md +1 -5
- package/bin/openclaw-agent-wallet.mjs +434 -68
- package/claude-code/plugins/agent-wallet/scripts/run_mcp.sh +21 -3
- package/codex/plugins/agent-wallet/scripts/run_mcp.sh +18 -0
- package/codex/plugins/agent-wallet/server.py +2 -118
- package/hermes/plugins/agent_wallet/tools.py +1 -1
- package/package.json +1 -1
- package/wdk-btc-wallet/src/local_vault.js +45 -68
- package/wdk-btc-wallet/src/server.js +1 -0
- package/wdk-evm-wallet/README.md +4 -3
- package/wdk-evm-wallet/src/config.js +15 -0
- package/wdk-evm-wallet/src/local_vault.js +45 -68
- package/wdk-evm-wallet/src/server.js +1 -0
- package/agent-wallet/agent_wallet/providers/houdini.py +0 -539
|
@@ -26,14 +26,6 @@ function assertPositiveInteger(value, fieldName) {
|
|
|
26
26
|
return parsed;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function assertNonNegativeInteger(value, fieldName) {
|
|
30
|
-
const parsed = Number(value);
|
|
31
|
-
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
32
|
-
throw new Error(`${fieldName} must be a non-negative integer.`);
|
|
33
|
-
}
|
|
34
|
-
return parsed;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
29
|
function sanitizeLabel(label) {
|
|
38
30
|
const normalized = String(label ?? "").trim();
|
|
39
31
|
return normalized || "EVM Wallet";
|
|
@@ -67,6 +59,7 @@ async function encryptSeedPhrase({ seedPhrase, password, walletId }) {
|
|
|
67
59
|
cipher.final(),
|
|
68
60
|
]);
|
|
69
61
|
const tag = cipher.getAuthTag();
|
|
62
|
+
key.fill(0);
|
|
70
63
|
return {
|
|
71
64
|
version: VAULT_VERSION,
|
|
72
65
|
kdf: {
|
|
@@ -95,7 +88,12 @@ async function decryptSeedPhrase({ encrypted, password, walletId }) {
|
|
|
95
88
|
decipher.setAAD(Buffer.from(`wdk-evm-wallet:${walletId}:v${VAULT_VERSION}`, "utf8"));
|
|
96
89
|
decipher.setAuthTag(tag);
|
|
97
90
|
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
98
|
-
|
|
91
|
+
key.fill(0);
|
|
92
|
+
// The returned string is an unavoidable transient (V8 strings are immutable);
|
|
93
|
+
// the derived key and plaintext buffers are zeroized so no zeroizable secret lingers.
|
|
94
|
+
const seedPhrase = plaintext.toString("utf8");
|
|
95
|
+
plaintext.fill(0);
|
|
96
|
+
return seedPhrase;
|
|
99
97
|
}
|
|
100
98
|
|
|
101
99
|
async function decryptSeedPhraseWithPasswordCheck(args) {
|
|
@@ -117,7 +115,6 @@ async function decryptSeedPhraseWithPasswordCheck(args) {
|
|
|
117
115
|
export class LocalEvmVault {
|
|
118
116
|
constructor(config) {
|
|
119
117
|
this.config = config;
|
|
120
|
-
this._unlocked = new Map();
|
|
121
118
|
}
|
|
122
119
|
|
|
123
120
|
async createWallet({
|
|
@@ -139,10 +136,9 @@ export class LocalEvmVault {
|
|
|
139
136
|
source: "created",
|
|
140
137
|
network,
|
|
141
138
|
});
|
|
142
|
-
await this.unlockWallet({ walletId: wallet.walletId, password, timeoutSeconds: 0 });
|
|
143
139
|
return {
|
|
144
140
|
...wallet,
|
|
145
|
-
unlocked:
|
|
141
|
+
unlocked: false,
|
|
146
142
|
unlockExpiresAt: null,
|
|
147
143
|
...(revealSeedPhrase ? { seedPhrase } : {}),
|
|
148
144
|
};
|
|
@@ -160,39 +156,35 @@ export class LocalEvmVault {
|
|
|
160
156
|
source: "imported",
|
|
161
157
|
network,
|
|
162
158
|
});
|
|
163
|
-
await this.unlockWallet({ walletId: wallet.walletId, password, timeoutSeconds: 0 });
|
|
164
159
|
return {
|
|
165
160
|
...wallet,
|
|
166
|
-
unlocked:
|
|
161
|
+
unlocked: false,
|
|
167
162
|
unlockExpiresAt: null,
|
|
168
163
|
};
|
|
169
164
|
}
|
|
170
165
|
|
|
171
166
|
async listWallets() {
|
|
172
|
-
this.#sweepExpiredUnlocked();
|
|
173
167
|
const registry = await this.#loadRegistry();
|
|
174
|
-
return registry.wallets.map((wallet) => {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
unlockExpiresAt: unlocked ? unlocked.expiresAt : null,
|
|
180
|
-
};
|
|
181
|
-
});
|
|
168
|
+
return registry.wallets.map((wallet) => ({
|
|
169
|
+
...wallet,
|
|
170
|
+
unlocked: false,
|
|
171
|
+
unlockExpiresAt: null,
|
|
172
|
+
}));
|
|
182
173
|
}
|
|
183
174
|
|
|
184
175
|
async getWallet({ walletId }) {
|
|
185
|
-
this.#sweepExpiredUnlocked();
|
|
186
176
|
const wallet = await this.#getWalletMetadata(assertNonEmptyString(walletId, "walletId"));
|
|
187
|
-
const unlocked = this._unlocked.get(wallet.walletId);
|
|
188
177
|
return {
|
|
189
178
|
...wallet,
|
|
190
|
-
unlocked:
|
|
191
|
-
unlockExpiresAt:
|
|
179
|
+
unlocked: false,
|
|
180
|
+
unlockExpiresAt: null,
|
|
192
181
|
};
|
|
193
182
|
}
|
|
194
183
|
|
|
195
|
-
|
|
184
|
+
// Deprecated: the wallet now uses a decrypt-on-demand model and never holds a
|
|
185
|
+
// plaintext seed in memory between requests. This endpoint only verifies the
|
|
186
|
+
// password so callers get feedback; it does not persist any unlocked state.
|
|
187
|
+
async unlockWallet({ walletId, password }) {
|
|
196
188
|
const metadata = await this.#getWalletMetadata(assertNonEmptyString(walletId, "walletId"));
|
|
197
189
|
const encrypted = await this.#loadEncryptedWallet(walletId);
|
|
198
190
|
const secret = await decryptSeedPhraseWithPasswordCheck({
|
|
@@ -203,28 +195,21 @@ export class LocalEvmVault {
|
|
|
203
195
|
if (!WDK.isValidSeed(secret)) {
|
|
204
196
|
throw new Error("Decrypted wallet seed phrase is invalid.");
|
|
205
197
|
}
|
|
206
|
-
const ttl =
|
|
207
|
-
timeoutSeconds === undefined || timeoutSeconds === null
|
|
208
|
-
? this.config.unlockTimeoutSeconds
|
|
209
|
-
: assertNonNegativeInteger(timeoutSeconds, "timeoutSeconds");
|
|
210
|
-
const expiresAt = ttl === 0 ? null : new Date(Date.now() + ttl * 1000).toISOString();
|
|
211
|
-
this._unlocked.set(walletId, {
|
|
212
|
-
seedPhrase: secret,
|
|
213
|
-
expiresAt,
|
|
214
|
-
});
|
|
215
198
|
return {
|
|
216
199
|
walletId,
|
|
217
200
|
label: metadata.label,
|
|
218
|
-
unlocked:
|
|
219
|
-
unlockExpiresAt:
|
|
201
|
+
unlocked: false,
|
|
202
|
+
unlockExpiresAt: null,
|
|
203
|
+
deprecated: true,
|
|
220
204
|
};
|
|
221
205
|
}
|
|
222
206
|
|
|
207
|
+
// Deprecated no-op: the wallet is always sealed at rest in the decrypt-on-demand model.
|
|
223
208
|
async lockWallet({ walletId }) {
|
|
224
|
-
this._unlocked.delete(assertNonEmptyString(walletId, "walletId"));
|
|
225
209
|
return {
|
|
226
|
-
walletId,
|
|
210
|
+
walletId: assertNonEmptyString(walletId, "walletId"),
|
|
227
211
|
unlocked: false,
|
|
212
|
+
deprecated: true,
|
|
228
213
|
};
|
|
229
214
|
}
|
|
230
215
|
|
|
@@ -283,25 +268,19 @@ export class LocalEvmVault {
|
|
|
283
268
|
};
|
|
284
269
|
await this.#saveRegistry(registry);
|
|
285
270
|
|
|
286
|
-
const unlocked = this._unlocked.get(id);
|
|
287
|
-
if (unlocked) {
|
|
288
|
-
this._unlocked.set(id, {
|
|
289
|
-
seedPhrase,
|
|
290
|
-
expiresAt: unlocked.expiresAt,
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
|
|
294
271
|
return {
|
|
295
272
|
walletId: id,
|
|
296
273
|
label: metadata.label,
|
|
297
274
|
passwordChanged: true,
|
|
298
275
|
updatedAt,
|
|
299
|
-
unlocked:
|
|
300
|
-
unlockExpiresAt:
|
|
276
|
+
unlocked: false,
|
|
277
|
+
unlockExpiresAt: null,
|
|
301
278
|
};
|
|
302
279
|
}
|
|
303
280
|
|
|
304
|
-
|
|
281
|
+
// Decrypt-on-demand: the seed is decrypted just-in-time for a single signing
|
|
282
|
+
// request from the supplied password, never persisted between requests.
|
|
283
|
+
async resolveSeedPhrase({ walletId, seedPhrase, password }) {
|
|
305
284
|
if (typeof seedPhrase === "string" && seedPhrase.trim()) {
|
|
306
285
|
if (!WDK.isValidSeed(seedPhrase.trim())) {
|
|
307
286
|
throw new Error("seedPhrase must be a valid BIP-39 seed phrase.");
|
|
@@ -313,16 +292,23 @@ export class LocalEvmVault {
|
|
|
313
292
|
};
|
|
314
293
|
}
|
|
315
294
|
const id = assertNonEmptyString(walletId, "walletId");
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
295
|
+
if (typeof password !== "string" || !password.trim()) {
|
|
296
|
+
throw new Error("Wallet is locked. Provide password or seedPhrase explicitly.");
|
|
297
|
+
}
|
|
298
|
+
await this.#getWalletMetadata(id);
|
|
299
|
+
const encrypted = await this.#loadEncryptedWallet(id);
|
|
300
|
+
const secret = await decryptSeedPhraseWithPasswordCheck({
|
|
301
|
+
encrypted,
|
|
302
|
+
password: password.trim(),
|
|
303
|
+
walletId: id,
|
|
304
|
+
});
|
|
305
|
+
if (!WDK.isValidSeed(secret)) {
|
|
306
|
+
throw new Error("Decrypted wallet seed phrase is invalid.");
|
|
320
307
|
}
|
|
321
308
|
return {
|
|
322
|
-
seedPhrase:
|
|
323
|
-
source: "local-vault",
|
|
309
|
+
seedPhrase: secret,
|
|
310
|
+
source: "local-vault-jit",
|
|
324
311
|
walletId: id,
|
|
325
|
-
unlockExpiresAt: unlocked.expiresAt,
|
|
326
312
|
};
|
|
327
313
|
}
|
|
328
314
|
|
|
@@ -418,13 +404,4 @@ export class LocalEvmVault {
|
|
|
418
404
|
#registryPath() {
|
|
419
405
|
return path.join(this.config.dataDir, REGISTRY_FILE);
|
|
420
406
|
}
|
|
421
|
-
|
|
422
|
-
#sweepExpiredUnlocked() {
|
|
423
|
-
const now = Date.now();
|
|
424
|
-
for (const [walletId, state] of this._unlocked.entries()) {
|
|
425
|
-
if (state.expiresAt && Date.parse(state.expiresAt) <= now) {
|
|
426
|
-
this._unlocked.delete(walletId);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
407
|
}
|