@agentlayer.tech/wallet 0.1.30 → 0.1.32

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.
Files changed (39) hide show
  1. package/.openclaw/extensions/agent-wallet/README.md +1 -2
  2. package/.openclaw/extensions/agent-wallet/dist/index.js +6 -340
  3. package/.openclaw/extensions/agent-wallet/index.ts +6 -340
  4. package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +0 -43
  5. package/.openclaw/extensions/agent-wallet/package.json +1 -1
  6. package/.openclaw/extensions/agent-wallet/skills/wallet-operator/SKILL.md +1 -3
  7. package/CHANGELOG.md +35 -0
  8. package/README.md +0 -5
  9. package/agent-wallet/.env.example +0 -12
  10. package/agent-wallet/README.md +0 -35
  11. package/agent-wallet/agent_wallet/btc_user_wallets.py +32 -1
  12. package/agent-wallet/agent_wallet/config.py +11 -7
  13. package/agent-wallet/agent_wallet/evm_user_wallets.py +2 -0
  14. package/agent-wallet/agent_wallet/openclaw_adapter.py +1 -655
  15. package/agent-wallet/agent_wallet/openclaw_cli.py +0 -7
  16. package/agent-wallet/agent_wallet/providers/evm_portfolio.py +18 -42
  17. package/agent-wallet/agent_wallet/providers/jupiter.py +1 -307
  18. package/agent-wallet/agent_wallet/providers/wdk_btc_local.py +31 -3
  19. package/agent-wallet/agent_wallet/providers/wdk_evm_local.py +37 -3
  20. package/agent-wallet/agent_wallet/transaction_policy.py +0 -262
  21. package/agent-wallet/agent_wallet/wallet_layer/base.py +0 -100
  22. package/agent-wallet/agent_wallet/wallet_layer/solana.py +1 -1118
  23. package/agent-wallet/openclaw.plugin.json +0 -4
  24. package/agent-wallet/pyproject.toml +1 -1
  25. package/agent-wallet/scripts/install_agent_wallet.py +113 -6
  26. package/agent-wallet/scripts/install_openclaw_local_config.py +7 -5
  27. package/agent-wallet/skills/wallet-operator/SKILL.md +1 -5
  28. package/bin/openclaw-agent-wallet.mjs +103 -36
  29. package/claude-code/plugins/agent-wallet/scripts/run_mcp.sh +7 -2
  30. package/codex/plugins/agent-wallet/server.py +2 -118
  31. package/hermes/plugins/agent_wallet/tools.py +1 -1
  32. package/package.json +1 -1
  33. package/wdk-btc-wallet/src/local_vault.js +45 -68
  34. package/wdk-btc-wallet/src/server.js +1 -0
  35. package/wdk-evm-wallet/README.md +4 -3
  36. package/wdk-evm-wallet/src/config.js +15 -0
  37. package/wdk-evm-wallet/src/local_vault.js +45 -68
  38. package/wdk-evm-wallet/src/server.js +1 -0
  39. 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 || "BTC 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-btc-wallet:${walletId}:v${VAULT_VERSION}`, "utf8"));
96
89
  decipher.setAuthTag(tag);
97
90
  const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
98
- return plaintext.toString("utf8");
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 LocalBtcVault {
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 LocalBtcVault {
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: true,
141
+ unlocked: false,
146
142
  unlockExpiresAt: null,
147
143
  ...(revealSeedPhrase ? { seedPhrase } : {}),
148
144
  };
@@ -160,39 +156,35 @@ export class LocalBtcVault {
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: true,
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
- const unlocked = this._unlocked.get(wallet.walletId);
176
- return {
177
- ...wallet,
178
- unlocked: Boolean(unlocked),
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: Boolean(unlocked),
191
- unlockExpiresAt: unlocked ? unlocked.expiresAt : null,
179
+ unlocked: false,
180
+ unlockExpiresAt: null,
192
181
  };
193
182
  }
194
183
 
195
- async unlockWallet({ walletId, password, timeoutSeconds }) {
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 LocalBtcVault {
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: true,
219
- unlockExpiresAt: expiresAt,
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 LocalBtcVault {
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: Boolean(this._unlocked.get(id)),
300
- unlockExpiresAt: this._unlocked.get(id)?.expiresAt ?? null,
276
+ unlocked: false,
277
+ unlockExpiresAt: null,
301
278
  };
302
279
  }
303
280
 
304
- async resolveSeedPhrase({ walletId, seedPhrase }) {
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 LocalBtcVault {
313
292
  };
314
293
  }
315
294
  const id = assertNonEmptyString(walletId, "walletId");
316
- this.#sweepExpiredUnlocked();
317
- const unlocked = this._unlocked.get(id);
318
- if (!unlocked) {
319
- throw new Error("Wallet is locked. Unlock it first or provide seedPhrase explicitly.");
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: unlocked.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
 
@@ -420,13 +406,4 @@ export class LocalBtcVault {
420
406
  #registryPath() {
421
407
  return path.join(this.config.dataDir, REGISTRY_FILE);
422
408
  }
423
-
424
- #sweepExpiredUnlocked() {
425
- const now = Date.now();
426
- for (const [walletId, state] of this._unlocked.entries()) {
427
- if (state.expiresAt && Date.parse(state.expiresAt) <= now) {
428
- this._unlocked.delete(walletId);
429
- }
430
- }
431
- }
432
409
  }
@@ -44,6 +44,7 @@ async function withResolvedSeed(body = {}) {
44
44
  const resolved = await vault.resolveSeedPhrase({
45
45
  walletId: body.walletId,
46
46
  seedPhrase: body.seedPhrase,
47
+ password: body.password,
47
48
  });
48
49
  return {
49
50
  ...body,
@@ -165,9 +165,10 @@ Gateway mode:
165
165
  - `PROVIDER_GATEWAY_URL` defaults to `https://agent-layer-production.up.railway.app`
166
166
  - set `PROVIDER_GATEWAY_URL=https://...` only when overriding the hosted default
167
167
  - `PROVIDER_GATEWAY_BEARER_TOKEN` is optional and only needed when the gateway is protected
168
- - optionally set `WDK_EVM_RPC_GATEWAY_PROVIDER=alchemy|shared|auto`
169
- - in gateway mode, `ethereum` and `base` use the provider gateway raw EVM RPC route
170
- - explicit `WDK_EVM_<NETWORK>_RPC_URL` values still override gateway mode per network
168
+ - `ethereum` and `base` mainnet are always routed through the provider gateway raw EVM RPC route
169
+ - `ethereum` and `base` mainnet are pinned to the gateway `provider=alchemy` path
170
+ - direct `WDK_EVM_ETHEREUM_RPC_URL` and `WDK_EVM_BASE_RPC_URL` values no longer override mainnet routing
171
+ - `WDK_EVM_SEPOLIA_RPC_URL` and `WDK_EVM_BASE_SEPOLIA_RPC_URL` remain direct per-network testnet overrides
171
172
 
172
173
  Local security note:
173
174
 
@@ -10,6 +10,7 @@ const DEFAULTS = {
10
10
  unlockTimeoutSeconds: 0,
11
11
  };
12
12
  const DEFAULT_PROVIDER_GATEWAY_URL = "https://agent-layer-production.up.railway.app";
13
+ const ENFORCED_GATEWAY_MAINNETS = new Set(["ethereum", "base"]);
13
14
 
14
15
  const DEFAULT_NETWORK_PROFILES = {
15
16
  ethereum: {
@@ -191,6 +192,20 @@ export function loadConfig(env = process.env) {
191
192
 
192
193
  function resolveProviderUrl(networkKey, envValue, fallbackUrl) {
193
194
  const direct = String(envValue ?? "").trim();
195
+ if (ENFORCED_GATEWAY_MAINNETS.has(networkKey)) {
196
+ const enforcedGatewayUrl = buildGatewayEvmRpcUrl(
197
+ providerGatewayUrl,
198
+ networkKey,
199
+ "alchemy",
200
+ providerGatewayToken
201
+ );
202
+ if (!enforcedGatewayUrl) {
203
+ throw new Error(
204
+ `PROVIDER_GATEWAY_URL is required for ${networkKey} mainnet RPC routing.`
205
+ );
206
+ }
207
+ return enforcedGatewayUrl;
208
+ }
194
209
  if (direct) {
195
210
  return direct;
196
211
  }
@@ -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
- return plaintext.toString("utf8");
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: true,
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: true,
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
- const unlocked = this._unlocked.get(wallet.walletId);
176
- return {
177
- ...wallet,
178
- unlocked: Boolean(unlocked),
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: Boolean(unlocked),
191
- unlockExpiresAt: unlocked ? unlocked.expiresAt : null,
179
+ unlocked: false,
180
+ unlockExpiresAt: null,
192
181
  };
193
182
  }
194
183
 
195
- async unlockWallet({ walletId, password, timeoutSeconds }) {
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: true,
219
- unlockExpiresAt: expiresAt,
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: Boolean(this._unlocked.get(id)),
300
- unlockExpiresAt: this._unlocked.get(id)?.expiresAt ?? null,
276
+ unlocked: false,
277
+ unlockExpiresAt: null,
301
278
  };
302
279
  }
303
280
 
304
- async resolveSeedPhrase({ walletId, seedPhrase }) {
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
- this.#sweepExpiredUnlocked();
317
- const unlocked = this._unlocked.get(id);
318
- if (!unlocked) {
319
- throw new Error("Wallet is locked. Unlock it first or provide seedPhrase explicitly.");
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: unlocked.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
  }
@@ -227,6 +227,7 @@ async function withResolvedSeed(body = {}) {
227
227
  const resolved = await vault.resolveSeedPhrase({
228
228
  walletId: body.walletId,
229
229
  seedPhrase: body.seedPhrase,
230
+ password: body.password,
230
231
  });
231
232
  return {
232
233
  ...body,