@trustwallet/cli 0.22.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4282,7 +4282,8 @@ var init_dist = __esm({
4282
4282
  PASSWORD_MISSING: "PASSWORD_MISSING",
4283
4283
  WALLET_NOT_FOUND: "WALLET_NOT_FOUND",
4284
4284
  WALLET_AUTH_FAILED: "WALLET_AUTH_FAILED",
4285
- STORAGE_ERROR: "STORAGE_ERROR"
4285
+ STORAGE_ERROR: "STORAGE_ERROR",
4286
+ CREDENTIALS_MISSING: "CREDENTIALS_MISSING"
4286
4287
  };
4287
4288
  EXECUTION_REVERTED_RE = /\bexecution reverted\b/i;
4288
4289
  FS_ERRNO_CODES = /* @__PURE__ */ new Set([
@@ -4452,6 +4453,7 @@ var init_dist = __esm({
4452
4453
  DEFAULT_X_ORIGIN = "Trust/2.15.0 AgentKit";
4453
4454
  TwakHttpClient = class {
4454
4455
  config;
4456
+ resolveCredentials;
4455
4457
  constructor(config) {
4456
4458
  this.config = {
4457
4459
  gatewayBaseUrl: DEFAULT_GATEWAY_URL,
@@ -4461,17 +4463,18 @@ var init_dist = __esm({
4461
4463
  xOrigin: DEFAULT_X_ORIGIN,
4462
4464
  ...config
4463
4465
  };
4466
+ if ("credentials" in config) {
4467
+ this.resolveCredentials = config.credentials;
4468
+ } else {
4469
+ const fixed = { accessId: config.accessId, hmacSecret: config.hmacSecret };
4470
+ this.resolveCredentials = () => fixed;
4471
+ }
4464
4472
  }
4465
4473
  async gateway(method, path7, body, query) {
4466
4474
  const url = new URL(path7, this.config.gatewayBaseUrl);
4467
4475
  if (query) url.search = query;
4468
- const headers = signRequest(
4469
- method,
4470
- path7,
4471
- query || "",
4472
- this.config.accessId,
4473
- this.config.hmacSecret
4474
- );
4476
+ const { accessId, hmacSecret } = this.resolveCredentials();
4477
+ const headers = signRequest(method, path7, query || "", accessId, hmacSecret);
4475
4478
  return this.fetch(url.toString(), {
4476
4479
  method,
4477
4480
  headers: {
@@ -4547,10 +4550,10 @@ var init_dist = __esm({
4547
4550
  }
4548
4551
  }
4549
4552
  get accessId() {
4550
- return this.config.accessId;
4553
+ return this.resolveCredentials().accessId;
4551
4554
  }
4552
4555
  get hmacSecret() {
4553
- return this.config.hmacSecret;
4556
+ return this.resolveCredentials().hmacSecret;
4554
4557
  }
4555
4558
  };
4556
4559
  ALLOWED_OFFRAMP_PROVIDERS = /* @__PURE__ */ new Set(["MoonPay"]);
@@ -6506,7 +6509,7 @@ var require_source = __commonJS({
6506
6509
  var dist_exports2 = {};
6507
6510
  __export(dist_exports2, {
6508
6511
  AgentWalletProvider: () => AgentWalletProvider,
6509
- BlockchainClient: () => BlockchainClient,
6512
+ ChainClient: () => ChainClient,
6510
6513
  TeeWalletProvider: () => TeeWalletProvider,
6511
6514
  VerifyCredentialsError: () => VerifyCredentialsError,
6512
6515
  WalletContext: () => WalletContext,
@@ -7366,7 +7369,7 @@ function registerShutdownHandlers(providers, onBeforeExit) {
7366
7369
  process.removeListener("SIGTERM", handler);
7367
7370
  };
7368
7371
  }
7369
- var fs2, path2, import_trust_sdk, import_trust_sdk2, import_trust_sdk3, import_trust_sdk4, import_trust_sdk5, import_crypto2, __require, polyfillsApplied, _credentials, _naasSessionId, nodeStubsPatched, sponsorNonceByAddress, NAAS_SESSION_RE, naasSessionPatched, coinTypeByValue, walletCore, initialized, BSC_COIN_ID, DEFAULT_BSC_SPONSORED_RPC, SPONSOR_PRECHECK_TIMEOUT_MS, SPONSOR_USER_AGENT, RPC_TIMEOUT_MS, RECEIPT_POLL_INTERVAL_MS, RECEIPT_DEFAULT_TIMEOUT_MS, BlockchainClient, WalletCoreSigner, domainsByCoinId, GATEWAY_URL, DEVICE_INFO_BODY, VerifyCredentialsError, WALLET_ID_SUFFIX, WALLET_ID_LENGTH, EVM_COIN_IDS, MAINTAINED_COIN_IDS, WalletProvider, AgentWalletProvider, TeeWalletProvider, WalletContext, SHUTDOWN_TIMEOUT_MS;
7372
+ var fs2, path2, import_trust_sdk, import_trust_sdk2, import_trust_sdk3, import_trust_sdk4, import_trust_sdk5, import_crypto2, __require, polyfillsApplied, _credentials, _naasSessionId, nodeStubsPatched, sponsorNonceByAddress, NAAS_SESSION_RE, naasSessionPatched, coinTypeByValue, walletCore, initialized, BSC_COIN_ID, DEFAULT_BSC_SPONSORED_RPC, SPONSOR_PRECHECK_TIMEOUT_MS, SPONSOR_USER_AGENT, RPC_TIMEOUT_MS, RECEIPT_POLL_INTERVAL_MS, RECEIPT_DEFAULT_TIMEOUT_MS, ChainClient, SigningChainClient, WalletCoreSigner, domainsByCoinId, GATEWAY_URL, DEVICE_INFO_BODY, VerifyCredentialsError, WALLET_ID_SUFFIX, WALLET_ID_LENGTH, EVM_COIN_IDS, MAINTAINED_COIN_IDS, WalletProvider, AgentWalletProvider, TeeWalletProvider, WalletContext, SHUTDOWN_TIMEOUT_MS;
7370
7373
  var init_dist3 = __esm({
7371
7374
  "../wallet-core/dist/index.mjs"() {
7372
7375
  "use strict";
@@ -7409,18 +7412,20 @@ var init_dist3 = __esm({
7409
7412
  RPC_TIMEOUT_MS = 15e3;
7410
7413
  RECEIPT_POLL_INTERVAL_MS = 1500;
7411
7414
  RECEIPT_DEFAULT_TIMEOUT_MS = 6e4;
7412
- BlockchainClient = class _BlockchainClient {
7413
- signer;
7414
- constructor(signer) {
7415
- this.signer = signer;
7416
- }
7417
- setSigner(signer) {
7418
- this.signer = signer;
7419
- }
7415
+ ChainClient = class {
7416
+ /** Balance of `address`, read through an account built from that address alone. */
7420
7417
  async getBalance(assetId, address, chainKey) {
7418
+ const { coinId } = parseTokenId(assetId);
7419
+ return this.loadBalance(
7420
+ assetId,
7421
+ this.createAccountForAddress(coinId, address, chainKey),
7422
+ address
7423
+ );
7424
+ }
7425
+ /** `address` feeds the Bitcoin REST fallback when the SDK load fails. */
7426
+ async loadBalance(assetId, account, address) {
7421
7427
  const { coinId, contractAddress } = parseTokenId(assetId);
7422
7428
  const ct5 = requireCoinType(coinId);
7423
- const account = this.createAccountForAddress(coinId, address, chainKey);
7424
7429
  const asset = contractAddress ? new import_trust_sdk2.Asset.Token(account, contractAddress, tokenTypeForCoinId(coinId)) : new import_trust_sdk2.Asset.Coin(account);
7425
7430
  let results;
7426
7431
  try {
@@ -7513,47 +7518,6 @@ var init_dist3 = __esm({
7513
7518
  }
7514
7519
  return result;
7515
7520
  }
7516
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
7517
- async transfer(params) {
7518
- const { coinId, contractAddress } = parseTokenId(params.assetId);
7519
- const chainKey = params.chainKey;
7520
- const ct5 = requireCoinType(coinId);
7521
- const account = this.getAccountFromSigner(chainKey);
7522
- const asset = contractAddress ? new import_trust_sdk2.Asset.Token(account, contractAddress, tokenTypeForCoinId(coinId)) : new import_trust_sdk2.Asset.Coin(account);
7523
- let decimals;
7524
- if (params.decimals !== void 0) {
7525
- decimals = params.decimals;
7526
- } else if (contractAddress) {
7527
- const known = lookupKnownTokenDecimals(chainKey, contractAddress);
7528
- if (known === void 0) {
7529
- throw new TwakValidationError(
7530
- `Cannot determine decimals for ${params.assetId}; pass decimals explicitly.`
7531
- );
7532
- }
7533
- decimals = known;
7534
- } else {
7535
- decimals = getCoinDecimals(coinId);
7536
- }
7537
- const amountWei = humanAmountToWei(params.amount, decimals);
7538
- const transaction = new import_trust_sdk2.TransactionTransfer(
7539
- params.to,
7540
- asset,
7541
- amountWei.toString(),
7542
- false,
7543
- params.memo || null
7544
- );
7545
- return this.signAndBroadcast(transaction, account, coinId, chainKey, ct5, params.hdWallet);
7546
- }
7547
- async executeContract(params) {
7548
- const { coinId } = parseTokenId(params.assetId);
7549
- const chainKey = params.chainKey;
7550
- const ct5 = requireCoinType(coinId);
7551
- const account = this.getAccountFromSigner(chainKey);
7552
- const asset = new import_trust_sdk2.Asset.Coin(account);
7553
- const valueWei = params.value && params.value !== "0x0" && params.value !== "0x" ? BigInt(params.value).toString() : "0";
7554
- const transaction = new import_trust_sdk2.TransactionSmartContract(asset, params.to, valueWei, params.data);
7555
- return this.signAndBroadcast(transaction, account, coinId, chainKey, ct5, params.hdWallet);
7556
- }
7557
7521
  /**
7558
7522
  * Execute a read-only EVM `eth_call` against the chain's default RPC node.
7559
7523
  * Returns the raw hex result string from the JSON-RPC envelope, or `'0x'`
@@ -7617,6 +7581,177 @@ var init_dist3 = __esm({
7617
7581
  contract(def) {
7618
7582
  return def.using(this);
7619
7583
  }
7584
+ async findTransactionStatus(chainKey, address, hash) {
7585
+ const coinId = chainKeyToCoinType(chainKey);
7586
+ if (coinId === void 0) throw new Error(`Unsupported chain: ${chainKey}`);
7587
+ const account = this.createAccountForAddress(coinId, address);
7588
+ let result;
7589
+ try {
7590
+ result = await import_trust_sdk2.BlockchainService.findTransaction(account, hash);
7591
+ } catch {
7592
+ return { confirmed: false, pending: false, failed: false, error: "Transaction not found" };
7593
+ }
7594
+ if (!result) {
7595
+ return { confirmed: false, pending: false, failed: false, error: "Transaction not found" };
7596
+ }
7597
+ const failed = result.status instanceof import_trust_sdk2.TransactionResult.Status.Failed;
7598
+ return {
7599
+ confirmed: result.status === import_trust_sdk2.TransactionResult.Status.Confirmed,
7600
+ pending: result.status === import_trust_sdk2.TransactionResult.Status.Pending,
7601
+ failed,
7602
+ error: failed ? result.status.error : void 0,
7603
+ meta: result.meta || void 0
7604
+ };
7605
+ }
7606
+ async getNodes(chainKey) {
7607
+ const coinId = chainKeyToCoinType(chainKey);
7608
+ if (coinId === void 0) {
7609
+ throw new Error(`Unsupported chain: ${chainKey}`);
7610
+ }
7611
+ const ct5 = requireCoinType(coinId);
7612
+ const chain2 = new import_trust_sdk2.DefinedChain(ct5);
7613
+ const nodes = await import_trust_sdk2.BlockchainService.getNodes(chain2);
7614
+ return nodes.map((n4) => ({ url: n4.url }));
7615
+ }
7616
+ async setNode(chainKey, rpcUrl) {
7617
+ const coinId = chainKeyToCoinType(chainKey);
7618
+ if (coinId === void 0) {
7619
+ throw new Error(`Unsupported chain: ${chainKey}`);
7620
+ }
7621
+ const ct5 = requireCoinType(coinId);
7622
+ const chain2 = new import_trust_sdk2.DefinedChain(ct5);
7623
+ await import_trust_sdk2.BlockchainService.setNode(chain2, new import_trust_sdk2.Node(rpcUrl));
7624
+ }
7625
+ createAccountForAddress(coinId, address, chainKey) {
7626
+ const ct5 = requireCoinType(coinId);
7627
+ const chain2 = chainKey !== void 0 && isTestnetChain(chainKey) ? chainKeyToDefinedChain(chainKey).chain : new import_trust_sdk2.DefinedChain(ct5);
7628
+ const wc3 = getWalletCore();
7629
+ const derivation = coinId === 0 && address.startsWith("bc1p") ? wc3.Derivation.bitcoinTaproot : coinId === 0 && address.startsWith("bc1") ? wc3.Derivation.bitcoinSegwit : wc3.Derivation.default;
7630
+ return new import_trust_sdk2.Account(address, derivation, "", "", "", chain2);
7631
+ }
7632
+ async fetchBitcoinBalanceFromBlockbook(address) {
7633
+ const res = await fetch(
7634
+ `https://btc-blockbook.twnodes.com/api/v2/address/${encodeURIComponent(address)}?details=basic`
7635
+ );
7636
+ if (!res.ok) throw new Error(`Blockbook error: ${res.status}`);
7637
+ const data = await res.json();
7638
+ const confirmed = data.balance || "0";
7639
+ const unconfirmed = data.unconfirmedBalance || "0";
7640
+ const total = BigInt(confirmed) + BigInt(unconfirmed);
7641
+ return { available: confirmed, total: total.toString() };
7642
+ }
7643
+ async resolveRpcUrl(chainKey) {
7644
+ const { chain: chain2 } = chainKeyToDefinedChain(chainKey);
7645
+ return this.rpcUrlForChain(chain2, chainKey);
7646
+ }
7647
+ async rpcUrlForChain(chain2, chainKey) {
7648
+ const nodes = await import_trust_sdk2.BlockchainService.getNodes(chain2);
7649
+ const rpcUrl = nodes[0]?.url;
7650
+ if (!rpcUrl) {
7651
+ throw new TwakNetworkError(`No RPC node available for ${chainKey}`, { chainKey });
7652
+ }
7653
+ return rpcUrl;
7654
+ }
7655
+ async postJsonRpc(rpcUrl, method, params, opts) {
7656
+ const timeoutMs = opts?.timeoutMs ?? RPC_TIMEOUT_MS;
7657
+ let response;
7658
+ try {
7659
+ response = await fetch(rpcUrl, {
7660
+ method: "POST",
7661
+ headers: { "Content-Type": "application/json", ...opts?.headers },
7662
+ body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
7663
+ signal: AbortSignal.timeout(timeoutMs)
7664
+ });
7665
+ } catch (err) {
7666
+ throw new TwakNetworkError(`RPC fetch failed on ${method}: ${errorMessage(err)}`, {
7667
+ method,
7668
+ rpcUrl,
7669
+ timeoutMs
7670
+ });
7671
+ }
7672
+ if (!response.ok) {
7673
+ const body = await response.text().catch(() => "");
7674
+ const truncated = body.slice(0, 500);
7675
+ throw new TwakNetworkError(`RPC HTTP error ${response.status} on ${method}: ${truncated}`, {
7676
+ status: response.status,
7677
+ method,
7678
+ rpcUrl,
7679
+ body: truncated
7680
+ });
7681
+ }
7682
+ let json;
7683
+ try {
7684
+ json = await response.json();
7685
+ } catch (err) {
7686
+ throw new TwakNetworkError(
7687
+ `RPC response JSON parse failed on ${method}: ${errorMessage(err)}`,
7688
+ { method, rpcUrl }
7689
+ );
7690
+ }
7691
+ if (json.error) {
7692
+ const { message, data } = json.error;
7693
+ const suffix = revertDataSuffix(message, data);
7694
+ throw new TwakNetworkError(`RPC error on ${method}: ${message}${suffix}`, {
7695
+ method,
7696
+ rpcUrl,
7697
+ rpcError: json.error
7698
+ });
7699
+ }
7700
+ return json.result;
7701
+ }
7702
+ };
7703
+ SigningChainClient = class _SigningChainClient extends ChainClient {
7704
+ constructor(signer) {
7705
+ super();
7706
+ this.signer = signer;
7707
+ }
7708
+ /** This wallet's own balance, read through its derived account (for UTXO
7709
+ * chains such as Bitcoin it carries the extended public key). */
7710
+ async getOwnBalance(assetId, chainKey) {
7711
+ const account = this.getAccountFromSigner(chainKey);
7712
+ return this.loadBalance(assetId, account, account.address);
7713
+ }
7714
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7715
+ async transfer(params) {
7716
+ const { coinId, contractAddress } = parseTokenId(params.assetId);
7717
+ const chainKey = params.chainKey;
7718
+ const ct5 = requireCoinType(coinId);
7719
+ const account = this.getAccountFromSigner(chainKey);
7720
+ const asset = contractAddress ? new import_trust_sdk2.Asset.Token(account, contractAddress, tokenTypeForCoinId(coinId)) : new import_trust_sdk2.Asset.Coin(account);
7721
+ let decimals;
7722
+ if (params.decimals !== void 0) {
7723
+ decimals = params.decimals;
7724
+ } else if (contractAddress) {
7725
+ const known = lookupKnownTokenDecimals(chainKey, contractAddress);
7726
+ if (known === void 0) {
7727
+ throw new TwakValidationError(
7728
+ `Cannot determine decimals for ${params.assetId}; pass decimals explicitly.`
7729
+ );
7730
+ }
7731
+ decimals = known;
7732
+ } else {
7733
+ decimals = getCoinDecimals(coinId);
7734
+ }
7735
+ const amountWei = humanAmountToWei(params.amount, decimals);
7736
+ const transaction = new import_trust_sdk2.TransactionTransfer(
7737
+ params.to,
7738
+ asset,
7739
+ amountWei.toString(),
7740
+ false,
7741
+ params.memo || null
7742
+ );
7743
+ return this.signAndBroadcast(transaction, account, coinId, chainKey, ct5, params.hdWallet);
7744
+ }
7745
+ async executeContract(params) {
7746
+ const { coinId } = parseTokenId(params.assetId);
7747
+ const chainKey = params.chainKey;
7748
+ const ct5 = requireCoinType(coinId);
7749
+ const account = this.getAccountFromSigner(chainKey);
7750
+ const asset = new import_trust_sdk2.Asset.Coin(account);
7751
+ const valueWei = params.value && params.value !== "0x0" && params.value !== "0x" ? BigInt(params.value).toString() : "0";
7752
+ const transaction = new import_trust_sdk2.TransactionSmartContract(asset, params.to, valueWei, params.data);
7753
+ return this.signAndBroadcast(transaction, account, coinId, chainKey, ct5, params.hdWallet);
7754
+ }
7620
7755
  async transferRawSolana(params) {
7621
7756
  const ct5 = requireCoinType(501);
7622
7757
  const chain2 = new import_trust_sdk2.DefinedChain(ct5);
@@ -7702,47 +7837,6 @@ var init_dist3 = __esm({
7702
7837
  currency: chainKey
7703
7838
  };
7704
7839
  }
7705
- async findTransactionStatus(chainKey, address, hash) {
7706
- const coinId = chainKeyToCoinType(chainKey);
7707
- if (coinId === void 0) throw new Error(`Unsupported chain: ${chainKey}`);
7708
- const account = this.createAccountForAddress(coinId, address);
7709
- let result;
7710
- try {
7711
- result = await import_trust_sdk2.BlockchainService.findTransaction(account, hash);
7712
- } catch {
7713
- return { confirmed: false, pending: false, failed: false, error: "Transaction not found" };
7714
- }
7715
- if (!result) {
7716
- return { confirmed: false, pending: false, failed: false, error: "Transaction not found" };
7717
- }
7718
- const failed = result.status instanceof import_trust_sdk2.TransactionResult.Status.Failed;
7719
- return {
7720
- confirmed: result.status === import_trust_sdk2.TransactionResult.Status.Confirmed,
7721
- pending: result.status === import_trust_sdk2.TransactionResult.Status.Pending,
7722
- failed,
7723
- error: failed ? result.status.error : void 0,
7724
- meta: result.meta || void 0
7725
- };
7726
- }
7727
- async getNodes(chainKey) {
7728
- const coinId = chainKeyToCoinType(chainKey);
7729
- if (coinId === void 0) {
7730
- throw new Error(`Unsupported chain: ${chainKey}`);
7731
- }
7732
- const ct5 = requireCoinType(coinId);
7733
- const chain2 = new import_trust_sdk2.DefinedChain(ct5);
7734
- const nodes = await import_trust_sdk2.BlockchainService.getNodes(chain2);
7735
- return nodes.map((n4) => ({ url: n4.url }));
7736
- }
7737
- async setNode(chainKey, rpcUrl) {
7738
- const coinId = chainKeyToCoinType(chainKey);
7739
- if (coinId === void 0) {
7740
- throw new Error(`Unsupported chain: ${chainKey}`);
7741
- }
7742
- const ct5 = requireCoinType(coinId);
7743
- const chain2 = new import_trust_sdk2.DefinedChain(ct5);
7744
- await import_trust_sdk2.BlockchainService.setNode(chain2, new import_trust_sdk2.Node(rpcUrl));
7745
- }
7746
7840
  // EIP-7702 sponsorship-eligible chain coin IDs
7747
7841
  static SPONSORABLE_COINS = [60, 20000714];
7748
7842
  /**
@@ -7902,7 +7996,7 @@ var init_dist3 = __esm({
7902
7996
  } catch {
7903
7997
  }
7904
7998
  const seen = new Set(candidates.map((a3) => a3.toLowerCase()));
7905
- for (const addr of _BlockchainClient.FALLBACK_GAS_TOKENS[coinId] || []) {
7999
+ for (const addr of _SigningChainClient.FALLBACK_GAS_TOKENS[coinId] || []) {
7906
8000
  if (!seen.has(addr.toLowerCase())) candidates.push(addr);
7907
8001
  }
7908
8002
  if (candidates.length === 0) return void 0;
@@ -7929,7 +8023,7 @@ var init_dist3 = __esm({
7929
8023
  * Trust SDK routes through the Smart Wallet fee service internally.
7930
8024
  */
7931
8025
  async calculateFeeWithSponsorship(transaction, account, coinId, chainKey) {
7932
- if (!_BlockchainClient.SPONSORABLE_COINS.includes(coinId) || chainKey !== void 0 && isTestnetChain(chainKey)) {
8026
+ if (!_SigningChainClient.SPONSORABLE_COINS.includes(coinId) || chainKey !== void 0 && isTestnetChain(chainKey)) {
7933
8027
  return { fee: await import_trust_sdk2.BlockchainService.calculateFee(transaction) };
7934
8028
  }
7935
8029
  let supports7702 = false;
@@ -8060,97 +8154,8 @@ var init_dist3 = __esm({
8060
8154
  return { fee: baseFee, transaction7702: tx7702 };
8061
8155
  }
8062
8156
  getAccountFromSigner(chainKey) {
8063
- if (!this.signer) {
8064
- throw new Error("BlockchainClient requires a signer for this operation");
8065
- }
8066
8157
  return this.signer.getAccount(chainKey);
8067
8158
  }
8068
- createAccountForAddress(coinId, address, chainKey) {
8069
- if (this.signer) {
8070
- const key = chainKey ?? coinTypeToChainKey(coinId);
8071
- if (key) {
8072
- try {
8073
- return this.signer.getAccount(key);
8074
- } catch {
8075
- }
8076
- }
8077
- }
8078
- const ct5 = requireCoinType(coinId);
8079
- const chain2 = chainKey !== void 0 && isTestnetChain(chainKey) ? chainKeyToDefinedChain(chainKey).chain : new import_trust_sdk2.DefinedChain(ct5);
8080
- const wc3 = getWalletCore();
8081
- const derivation = coinId === 0 && address.startsWith("bc1p") ? wc3.Derivation.bitcoinTaproot : coinId === 0 && address.startsWith("bc1") ? wc3.Derivation.bitcoinSegwit : wc3.Derivation.default;
8082
- return new import_trust_sdk2.Account(address, derivation, "", "", "", chain2);
8083
- }
8084
- async fetchBitcoinBalanceFromBlockbook(address) {
8085
- const res = await fetch(
8086
- `https://btc-blockbook.twnodes.com/api/v2/address/${encodeURIComponent(address)}?details=basic`
8087
- );
8088
- if (!res.ok) throw new Error(`Blockbook error: ${res.status}`);
8089
- const data = await res.json();
8090
- const confirmed = data.balance || "0";
8091
- const unconfirmed = data.unconfirmedBalance || "0";
8092
- const total = BigInt(confirmed) + BigInt(unconfirmed);
8093
- return { available: confirmed, total: total.toString() };
8094
- }
8095
- async resolveRpcUrl(chainKey) {
8096
- const { chain: chain2 } = chainKeyToDefinedChain(chainKey);
8097
- return this.rpcUrlForChain(chain2, chainKey);
8098
- }
8099
- async rpcUrlForChain(chain2, chainKey) {
8100
- const nodes = await import_trust_sdk2.BlockchainService.getNodes(chain2);
8101
- const rpcUrl = nodes[0]?.url;
8102
- if (!rpcUrl) {
8103
- throw new TwakNetworkError(`No RPC node available for ${chainKey}`, { chainKey });
8104
- }
8105
- return rpcUrl;
8106
- }
8107
- async postJsonRpc(rpcUrl, method, params, opts) {
8108
- const timeoutMs = opts?.timeoutMs ?? RPC_TIMEOUT_MS;
8109
- let response;
8110
- try {
8111
- response = await fetch(rpcUrl, {
8112
- method: "POST",
8113
- headers: { "Content-Type": "application/json", ...opts?.headers },
8114
- body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
8115
- signal: AbortSignal.timeout(timeoutMs)
8116
- });
8117
- } catch (err) {
8118
- throw new TwakNetworkError(`RPC fetch failed on ${method}: ${errorMessage(err)}`, {
8119
- method,
8120
- rpcUrl,
8121
- timeoutMs
8122
- });
8123
- }
8124
- if (!response.ok) {
8125
- const body = await response.text().catch(() => "");
8126
- const truncated = body.slice(0, 500);
8127
- throw new TwakNetworkError(`RPC HTTP error ${response.status} on ${method}: ${truncated}`, {
8128
- status: response.status,
8129
- method,
8130
- rpcUrl,
8131
- body: truncated
8132
- });
8133
- }
8134
- let json;
8135
- try {
8136
- json = await response.json();
8137
- } catch (err) {
8138
- throw new TwakNetworkError(
8139
- `RPC response JSON parse failed on ${method}: ${errorMessage(err)}`,
8140
- { method, rpcUrl }
8141
- );
8142
- }
8143
- if (json.error) {
8144
- const { message, data } = json.error;
8145
- const suffix = revertDataSuffix(message, data);
8146
- throw new TwakNetworkError(`RPC error on ${method}: ${message}${suffix}`, {
8147
- method,
8148
- rpcUrl,
8149
- rpcError: json.error
8150
- });
8151
- }
8152
- return json.result;
8153
- }
8154
8159
  };
8155
8160
  WalletCoreSigner = class _WalletCoreSigner {
8156
8161
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -8299,6 +8304,7 @@ var init_dist3 = __esm({
8299
8304
  };
8300
8305
  AgentWalletProvider = class _AgentWalletProvider extends WalletProvider {
8301
8306
  name = "agent";
8307
+ kind = "local";
8302
8308
  signer;
8303
8309
  blockchainClient;
8304
8310
  connected = true;
@@ -8355,7 +8361,7 @@ var init_dist3 = __esm({
8355
8361
  const store = new WalletConfigStore();
8356
8362
  const mnemonic = await store.loadMnemonic(password3);
8357
8363
  this.signer = await WalletCoreSigner.fromMnemonic(mnemonic);
8358
- this.blockchainClient = new BlockchainClient(this.signer);
8364
+ this.blockchainClient = new SigningChainClient(this.signer);
8359
8365
  this.connected = true;
8360
8366
  })();
8361
8367
  try {
@@ -8371,21 +8377,21 @@ var init_dist3 = __esm({
8371
8377
  const store = new WalletConfigStore();
8372
8378
  await store.saveMnemonic(mnemonic, password3, Object.keys(CHAINS));
8373
8379
  const signer = WalletCoreSigner.fromHDWallet(hdWallet);
8374
- const client = new BlockchainClient(signer);
8380
+ const client = new SigningChainClient(signer);
8375
8381
  return new _AgentWalletProvider(signer, client);
8376
8382
  }
8377
8383
  static async fromStorage(password3) {
8378
8384
  const store = new WalletConfigStore();
8379
8385
  const mnemonic = await store.loadMnemonic(password3);
8380
8386
  const signer = await WalletCoreSigner.fromMnemonic(mnemonic);
8381
- const client = new BlockchainClient(signer);
8387
+ const client = new SigningChainClient(signer);
8382
8388
  return new _AgentWalletProvider(signer, client);
8383
8389
  }
8384
8390
  static async fromMnemonic(mnemonic, password3) {
8385
8391
  const store = new WalletConfigStore();
8386
8392
  await store.saveMnemonic(mnemonic, password3, Object.keys(CHAINS));
8387
8393
  const signer = await WalletCoreSigner.fromMnemonic(mnemonic);
8388
- const client = new BlockchainClient(signer);
8394
+ const client = new SigningChainClient(signer);
8389
8395
  return new _AgentWalletProvider(signer, client);
8390
8396
  }
8391
8397
  async getAddress(chainId) {
@@ -8473,6 +8479,20 @@ var init_dist3 = __esm({
8473
8479
  });
8474
8480
  return result.hash;
8475
8481
  }
8482
+ async transfer(request) {
8483
+ await this.ensureConnected();
8484
+ this.resetAutoLockTimer();
8485
+ const { hash, chainKey } = await this.blockchainClient.transfer({
8486
+ ...request,
8487
+ hdWallet: this.signer.getHDWallet()
8488
+ });
8489
+ return { hash, chainKey };
8490
+ }
8491
+ async getBalance(assetId, chainKey) {
8492
+ await this.ensureConnected();
8493
+ this.resetAutoLockTimer();
8494
+ return this.blockchainClient.getOwnBalance(assetId, chainKey);
8495
+ }
8476
8496
  supportsChain(chainId) {
8477
8497
  return isChainSupported(chainId);
8478
8498
  }
@@ -8500,19 +8520,10 @@ var init_dist3 = __esm({
8500
8520
  isConnected() {
8501
8521
  return this.connected;
8502
8522
  }
8503
- getBlockchainClient() {
8504
- if (!this.connected)
8505
- throw new Error("Wallet is locked. Use an async method to trigger re-auth.");
8506
- return this.blockchainClient;
8507
- }
8508
- getSigner() {
8509
- if (!this.connected)
8510
- throw new Error("Wallet is locked. Use an async method to trigger re-auth.");
8511
- return this.signer;
8512
- }
8513
8523
  };
8514
8524
  TeeWalletProvider = class extends WalletProvider {
8515
8525
  name = "tee";
8526
+ kind = "remote";
8516
8527
  address = null;
8517
8528
  async getAddress(_chainId) {
8518
8529
  if (!this.address) throw new Error("TEE wallet not initialized");
@@ -8531,6 +8542,12 @@ var init_dist3 = __esm({
8531
8542
  async sendTransaction(_params) {
8532
8543
  throw new Error("TEE transaction sending not yet implemented");
8533
8544
  }
8545
+ async transfer() {
8546
+ throw new Error("TEE transfers not yet implemented");
8547
+ }
8548
+ async getBalance() {
8549
+ throw new Error("TEE balances not yet implemented");
8550
+ }
8534
8551
  supportsChain(_chainId) {
8535
8552
  return false;
8536
8553
  }
@@ -25724,6 +25741,17 @@ var import_chalk2 = __toESM(require_source());
25724
25741
  init_dist();
25725
25742
  init_dist3();
25726
25743
  init_dist2();
25744
+
25745
+ // src/credentials-error.ts
25746
+ init_dist();
25747
+ function credentialsMissingError() {
25748
+ return new CommandError(
25749
+ "CREDENTIALS_MISSING",
25750
+ "No API credentials found. Run `twak init` or `twak setup` (or set TWAK_ACCESS_ID and TWAK_HMAC_SECRET env vars)."
25751
+ );
25752
+ }
25753
+
25754
+ // src/config-loader.ts
25727
25755
  var EMBEDDED_NAAS_SESSION_ID = "NDVjMWRjY2MtYTYwNy00NGNhLWIxZGUtMmJiZDIyNmEyODQ4";
25728
25756
  function resolveNaasSessionId() {
25729
25757
  if (process.env.TWAK_NAAS_SESSION_ID) return process.env.TWAK_NAAS_SESSION_ID;
@@ -25731,45 +25759,41 @@ function resolveNaasSessionId() {
25731
25759
  return EMBEDDED_NAAS_SESSION_ID;
25732
25760
  return void 0;
25733
25761
  }
25734
- async function getCredentials() {
25762
+ async function loadCredentials() {
25735
25763
  const envAccessId = process.env.TWAK_ACCESS_ID;
25736
25764
  const envSecret = process.env.TWAK_HMAC_SECRET;
25737
25765
  if (envAccessId && envSecret) {
25738
25766
  return { accessId: envAccessId, hmacSecret: envSecret };
25739
25767
  }
25740
- const credManager = new CredentialManager();
25741
- const credentials = await credManager.load();
25768
+ const credentials = await new CredentialManager().load();
25742
25769
  if (credentials) {
25743
25770
  return { accessId: credentials.accessId, hmacSecret: credentials.hmacSecret };
25744
25771
  }
25745
- throw new CommandError(
25746
- "VALIDATION_ERROR",
25747
- "No API credentials found. Run `twak setup` (or set TWAK_ACCESS_ID and TWAK_HMAC_SECRET env vars)."
25748
- );
25772
+ return null;
25773
+ }
25774
+ async function getCredentials() {
25775
+ const credentials = await loadCredentials();
25776
+ if (credentials) return credentials;
25777
+ throw credentialsMissingError();
25749
25778
  }
25750
25779
  async function getClient() {
25751
25780
  const creds = await getCredentials();
25752
25781
  return createTwakClient({ ...creds });
25753
25782
  }
25754
25783
  async function tryGetClient() {
25755
- const envAccessId = process.env.TWAK_ACCESS_ID;
25756
- const envSecret = process.env.TWAK_HMAC_SECRET;
25757
- if (envAccessId && envSecret) {
25758
- return createTwakClient({ accessId: envAccessId, hmacSecret: envSecret });
25759
- }
25760
- const credentials = await new CredentialManager().load();
25761
- if (credentials) {
25762
- return createTwakClient({ accessId: credentials.accessId, hmacSecret: credentials.hmacSecret });
25763
- }
25764
- return void 0;
25784
+ const credentials = await loadCredentials();
25785
+ return credentials ? createTwakClient(credentials) : void 0;
25765
25786
  }
25766
- async function ensureTrustSDK() {
25767
- const creds = await getCredentials();
25787
+ async function initTrustSDKWithCredentials(creds) {
25768
25788
  await initTrustSDK({
25769
25789
  accessId: creds.accessId,
25770
25790
  hmacSecret: creds.hmacSecret,
25771
25791
  naasSessionId: resolveNaasSessionId()
25772
25792
  });
25793
+ }
25794
+ async function ensureTrustSDK() {
25795
+ const creds = await getCredentials();
25796
+ await initTrustSDKWithCredentials(creds);
25773
25797
  return creds;
25774
25798
  }
25775
25799
  async function registerWalletWithBackend(creds, provider) {
@@ -41752,6 +41776,7 @@ async function settleWithin(promise, ms3) {
41752
41776
  var WC_NOT_CONNECTED_MSG = 'WalletConnect session not established. If pairing is in progress, complete the approval in Trust Wallet mobile and retry; otherwise call switch_wallet_mode({mode: "walletconnect"}) to start pairing.';
41753
41777
  var WalletConnectProvider = class extends WalletProvider {
41754
41778
  name = "walletconnect";
41779
+ kind = "remote";
41755
41780
  client = null;
41756
41781
  initPromise = null;
41757
41782
  currentSession = null;
@@ -41903,6 +41928,43 @@ var WalletConnectProvider = class extends WalletProvider {
41903
41928
  }
41904
41929
  });
41905
41930
  }
41931
+ /**
41932
+ * EVM only: the phone signs and broadcasts. Native coins go out as a value
41933
+ * transfer, tokens as an ERC-20 `transfer` call built by the chain client.
41934
+ */
41935
+ async transfer(request) {
41936
+ const { chainKey } = request;
41937
+ const chain2 = requireChain(chainKey);
41938
+ if (chain2.namespace !== "eip155") {
41939
+ throw new TransferError(
41940
+ "CHAIN_UNSUPPORTED",
41941
+ `transfer on ${chainKey} is not supported in WalletConnect mode (EVM-only). Switch to local wallet mode for ${chainKey} transfers.`
41942
+ );
41943
+ }
41944
+ if (request.decimals === void 0) {
41945
+ throw new TransferError("VALIDATION_ERROR", `Decimals are required for ${request.assetId}`);
41946
+ }
41947
+ const atomicAmount = BigInt(parseAmount(request.amount, request.decimals));
41948
+ const { contractAddress } = parseTokenId(request.assetId);
41949
+ if (contractAddress) {
41950
+ assertEvmAddress(contractAddress, "tokenAddress");
41951
+ const { erc20: erc202 } = await Promise.resolve().then(() => (init_dist4(), dist_exports3));
41952
+ const pending = await this.config.chainClient.contract(erc202).withAddress(contractAddress, chainKey).write.transfer([request.to, atomicAmount], this);
41953
+ return { hash: pending.hash, chainKey };
41954
+ }
41955
+ assertEvmAddress(request.to, "to");
41956
+ const hash = await this.sendTransaction({
41957
+ chainId: `${chain2.namespace}:${chain2.chainId}`,
41958
+ to: request.to,
41959
+ data: "0x",
41960
+ value: "0x" + atomicAmount.toString(16)
41961
+ });
41962
+ return { hash, chainKey };
41963
+ }
41964
+ async getBalance(assetId, chainKey) {
41965
+ const address = await this.getAddress(chainKey);
41966
+ return this.config.chainClient.getBalance(assetId, address, chainKey);
41967
+ }
41906
41968
  async sendTransaction(params) {
41907
41969
  const session = this.getActiveSession();
41908
41970
  if (!session) throw new WalletError("WC_NOT_CONNECTED", WC_NOT_CONNECTED_MSG);
@@ -43186,9 +43248,6 @@ function mapBroadcastError(err) {
43186
43248
  }
43187
43249
  throw err;
43188
43250
  }
43189
- function hasLocalSigner(wallet) {
43190
- return typeof wallet.getSigner === "function";
43191
- }
43192
43251
  var TransferExecutor = class {
43193
43252
  constructor(deps) {
43194
43253
  this.deps = deps;
@@ -43207,7 +43266,7 @@ var TransferExecutor = class {
43207
43266
  }
43208
43267
  }
43209
43268
  async run(params) {
43210
- const { wallet, swapClient, blockchainClient, onInfo, onWarn } = this.deps;
43269
+ const { wallet, swapClient, onInfo, onWarn } = this.deps;
43211
43270
  const chainKey = resolveChain(params.chain);
43212
43271
  const contractAddress = params.tokenAddress && params.tokenAddress.toLowerCase() !== NATIVE_TOKEN_ADDRESS.toLowerCase() ? params.tokenAddress : void 0;
43213
43272
  const { assetId } = resolveAsset(chainKey, contractAddress);
@@ -43245,7 +43304,7 @@ var TransferExecutor = class {
43245
43304
  isNative: !contractAddress,
43246
43305
  explicitDecimals: params.decimals ?? null
43247
43306
  });
43248
- const atomicAmount = parseAmount(params.amount, decimals);
43307
+ parseAmount(params.amount, decimals);
43249
43308
  if (!params.safety?.skip) {
43250
43309
  const maxUsd = params.safety?.maxUsd ?? DEFAULT_MAX_TRANSFER_USD;
43251
43310
  const warnThreshold = params.safety?.warnThresholdUsd ?? DEFAULT_WARN_THRESHOLD_USD;
@@ -43271,80 +43330,30 @@ var TransferExecutor = class {
43271
43330
  );
43272
43331
  }
43273
43332
  }
43274
- let hash;
43275
- let resultChainKey = chainKey;
43276
- if (hasLocalSigner(wallet)) {
43277
- if (!blockchainClient) {
43278
- throw new TransferError(
43279
- "WALLET_NOT_FOUND",
43280
- "BlockchainClient not available \u2014 local transfer requires a configured blockchain client"
43281
- );
43282
- }
43283
- const from8 = await wallet.getAddress(chainKey);
43284
- onInfo?.(`Sending from ${from8} on ${chainKey}...`);
43285
- const hdWallet = wallet.getSigner().getHDWallet();
43286
- try {
43287
- const result = await blockchainClient.transfer({
43288
- assetId,
43289
- to: resolvedTo,
43290
- amount: params.amount,
43291
- decimals,
43292
- memo: params.memo,
43293
- hdWallet,
43294
- chainKey
43295
- });
43296
- hash = result.hash;
43297
- resultChainKey = result.chainKey;
43298
- } catch (err) {
43299
- mapBroadcastError(err);
43300
- }
43301
- const explorerUrl2 = getExplorerTxUrl(resultChainKey, hash);
43302
- return {
43303
- success: true,
43304
- hash,
43305
- chainKey: resultChainKey,
43333
+ const local = wallet.kind === "local";
43334
+ const from8 = local ? await wallet.getAddress(chainKey) : "";
43335
+ if (local) onInfo?.(`Sending from ${from8} on ${chainKey}...`);
43336
+ let receipt;
43337
+ try {
43338
+ receipt = await wallet.transfer({
43339
+ chainKey,
43306
43340
  assetId,
43307
- from: from8,
43308
- to: resolvedTo,
43309
- resolvedFrom,
43310
- explorerUrl: explorerUrl2 || void 0
43311
- };
43312
- }
43313
- const chain2 = requireChain(chainKey);
43314
- if (chain2.namespace !== "eip155") {
43315
- throw new TransferError(
43316
- "CHAIN_UNSUPPORTED",
43317
- `transfer on ${chainKey} is not supported in WalletConnect mode (EVM-only). Switch to local wallet mode for ${chainKey} transfers.`
43318
- );
43319
- }
43320
- const caip2 = `${chain2.namespace}:${chain2.chainId}`;
43321
- if (contractAddress) {
43322
- assertEvmAddress(contractAddress, "tokenAddress");
43323
- if (!blockchainClient) {
43324
- throw new TransferError(
43325
- "WALLET_NOT_FOUND",
43326
- "BlockchainClient not available \u2014 ERC-20 transfer requires a configured blockchain client"
43327
- );
43328
- }
43329
- const { erc20: erc202 } = await Promise.resolve().then(() => (init_dist4(), dist_exports3));
43330
- const pending = await blockchainClient.contract(erc202).withAddress(contractAddress, chainKey).write.transfer([resolvedTo, BigInt(atomicAmount)], wallet);
43331
- hash = pending.hash;
43332
- } else {
43333
- assertEvmAddress(resolvedTo, "to");
43334
- hash = await wallet.sendTransaction({
43335
- chainId: caip2,
43336
43341
  to: resolvedTo,
43337
- data: "0x",
43338
- value: "0x" + BigInt(atomicAmount).toString(16)
43342
+ amount: params.amount,
43343
+ decimals,
43344
+ memo: params.memo
43339
43345
  });
43346
+ } catch (err) {
43347
+ if (local) mapBroadcastError(err);
43348
+ throw err;
43340
43349
  }
43341
- const explorerUrl = getExplorerTxUrl(chainKey, hash);
43350
+ const explorerUrl = getExplorerTxUrl(receipt.chainKey, receipt.hash);
43342
43351
  return {
43343
43352
  success: true,
43344
- hash,
43345
- chainKey,
43353
+ hash: receipt.hash,
43354
+ chainKey: receipt.chainKey,
43346
43355
  assetId,
43347
- from: "",
43356
+ from: from8,
43348
43357
  to: resolvedTo,
43349
43358
  resolvedFrom,
43350
43359
  explorerUrl: explorerUrl || void 0
@@ -43832,9 +43841,10 @@ async function resolveWcProjectId(flag) {
43832
43841
  return DEFAULT_WC_PROJECT_ID;
43833
43842
  }
43834
43843
  }
43835
- function createWalletConnectProvider(projectId) {
43844
+ function createWalletConnectProvider(projectId, chainClient) {
43836
43845
  return new WalletConnectProvider({
43837
43846
  projectId,
43847
+ chainClient,
43838
43848
  storagePath: wcStoragePath(),
43839
43849
  onStorageWarning: (msg) => warn(msg)
43840
43850
  });
@@ -43870,7 +43880,7 @@ function getQRString(address) {
43870
43880
  async function openStoredWalletConnect(projectIdFlag) {
43871
43881
  if (!fs6.existsSync(wcStoragePath())) return null;
43872
43882
  const projectId = await resolveWcProjectId(projectIdFlag);
43873
- const provider = createWalletConnectProvider(projectId);
43883
+ const provider = createWalletConnectProvider(projectId, new ChainClient());
43874
43884
  const session = await withTimeout(provider.restore(), RESTORE_TIMEOUT_MS);
43875
43885
  return { provider, session };
43876
43886
  }
@@ -44178,7 +44188,7 @@ ${qr3}`;
44178
44188
  ).option("--timeout <seconds>", "Connection timeout in seconds", "300").option("--json", "JSON output").action(async (opts) => {
44179
44189
  try {
44180
44190
  const projectId = await resolveWcProjectId(opts.projectId);
44181
- const provider = createWalletConnectProvider(projectId);
44191
+ const provider = createWalletConnectProvider(projectId, new ChainClient());
44182
44192
  const chains = opts.chains ? opts.chains.split(",").map((s2) => s2.trim()).filter(Boolean) : getDefaultWcChains();
44183
44193
  info("Connecting via WalletConnect...");
44184
44194
  const result = await provider.connect(chains);
@@ -44539,7 +44549,7 @@ function classifySwapError(message) {
44539
44549
  return "TX_FAILED";
44540
44550
  }
44541
44551
  var SwapExecutor = class {
44542
- constructor(swapClient, walletProvider, options = {}) {
44552
+ constructor(swapClient, walletProvider, options) {
44543
44553
  this.swapClient = swapClient;
44544
44554
  this.walletProvider = walletProvider;
44545
44555
  this.options = options;
@@ -44675,7 +44685,7 @@ var SwapExecutor = class {
44675
44685
  const fromAssetAddr = route.steps[0].from.asset.address;
44676
44686
  if (!fromAssetAddr || fromAssetAddr.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase())
44677
44687
  return void 0;
44678
- const blockchainClient = this.getBlockchainClient();
44688
+ const blockchainClient = this.options.chainClient;
44679
44689
  const allowance = await blockchainClient.contract(erc20).withAddress(fromAssetAddr, fromChain).read.allowance([address, stepApproval.operatorAddress]);
44680
44690
  const required = BigInt(stepApproval.minAmount || parsedAmount);
44681
44691
  if (allowance >= required) return void 0;
@@ -44684,14 +44694,6 @@ var SwapExecutor = class {
44684
44694
  this.log(`Approval tx: ${getExplorerTxUrl(fromChain, approveResult.hash)}`);
44685
44695
  return approveResult.hash;
44686
44696
  }
44687
- getBlockchainClient() {
44688
- const blockchainClient = this.walletProvider.getBlockchainClient?.() ?? this.options.blockchainClient;
44689
- if (!blockchainClient)
44690
- throw new Error(
44691
- "Wallet provider does not support on-chain reads; cannot check allowance or confirm transactions"
44692
- );
44693
- return blockchainClient;
44694
- }
44695
44697
  async sendTransaction(txData, chainConfig, fromChain) {
44696
44698
  if (chainConfig?.namespace === "solana" && txData.transaction.solanaTx) {
44697
44699
  return this.walletProvider.sendTransaction({
@@ -44710,7 +44712,7 @@ var SwapExecutor = class {
44710
44712
  throw new Error(`No transaction data returned for swap step on ${fromChain}`);
44711
44713
  }
44712
44714
  async pollForConfirmation(chainKey, address, hash, timeoutMs) {
44713
- const blockchainClient = this.getBlockchainClient();
44715
+ const blockchainClient = this.options.chainClient;
44714
44716
  const deadline = Date.now() + timeoutMs;
44715
44717
  while (Date.now() < deadline) {
44716
44718
  await new Promise((r3) => setTimeout(r3, 2e3));
@@ -44847,7 +44849,6 @@ function registerTransferCommand(program3) {
44847
44849
  const executor = new TransferExecutor({
44848
44850
  wallet: provider,
44849
44851
  swapClient,
44850
- blockchainClient: provider.getBlockchainClient(),
44851
44852
  onInfo: info,
44852
44853
  onWarn: warn
44853
44854
  });
@@ -45054,6 +45055,7 @@ function registerSwapCommand(program3) {
45054
45055
  return;
45055
45056
  }
45056
45057
  const executor = new SwapExecutor(swapClient, provider, {
45058
+ chainClient: new ChainClient(),
45057
45059
  onStatus: info
45058
45060
  });
45059
45061
  const result = await executor.execute({
@@ -45154,7 +45156,7 @@ function registerErc20Command(program3) {
45154
45156
  warn("WARNING: Unlimited approval grants the spender access to ALL tokens of this type.");
45155
45157
  }
45156
45158
  info(`Approving ${opts.spender} to spend from ${address} on ${chainKey}...`);
45157
- const client = provider.getBlockchainClient();
45159
+ const client = new ChainClient();
45158
45160
  const pending = await client.contract(erc20).withAddress(contractAddress, chainKey).write.approve([opts.spender, BigInt(amount)], provider);
45159
45161
  const explorerUrl = getExplorerTxUrl(chainKey, pending.hash);
45160
45162
  success("Approval sent!");
@@ -45195,7 +45197,7 @@ function registerErc20Command(program3) {
45195
45197
  );
45196
45198
  const address = await provider.getAddress(chainKey);
45197
45199
  info(`Revoking approval for ${opts.spender} on ${chainKey}...`);
45198
- const client = provider.getBlockchainClient();
45200
+ const client = new ChainClient();
45199
45201
  const pending = await client.contract(erc20).withAddress(contractAddress, chainKey).write.approve([opts.spender, 0n], provider);
45200
45202
  const explorerUrl = getExplorerTxUrl(chainKey, pending.hash);
45201
45203
  success("Approval revoked!");
@@ -45275,7 +45277,7 @@ function registerErc20Command(program3) {
45275
45277
  contractAddress = parsed;
45276
45278
  chainKey = k4;
45277
45279
  }
45278
- const client = new BlockchainClient();
45280
+ const client = new ChainClient();
45279
45281
  const allowance = await client.contract(erc20).withAddress(contractAddress, chainKey).read.allowance([opts.owner, opts.spender]);
45280
45282
  output(
45281
45283
  {
@@ -45329,10 +45331,10 @@ function registerErc8004Command(program3) {
45329
45331
  await ensureTrustSDK();
45330
45332
  const password3 = await resolvePassword(opts);
45331
45333
  const provider = await AgentWalletProvider.fromStorage(password3);
45332
- const ops = new Erc8004Operations(
45333
- resolveErc8004Contract(provider.getBlockchainClient(), opts.chain),
45334
- { signer: provider, onInfo: info }
45335
- );
45334
+ const ops = new Erc8004Operations(resolveErc8004Contract(new ChainClient(), opts.chain), {
45335
+ signer: provider,
45336
+ onInfo: info
45337
+ });
45336
45338
  const result = await ops.register({
45337
45339
  agentURI: opts.uri,
45338
45340
  metadata: parseMetadataPairs(opts.metadata)
@@ -45368,10 +45370,10 @@ function registerErc8004Command(program3) {
45368
45370
  await ensureTrustSDK();
45369
45371
  const password3 = await resolvePassword(opts);
45370
45372
  const provider = await AgentWalletProvider.fromStorage(password3);
45371
- const ops = new Erc8004Operations(
45372
- resolveErc8004Contract(provider.getBlockchainClient(), opts.chain),
45373
- { signer: provider, onInfo: info }
45374
- );
45373
+ const ops = new Erc8004Operations(resolveErc8004Contract(new ChainClient(), opts.chain), {
45374
+ signer: provider,
45375
+ onInfo: info
45376
+ });
45375
45377
  const result = await ops.setUri({ agentId, agentURI: opts.uri });
45376
45378
  success("agentURI updated!");
45377
45379
  output(
@@ -45399,10 +45401,10 @@ function registerErc8004Command(program3) {
45399
45401
  await ensureTrustSDK();
45400
45402
  const password3 = await resolvePassword(opts);
45401
45403
  const provider = await AgentWalletProvider.fromStorage(password3);
45402
- const ops = new Erc8004Operations(
45403
- resolveErc8004Contract(provider.getBlockchainClient(), opts.chain),
45404
- { signer: provider, onInfo: info }
45405
- );
45404
+ const ops = new Erc8004Operations(resolveErc8004Contract(new ChainClient(), opts.chain), {
45405
+ signer: provider,
45406
+ onInfo: info
45407
+ });
45406
45408
  const result = await ops.setMetadata({ agentId, key: opts.key, value: opts.value });
45407
45409
  success("Metadata set!");
45408
45410
  output(
@@ -45429,9 +45431,7 @@ function registerErc8004Command(program3) {
45429
45431
  try {
45430
45432
  parseAgentId(agentId);
45431
45433
  await ensureTrustSDK();
45432
- const ops = new Erc8004Operations(
45433
- resolveErc8004Contract(new BlockchainClient(), opts.chain)
45434
- );
45434
+ const ops = new Erc8004Operations(resolveErc8004Contract(new ChainClient(), opts.chain));
45435
45435
  const result = await ops.getMetadata({ agentId, key: opts.key });
45436
45436
  output(
45437
45437
  { agentId, key: result.key, value: result.value, chain: result.chainKey },
@@ -45450,9 +45450,7 @@ function registerErc8004Command(program3) {
45450
45450
  try {
45451
45451
  parseAgentId(agentId);
45452
45452
  await ensureTrustSDK();
45453
- const ops = new Erc8004Operations(
45454
- resolveErc8004Contract(new BlockchainClient(), opts.chain)
45455
- );
45453
+ const ops = new Erc8004Operations(resolveErc8004Contract(new ChainClient(), opts.chain));
45456
45454
  const result = await ops.show({ agentId });
45457
45455
  output(
45458
45456
  {
@@ -45494,7 +45492,7 @@ function registerErc8183Command(program3) {
45494
45492
  await ensureTrustSDK();
45495
45493
  const password3 = await resolvePassword(opts);
45496
45494
  const provider = await AgentWalletProvider.fromStorage(password3);
45497
- const ops = buildOps(provider.getBlockchainClient(), opts.chain, provider);
45495
+ const ops = buildOps(new ChainClient(), opts.chain, provider);
45498
45496
  const result = await ops.createJob({
45499
45497
  provider: opts.provider,
45500
45498
  evaluator: opts.evaluator,
@@ -45525,7 +45523,7 @@ function registerErc8183Command(program3) {
45525
45523
  await ensureTrustSDK();
45526
45524
  const password3 = await resolvePassword(opts);
45527
45525
  const provider = await AgentWalletProvider.fromStorage(password3);
45528
- const ops = buildOps(provider.getBlockchainClient(), opts.chain, provider);
45526
+ const ops = buildOps(new ChainClient(), opts.chain, provider);
45529
45527
  const result = await ops.setProvider({
45530
45528
  jobId,
45531
45529
  provider: opts.provider,
@@ -45555,7 +45553,7 @@ function registerErc8183Command(program3) {
45555
45553
  await ensureTrustSDK();
45556
45554
  const password3 = await resolvePassword(opts);
45557
45555
  const provider = await AgentWalletProvider.fromStorage(password3);
45558
- const ops = buildOps(provider.getBlockchainClient(), opts.chain, provider);
45556
+ const ops = buildOps(new ChainClient(), opts.chain, provider);
45559
45557
  const result = await ops.setBudget({
45560
45558
  jobId,
45561
45559
  amount: opts.amount,
@@ -45587,7 +45585,7 @@ function registerErc8183Command(program3) {
45587
45585
  await ensureTrustSDK();
45588
45586
  const password3 = await resolvePassword(opts);
45589
45587
  const provider = await AgentWalletProvider.fromStorage(password3);
45590
- const ops = buildOps(provider.getBlockchainClient(), opts.chain, provider);
45588
+ const ops = buildOps(new ChainClient(), opts.chain, provider);
45591
45589
  const result = await ops.fund({
45592
45590
  jobId,
45593
45591
  expectedBudget: opts.expectedBudget,
@@ -45617,7 +45615,7 @@ function registerErc8183Command(program3) {
45617
45615
  await ensureTrustSDK();
45618
45616
  const password3 = await resolvePassword(opts);
45619
45617
  const provider = await AgentWalletProvider.fromStorage(password3);
45620
- const ops = buildOps(provider.getBlockchainClient(), opts.chain, provider);
45618
+ const ops = buildOps(new ChainClient(), opts.chain, provider);
45621
45619
  const result = await ops.submit({
45622
45620
  jobId,
45623
45621
  deliverable: opts.deliverable,
@@ -45646,7 +45644,7 @@ function registerErc8183Command(program3) {
45646
45644
  await ensureTrustSDK();
45647
45645
  const password3 = await resolvePassword(opts);
45648
45646
  const provider = await AgentWalletProvider.fromStorage(password3);
45649
- const ops = buildOps(provider.getBlockchainClient(), opts.chain, provider);
45647
+ const ops = buildOps(new ChainClient(), opts.chain, provider);
45650
45648
  const result = await ops.complete({
45651
45649
  jobId,
45652
45650
  reason: opts.reason,
@@ -45676,7 +45674,7 @@ function registerErc8183Command(program3) {
45676
45674
  await ensureTrustSDK();
45677
45675
  const password3 = await resolvePassword(opts);
45678
45676
  const provider = await AgentWalletProvider.fromStorage(password3);
45679
- const ops = buildOps(provider.getBlockchainClient(), opts.chain, provider);
45677
+ const ops = buildOps(new ChainClient(), opts.chain, provider);
45680
45678
  const result = await ops.reject({
45681
45679
  jobId,
45682
45680
  reason: opts.reason,
@@ -45705,7 +45703,7 @@ function registerErc8183Command(program3) {
45705
45703
  await ensureTrustSDK();
45706
45704
  const password3 = await resolvePassword(opts);
45707
45705
  const provider = await AgentWalletProvider.fromStorage(password3);
45708
- const ops = buildOps(provider.getBlockchainClient(), opts.chain, provider);
45706
+ const ops = buildOps(new ChainClient(), opts.chain, provider);
45709
45707
  const result = await ops.claimRefund({ jobId });
45710
45708
  success("Refund claimed!");
45711
45709
  output(
@@ -45728,7 +45726,7 @@ function registerErc8183Command(program3) {
45728
45726
  try {
45729
45727
  parseUint(jobId, "jobId");
45730
45728
  await ensureTrustSDK();
45731
- const ops = buildOps(new BlockchainClient(), opts.chain);
45729
+ const ops = buildOps(new ChainClient(), opts.chain);
45732
45730
  const result = await ops.status({ jobId });
45733
45731
  output(
45734
45732
  {
@@ -45759,7 +45757,7 @@ function registerErc8183Command(program3) {
45759
45757
  await ensureTrustSDK();
45760
45758
  const password3 = await resolvePassword(opts);
45761
45759
  const provider = await AgentWalletProvider.fromStorage(password3);
45762
- const ops = buildOps(provider.getBlockchainClient(), opts.chain, provider);
45760
+ const ops = buildOps(new ChainClient(), opts.chain, provider);
45763
45761
  const result = await ops.registerJob({ jobId, policy: opts.policy });
45764
45762
  success("Job registered!");
45765
45763
  output(
@@ -45783,7 +45781,7 @@ function registerErc8183Command(program3) {
45783
45781
  await ensureTrustSDK();
45784
45782
  const password3 = await resolvePassword(opts);
45785
45783
  const provider = await AgentWalletProvider.fromStorage(password3);
45786
- const ops = buildOps(provider.getBlockchainClient(), opts.chain, provider);
45784
+ const ops = buildOps(new ChainClient(), opts.chain, provider);
45787
45785
  const result = await ops.settle({ jobId, evidence: opts.evidence });
45788
45786
  success("Job settled!");
45789
45787
  output(
@@ -45806,7 +45804,7 @@ function registerErc8183Command(program3) {
45806
45804
  await ensureTrustSDK();
45807
45805
  const password3 = await resolvePassword(opts);
45808
45806
  const provider = await AgentWalletProvider.fromStorage(password3);
45809
- const ops = buildOps(provider.getBlockchainClient(), opts.chain, provider);
45807
+ const ops = buildOps(new ChainClient(), opts.chain, provider);
45810
45808
  const result = await ops.markExpired({ jobId });
45811
45809
  success("Job marked expired!");
45812
45810
  output(
@@ -45828,7 +45826,7 @@ function registerErc8183Command(program3) {
45828
45826
  try {
45829
45827
  parseUint(jobId, "jobId");
45830
45828
  await ensureTrustSDK();
45831
- const ops = buildOps(new BlockchainClient(), opts.chain);
45829
+ const ops = buildOps(new ChainClient(), opts.chain);
45832
45830
  const result = await ops.policyInfo({ jobId });
45833
45831
  output(
45834
45832
  {
@@ -45857,7 +45855,7 @@ function registerErc8183Command(program3) {
45857
45855
  await ensureTrustSDK();
45858
45856
  const password3 = await resolvePassword(opts);
45859
45857
  const provider = await AgentWalletProvider.fromStorage(password3);
45860
- const ops = buildOps(provider.getBlockchainClient(), opts.chain, provider);
45858
+ const ops = buildOps(new ChainClient(), opts.chain, provider);
45861
45859
  const result = await ops.dispute({ jobId });
45862
45860
  success("Dispute raised!");
45863
45861
  output(
@@ -45880,7 +45878,7 @@ function registerErc8183Command(program3) {
45880
45878
  await ensureTrustSDK();
45881
45879
  const password3 = await resolvePassword(opts);
45882
45880
  const provider = await AgentWalletProvider.fromStorage(password3);
45883
- const ops = buildOps(provider.getBlockchainClient(), opts.chain, provider);
45881
+ const ops = buildOps(new ChainClient(), opts.chain, provider);
45884
45882
  const result = await ops.voteReject({ jobId });
45885
45883
  success("Reject vote cast!");
45886
45884
  output(
@@ -45915,7 +45913,7 @@ function registerCompeteCommand(program3) {
45915
45913
  await ensureTrustSDK();
45916
45914
  const password3 = await resolvePassword(opts);
45917
45915
  const provider = await AgentWalletProvider.fromStorage(password3);
45918
- const { contract, chainKey } = resolveCompetitionContract(provider.getBlockchainClient());
45916
+ const { contract, chainKey } = resolveCompetitionContract(new ChainClient());
45919
45917
  const participant = await provider.getAddress(chainKey);
45920
45918
  const [start, deadline, alreadyRegistered] = await Promise.all([
45921
45919
  contract.read.registrationStart([]),
@@ -45984,7 +45982,7 @@ function registerCompeteCommand(program3) {
45984
45982
  await ensureTrustSDK();
45985
45983
  const password3 = await resolvePassword(opts);
45986
45984
  const provider = await AgentWalletProvider.fromStorage(password3);
45987
- const { contract, chainKey } = resolveCompetitionContract(provider.getBlockchainClient());
45985
+ const { contract, chainKey } = resolveCompetitionContract(new ChainClient());
45988
45986
  const participant = await provider.getAddress(chainKey);
45989
45987
  const [registered, start, deadline] = await Promise.all([
45990
45988
  contract.read.isRegistered([participant]),
@@ -46795,8 +46793,8 @@ function registerQueryCommand(program3) {
46795
46793
  }
46796
46794
  if (address) {
46797
46795
  await ensureTrustSDK();
46798
- const blockchainClient = new BlockchainClient();
46799
- const status = await blockchainClient.findTransactionStatus(opts.chain, address, hash);
46796
+ const chainClient = new ChainClient();
46797
+ const status = await chainClient.findTransactionStatus(opts.chain, address, hash);
46800
46798
  output(
46801
46799
  {
46802
46800
  hash,
@@ -47544,7 +47542,7 @@ async function payAndFetch(args) {
47544
47542
  if (!confirmed) {
47545
47543
  throw new X402Error("PAYMENT_CANCELLED", "Payment cancelled by user");
47546
47544
  }
47547
- const bc2 = new BlockchainClient();
47545
+ const bc2 = new ChainClient();
47548
47546
  const fromAddress = await args.provider.getAddress(chainKey);
47549
47547
  const tokenAddress = getAsset(kind);
47550
47548
  const amount = getAmount(kind);
@@ -48122,11 +48120,10 @@ var BalanceActionProvider = class extends ActionProvider {
48122
48120
  };
48123
48121
  var DEFAULT_MAX_TRANSFER_USD3 = 1e4;
48124
48122
  var TransferActionProvider = class extends ActionProvider {
48125
- constructor(walletContext, swapClient, blockchainClient, maxTransferUsd = DEFAULT_MAX_TRANSFER_USD3) {
48123
+ constructor(walletContext, swapClient, maxTransferUsd = DEFAULT_MAX_TRANSFER_USD3) {
48126
48124
  super();
48127
48125
  this.walletContext = walletContext;
48128
48126
  this.swapClient = swapClient;
48129
- this.blockchainClient = blockchainClient;
48130
48127
  this.maxTransferUsd = maxTransferUsd;
48131
48128
  }
48132
48129
  name = "transfer";
@@ -48155,8 +48152,7 @@ var TransferActionProvider = class extends ActionProvider {
48155
48152
  const wallet = this.walletContext.activeWallet();
48156
48153
  const executor = new TransferExecutor({
48157
48154
  wallet,
48158
- swapClient: this.swapClient,
48159
- blockchainClient: this.blockchainClient
48155
+ swapClient: this.swapClient
48160
48156
  });
48161
48157
  const result = await executor.execute({
48162
48158
  chain: input2.chain,
@@ -48236,8 +48232,11 @@ var SwapActionProvider = class extends ActionProvider {
48236
48232
  message: `Unknown token: ${input2.toToken} on ${toChain}`
48237
48233
  };
48238
48234
  }
48235
+ if (!this.blockchainClient) {
48236
+ throw new WalletError("WALLET_NOT_FOUND", "Chain client not available");
48237
+ }
48239
48238
  const executor = new SwapExecutor(this.swapClient, signer, {
48240
- blockchainClient: this.blockchainClient
48239
+ chainClient: this.blockchainClient
48241
48240
  });
48242
48241
  const result = await executor.execute({
48243
48242
  fromChain,
@@ -48586,9 +48585,8 @@ var AlertActionProvider = class extends ActionProvider {
48586
48585
  }
48587
48586
  };
48588
48587
  var AgentWalletActionProvider = class extends ActionProvider {
48589
- constructor(blockchainClient, walletContext) {
48588
+ constructor(walletContext) {
48590
48589
  super();
48591
- this.blockchainClient = blockchainClient;
48592
48590
  this.walletContext = walletContext;
48593
48591
  }
48594
48592
  name = "agent-wallet";
@@ -48637,10 +48635,7 @@ var AgentWalletActionProvider = class extends ActionProvider {
48637
48635
  const { chainKey, assetId } = resolveAsset(input2.chain);
48638
48636
  const wallet = this.requireWallet();
48639
48637
  const address = await wallet.getAddress(chainKey);
48640
- if (!this.blockchainClient) {
48641
- return { chain: chainKey, address, balance: "BlockchainClient not available" };
48642
- }
48643
- const balance = await this.blockchainClient.getBalance(assetId, address, chainKey);
48638
+ const balance = await wallet.getBalance(assetId, chainKey);
48644
48639
  return { chain: chainKey, address, balance };
48645
48640
  }
48646
48641
  };
@@ -48691,7 +48686,7 @@ var TokenActionProvider = class extends ActionProvider {
48691
48686
  }),
48692
48687
  invoke: async (_ctx, input2) => {
48693
48688
  if (!this.blockchainClient) {
48694
- throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
48689
+ throw new WalletError("WALLET_NOT_FOUND", "Chain client not available");
48695
48690
  }
48696
48691
  const { chainKey, assetId } = resolveAsset(input2.chain, input2.tokenAddress);
48697
48692
  const balance = await this.blockchainClient.getBalance(assetId, input2.address, chainKey);
@@ -48722,7 +48717,7 @@ var TokenActionProvider = class extends ActionProvider {
48722
48717
  assertEvmAddress(input2.tokenAddress, "tokenAddress");
48723
48718
  assertEvmAddress(input2.spender, "spender");
48724
48719
  if (!this.blockchainClient) {
48725
- throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
48720
+ throw new WalletError("WALLET_NOT_FOUND", "Chain client not available");
48726
48721
  }
48727
48722
  const amount = input2.amount === "unlimited" ? "115792089237316195423570985008687907853269984665640564039457584007913129639935" : input2.amount;
48728
48723
  const pending = await this.blockchainClient.contract(erc20).withAddress(input2.tokenAddress, chainKey).write.approve([input2.spender, BigInt(amount)], wallet);
@@ -48752,7 +48747,7 @@ var TokenActionProvider = class extends ActionProvider {
48752
48747
  assertEvmAddress(input2.tokenAddress, "tokenAddress");
48753
48748
  assertEvmAddress(input2.spender, "spender");
48754
48749
  if (!this.blockchainClient) {
48755
- throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
48750
+ throw new WalletError("WALLET_NOT_FOUND", "Chain client not available");
48756
48751
  }
48757
48752
  const pending = await this.blockchainClient.contract(erc20).withAddress(input2.tokenAddress, chainKey).write.approve([input2.spender, 0n], wallet);
48758
48753
  return { hash: pending.hash, chain: chainKey };
@@ -48771,7 +48766,7 @@ var TokenActionProvider = class extends ActionProvider {
48771
48766
  }),
48772
48767
  invoke: async (_ctx, input2) => {
48773
48768
  if (!this.blockchainClient) {
48774
- throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
48769
+ throw new WalletError("WALLET_NOT_FOUND", "Chain client not available");
48775
48770
  }
48776
48771
  const chainKey = resolveChain(input2.chain);
48777
48772
  const allowance = await this.blockchainClient.contract(erc20).withAddress(input2.tokenAddress, chainKey).read.allowance([input2.owner, input2.spender]);
@@ -49230,7 +49225,7 @@ var OnrampActionProvider = class extends ActionProvider {
49230
49225
  if (!this.blockchainClient) {
49231
49226
  throw new TransferError(
49232
49227
  "WALLET_NOT_FOUND",
49233
- "BlockchainClient not available \u2014 off-ramp token payout requires a configured blockchain client"
49228
+ "Chain client not available \u2014 off-ramp token payout requires a configured blockchain client"
49234
49229
  );
49235
49230
  }
49236
49231
  const pending = await this.blockchainClient.contract(erc20).withAddress(input2.tokenAddress, chainKey).write.transfer([input2.depositAddress, BigInt(atomic)], signer);
@@ -49409,8 +49404,11 @@ var AutomateActionProvider = class extends ActionProvider {
49409
49404
  message: `Unknown token: ${record2.toToken} on ${record2.chain}`
49410
49405
  };
49411
49406
  }
49407
+ if (!this.blockchainClient) {
49408
+ throw new WalletError("WALLET_NOT_FOUND", "Chain client not available");
49409
+ }
49412
49410
  const executor = new SwapExecutor(this.swapClient, walletProvider, {
49413
- blockchainClient: this.blockchainClient
49411
+ chainClient: this.blockchainClient
49414
49412
  });
49415
49413
  const result = await executeAutomation(record2, {
49416
49414
  executor,
@@ -49534,10 +49532,21 @@ var WalletModeActionProvider = class extends ActionProvider {
49534
49532
  description = "Switch wallet modes mid-session. The local agent wallet is auto-bound at server startup when configured, so signing actions work out of the box without calling this. Only call switch_wallet_mode when the user explicitly asks to use WalletConnect (or to return to the local wallet afterwards).";
49535
49533
  walletContext;
49536
49534
  agentWallet;
49535
+ /**
49536
+ * `agentWallet` resolves the agent wallet on each switch to local mode,
49537
+ * loading it if needed, so a wallet created or unlocked after construction
49538
+ * is used. It rejects with a typed WalletError (WALLET_NOT_FOUND,
49539
+ * PASSWORD_MISSING, WALLET_AUTH_FAILED), which surfaces as is.
49540
+ */
49537
49541
  constructor(options) {
49538
49542
  super();
49539
49543
  this.walletContext = options.walletContext;
49540
- this.agentWallet = options.agentWallet;
49544
+ this.agentWallet = options.agentWallet ?? (async () => {
49545
+ throw new WalletError(
49546
+ "WALLET_NOT_FOUND",
49547
+ "No local agent wallet is available. Create one with `twak wallet create`."
49548
+ );
49549
+ });
49541
49550
  }
49542
49551
  getActions() {
49543
49552
  return [this.switchWalletMode(), this.getWalletStatus()];
@@ -49571,13 +49580,8 @@ var WalletModeActionProvider = class extends ActionProvider {
49571
49580
  }),
49572
49581
  invoke: async (_ctx, input2) => {
49573
49582
  if (input2.mode === "local") {
49574
- if (!this.agentWallet) {
49575
- throw new WalletError(
49576
- "WALLET_NOT_FOUND",
49577
- "No local agent wallet found. Create one with `twak init` (CLI), then restart the MCP client (e.g. Claude Desktop)."
49578
- );
49579
- }
49580
- await this.walletContext.bindLocal(this.agentWallet);
49583
+ const agentWallet = await this.agentWallet();
49584
+ await this.walletContext.bindLocal(agentWallet);
49581
49585
  return this.snapshotResponse(this.walletContext.snapshot());
49582
49586
  }
49583
49587
  if (!this.walletContext.walletConnectAvailable) {
@@ -49729,7 +49733,7 @@ var Erc8004ActionProvider = class extends ActionProvider {
49729
49733
  }
49730
49734
  ops(chainInput, signer) {
49731
49735
  if (!this.blockchainClient) {
49732
- throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
49736
+ throw new WalletError("WALLET_NOT_FOUND", "Chain client not available");
49733
49737
  }
49734
49738
  return new Erc8004Operations(resolveErc8004Contract(this.blockchainClient, chainInput), {
49735
49739
  signer
@@ -50114,7 +50118,7 @@ var Erc8183ActionProvider = class extends ActionProvider {
50114
50118
  }
50115
50119
  ops(chainInput, signer) {
50116
50120
  if (!this.blockchainClient) {
50117
- throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
50121
+ throw new WalletError("WALLET_NOT_FOUND", "Chain client not available");
50118
50122
  }
50119
50123
  const client = this.blockchainClient;
50120
50124
  return new Erc8183Operations(resolveErc8183Contracts(client, chainInput), {
@@ -50226,7 +50230,7 @@ var CompetitionActionProvider = class extends ActionProvider {
50226
50230
  }
50227
50231
  bind() {
50228
50232
  if (!this.blockchainClient) {
50229
- throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
50233
+ throw new WalletError("WALLET_NOT_FOUND", "Chain client not available");
50230
50234
  }
50231
50235
  return resolveCompetitionContract(this.blockchainClient).contract;
50232
50236
  }
@@ -54871,20 +54875,24 @@ var import_crypto7 = require("crypto");
54871
54875
  init_dist();
54872
54876
  var SERVER_INSTRUCTIONS = `twak signs blockchain operations through one of two wallet modes:
54873
54877
 
54874
- LOCAL (default): an HD wallet auto-bound at server startup. In-process signing, no user-side prompts.
54878
+ LOCAL (default): an HD wallet auto-bound at server startup when no WalletConnect session was resumed. In-process signing, no user-side prompts.
54875
54879
 
54876
- WALLETCONNECT: a remote wallet (e.g. Trust Wallet on phone) paired via WC v2. Each signing action prompts the user on their device. More secure: the private key never leaves the user's device.
54880
+ WALLETCONNECT: a remote wallet (e.g. Trust Wallet on phone) paired via WC v2. Each signing action must be approved on the user's device; Trust Wallet shows the request only once the app is open (no push notification), so tell the user to open it. More secure: the private key never leaves the user's device.
54877
54881
 
54878
54882
  When the user first engages with signing operations in a fresh conversation, briefly mention that they can optionally switch to phone-based signing via \`switch_wallet_mode({mode: "walletconnect"})\` \u2014 many users don't know this is an option. Mention once; don't repeat on every signing call.
54879
54883
 
54880
- To switch mid-session, call \`switch_wallet_mode\`. The new mode applies to subsequent calls; in-flight signing keeps using its captured wallet. Mode persists for the MCP server's lifetime; restarting the MCP client (e.g. Claude Desktop) resets to local.`;
54884
+ To switch mid-session, call \`switch_wallet_mode\`. The new mode applies to subsequent calls; in-flight signing keeps using its captured wallet. A WalletConnect session is stored on disk and resumed when the server restarts, until it expires (about 7 days) or is revoked; choosing local mode revokes it.
54885
+
54886
+ If a tool returns CREDENTIALS_MISSING, the Trust Wallet API credentials are not set up yet: ask the user to run \`twak init\` (or \`twak setup\`) with their access ID and HMAC secret. The tools work as soon as the credentials are saved; no server restart is needed.`;
54881
54887
  var TwakMcpServer = class {
54882
54888
  server;
54883
54889
  registry;
54884
54890
  context;
54885
- constructor(registry, context) {
54891
+ beforeInvoke;
54892
+ constructor(registry, context, options = {}) {
54886
54893
  this.registry = registry;
54887
54894
  this.context = context;
54895
+ this.beforeInvoke = options.beforeInvoke;
54888
54896
  this.server = new Server(
54889
54897
  { name: "twak", version: "0.1.0" },
54890
54898
  { capabilities: { tools: {} }, instructions: SERVER_INSTRUCTIONS }
@@ -54920,6 +54928,7 @@ var TwakMcpServer = class {
54920
54928
  };
54921
54929
  }
54922
54930
  try {
54931
+ await this.beforeInvoke?.();
54923
54932
  const parsed = action.schema.parse(args);
54924
54933
  const result = await action.invoke(this.context, parsed);
54925
54934
  if (result instanceof McpResponse) {
@@ -55124,6 +55133,95 @@ var TwakRestServer = class {
55124
55133
 
55125
55134
  // src/commands/serve.ts
55126
55135
  init_dist();
55136
+
55137
+ // src/serve-bootstrap.ts
55138
+ var ServeBootstrap = class {
55139
+ constructor(deps) {
55140
+ this.deps = deps;
55141
+ }
55142
+ ready;
55143
+ attempt;
55144
+ async run() {
55145
+ const credentials = await this.deps.loadCredentials();
55146
+ if (!credentials) throw credentialsMissingError();
55147
+ await this.deps.initTrustSDK(credentials);
55148
+ await this.deps.onReady();
55149
+ this.ready = { credentials };
55150
+ }
55151
+ async ensureReady() {
55152
+ if (this.ready) return;
55153
+ if (!this.attempt) {
55154
+ this.attempt = this.run().finally(() => {
55155
+ this.attempt = void 0;
55156
+ });
55157
+ }
55158
+ return this.attempt;
55159
+ }
55160
+ /** Credentials once ready; throws CREDENTIALS_MISSING before. */
55161
+ credentials() {
55162
+ if (!this.ready) throw credentialsMissingError();
55163
+ return this.ready.credentials;
55164
+ }
55165
+ };
55166
+
55167
+ // src/agent-wallet-loader.ts
55168
+ init_dist();
55169
+ var AgentWalletLoader = class {
55170
+ constructor(deps) {
55171
+ this.deps = deps;
55172
+ }
55173
+ loaded;
55174
+ attempt;
55175
+ async open() {
55176
+ if (!await this.deps.walletExists()) {
55177
+ throw new WalletError(
55178
+ "WALLET_NOT_FOUND",
55179
+ "No local agent wallet found. Create one with `twak wallet create`, then switch to local mode again."
55180
+ );
55181
+ }
55182
+ const password3 = await this.deps.resolvePassword();
55183
+ if (!password3) {
55184
+ throw new WalletError(
55185
+ "PASSWORD_MISSING",
55186
+ "No wallet password available to the server. Run `twak wallet keychain save`, or start the server with TWAK_WALLET_PASSWORD or --password."
55187
+ );
55188
+ }
55189
+ let wallet;
55190
+ try {
55191
+ wallet = await this.deps.open(password3);
55192
+ } catch (err) {
55193
+ const classified = classifyWalletLoadError(err);
55194
+ if (classified) throw new WalletError(classified.code, classified.message);
55195
+ throw err;
55196
+ }
55197
+ this.deps.onLoaded(wallet);
55198
+ this.loaded = wallet;
55199
+ return wallet;
55200
+ }
55201
+ /** The loaded wallet; never loads. */
55202
+ get current() {
55203
+ return this.loaded;
55204
+ }
55205
+ /** Rejects with WalletError WALLET_NOT_FOUND, PASSWORD_MISSING or
55206
+ * WALLET_AUTH_FAILED, or the underlying error when unclassified. */
55207
+ async load() {
55208
+ if (this.loaded) return this.loaded;
55209
+ if (!this.attempt) {
55210
+ this.attempt = this.open().finally(() => {
55211
+ this.attempt = void 0;
55212
+ });
55213
+ }
55214
+ return this.attempt;
55215
+ }
55216
+ };
55217
+
55218
+ // src/stdio-console.ts
55219
+ var import_node_console = require("console");
55220
+ function routeConsoleToStderr(target = console, stderr = process.stderr) {
55221
+ Object.assign(target, new import_node_console.Console({ stdout: stderr, stderr }));
55222
+ }
55223
+
55224
+ // src/commands/serve.ts
55127
55225
  function registerServeCommand(program3) {
55128
55226
  program3.command("serve").description("Start MCP server (stdio) or REST API server for AI agent connection").option("--rest", "Start REST HTTP server instead of MCP stdio server").option("--port <n>", "Port for REST server", "3000").option(
55129
55227
  "--host <host>",
@@ -55139,32 +55237,21 @@ function registerServeCommand(program3) {
55139
55237
  "Automation poll interval when --watch is set \u2014 e.g. 30s, 5m, 1h (min 5s)",
55140
55238
  "60"
55141
55239
  ).action(async (opts) => {
55142
- await ensureTrustSDK();
55143
- const client = await getClient();
55144
- const swapClient = new SwapClient(client);
55145
- const alertStore = new AlertStore();
55146
- const automationStore = new AutomationStore();
55147
- const blockchainClient = new BlockchainClient();
55148
- let agentWallet;
55149
- try {
55150
- const password3 = opts.password || process.env.TWAK_WALLET_PASSWORD || await new KeychainManager().getWalletPassword();
55151
- if (password3) agentWallet = await AgentWalletProvider.fromStorage(password3);
55152
- } catch {
55153
- }
55154
- if (opts.autoLock && agentWallet) {
55155
- const minutes = parseInt(opts.autoLock, 10);
55156
- if (isNaN(minutes) || minutes <= 0) {
55240
+ if (!opts.rest) routeConsoleToStderr();
55241
+ let autoLockMinutes;
55242
+ if (opts.autoLock) {
55243
+ autoLockMinutes = parseInt(opts.autoLock, 10);
55244
+ if (isNaN(autoLockMinutes) || autoLockMinutes <= 0) {
55157
55245
  error("--auto-lock must be a positive number of minutes");
55158
55246
  process.exit(1);
55159
55247
  }
55160
- agentWallet.setAutoLock({
55161
- autoLockMinutes: minutes,
55162
- passwordResolver: () => resolvePassword(opts)
55163
- });
55164
55248
  }
55249
+ const alertStore = new AlertStore();
55250
+ const automationStore = new AutomationStore();
55251
+ const chainClient = new ChainClient();
55165
55252
  const wcProjectId = await resolveWcProjectId(opts.wcProjectId);
55166
55253
  const walletContext = new WalletContext({
55167
- walletConnectFactory: () => createWalletConnectProvider(wcProjectId)
55254
+ walletConnectFactory: () => createWalletConnectProvider(wcProjectId, chainClient)
55168
55255
  });
55169
55256
  let resumeOutcome = "none";
55170
55257
  if (fs8.existsSync(wcStoragePath())) {
@@ -55182,37 +55269,75 @@ function registerServeCommand(program3) {
55182
55269
  warn(`Could not resume WalletConnect session: ${errorMessage(err)}`);
55183
55270
  }
55184
55271
  }
55185
- if (agentWallet && resumeOutcome === "none") {
55186
- await walletContext.bindLocal(agentWallet);
55187
- blockchainClient.setSigner(agentWallet.getSigner());
55272
+ const walletLoader = new AgentWalletLoader({
55273
+ walletExists: () => new WalletConfigStore().exists(),
55274
+ resolvePassword: async () => opts.password || process.env.TWAK_WALLET_PASSWORD || await new KeychainManager().getWalletPassword() || void 0,
55275
+ open: (password3) => AgentWalletProvider.fromStorage(password3),
55276
+ onLoaded: (agentWallet) => {
55277
+ if (autoLockMinutes) {
55278
+ agentWallet.setAutoLock({
55279
+ autoLockMinutes,
55280
+ passwordResolver: () => resolvePassword(opts)
55281
+ });
55282
+ }
55283
+ }
55284
+ });
55285
+ const bootstrap = new ServeBootstrap({
55286
+ loadCredentials,
55287
+ initTrustSDK: initTrustSDKWithCredentials,
55288
+ onReady: async () => {
55289
+ let agentWallet;
55290
+ try {
55291
+ agentWallet = await walletLoader.load();
55292
+ } catch (err) {
55293
+ if (!(err instanceof CommandError && err.code === "WALLET_NOT_FOUND")) {
55294
+ warn(`Local agent wallet not loaded: ${errorMessage(err)}`);
55295
+ }
55296
+ return;
55297
+ }
55298
+ if (resumeOutcome === "none") await walletContext.bindLocal(agentWallet);
55299
+ }
55300
+ });
55301
+ const client = createTwakClient({ credentials: () => bootstrap.credentials() });
55302
+ const swapClient = new SwapClient(client);
55303
+ if (opts.rest || opts.watch) {
55304
+ await bootstrap.ensureReady();
55305
+ } else {
55306
+ try {
55307
+ await bootstrap.ensureReady();
55308
+ } catch (err) {
55309
+ warn(`${errorMessage(err)} Tool calls report this error until it is resolved.`);
55310
+ }
55188
55311
  }
55189
55312
  const registry = new ActionRegistry();
55190
55313
  registry.register(new BalanceActionProvider());
55191
- registry.register(new WalletModeActionProvider({ walletContext, agentWallet }));
55192
- registry.register(new SwapActionProvider(swapClient, walletContext, blockchainClient));
55314
+ registry.register(
55315
+ new WalletModeActionProvider({ walletContext, agentWallet: () => walletLoader.load() })
55316
+ );
55317
+ registry.register(new SwapActionProvider(swapClient, walletContext, chainClient));
55193
55318
  registry.register(new SecurityActionProvider());
55194
55319
  registry.register(new HistoryActionProvider());
55195
- registry.register(new TransferActionProvider(walletContext, swapClient, blockchainClient));
55320
+ registry.register(new TransferActionProvider(walletContext, swapClient));
55196
55321
  registry.register(new AlertActionProvider(alertStore));
55197
- registry.register(new AgentWalletActionProvider(blockchainClient, walletContext));
55198
- registry.register(new TokenActionProvider(blockchainClient, walletContext));
55322
+ registry.register(new AgentWalletActionProvider(walletContext));
55323
+ registry.register(new TokenActionProvider(chainClient, walletContext));
55199
55324
  registry.register(new MarketActionProvider());
55200
55325
  registry.register(
55201
- new OnrampActionProvider(walletContext, swapClient, void 0, blockchainClient)
55326
+ new OnrampActionProvider(walletContext, swapClient, void 0, chainClient)
55202
55327
  );
55203
55328
  registry.register(
55204
55329
  new AutomateActionProvider(
55205
55330
  automationStore,
55206
55331
  swapClient,
55207
55332
  walletContext,
55208
- blockchainClient,
55333
+ chainClient,
55209
55334
  Boolean(opts.watch)
55210
55335
  )
55211
55336
  );
55212
55337
  registry.register(new X402ActionProvider(walletContext));
55213
- registry.register(new Erc8004ActionProvider(blockchainClient, walletContext));
55214
- registry.register(new Erc8183ActionProvider(blockchainClient, walletContext));
55215
- registry.register(new CompetitionActionProvider(blockchainClient, walletContext));
55338
+ registry.register(new Erc8004ActionProvider(chainClient, walletContext));
55339
+ registry.register(new Erc8183ActionProvider(chainClient, walletContext));
55340
+ registry.register(new CompetitionActionProvider(chainClient, walletContext));
55216
55341
  let watcher;
55217
55342
  if (opts.watch) {
55218
55343
  const intervalSec = parseInterval(opts.watchInterval);
@@ -55226,7 +55351,7 @@ function registerServeCommand(program3) {
55226
55351
  automationStore,
55227
55352
  client,
55228
55353
  getExecutor: () => walletContext.state === "local" ? new SwapExecutor(swapClient, walletContext.activeWallet(), {
55229
- blockchainClient,
55354
+ chainClient,
55230
55355
  onStatus: (msg) => info(msg)
55231
55356
  }) : null,
55232
55357
  log: (level, msg) => {
@@ -55241,7 +55366,7 @@ function registerServeCommand(program3) {
55241
55366
  }
55242
55367
  );
55243
55368
  info(
55244
- `Automation watcher active (every ${intervalSec}s)${agentWallet ? "" : " \u2014 idle until a local wallet is unlocked"}`
55369
+ `Automation watcher active (every ${intervalSec}s)${walletLoader.current ? "" : " \u2014 idle until a local wallet is unlocked"}`
55245
55370
  );
55246
55371
  watcher.start();
55247
55372
  }
@@ -55249,15 +55374,21 @@ function registerServeCommand(program3) {
55249
55374
  const context = {
55250
55375
  httpClient: client
55251
55376
  };
55377
+ const disconnectLoadedWallet = async () => {
55378
+ const agentWallet = walletLoader.current;
55379
+ if (agentWallet?.isConnected()) await agentWallet.disconnect().catch(() => {
55380
+ });
55381
+ };
55252
55382
  if (opts.rest) {
55253
55383
  const restServer = new TwakRestServer(registry, context, {
55254
55384
  port: parseInt(opts.port),
55255
55385
  host: opts.host,
55256
55386
  apiKey: client.hmacSecret
55257
55387
  });
55258
- registerShutdownHandlers([agentWallet], () => {
55388
+ registerShutdownHandlers([], async () => {
55259
55389
  watcher?.stop();
55260
55390
  restServer.stop();
55391
+ await disconnectLoadedWallet();
55261
55392
  });
55262
55393
  try {
55263
55394
  await restServer.start();
@@ -55266,10 +55397,13 @@ function registerServeCommand(program3) {
55266
55397
  process.exit(1);
55267
55398
  }
55268
55399
  } else {
55269
- registerShutdownHandlers([agentWallet], () => {
55400
+ registerShutdownHandlers([], async () => {
55270
55401
  watcher?.stop();
55402
+ await disconnectLoadedWallet();
55403
+ });
55404
+ const mcpServer = new TwakMcpServer(registry, context, {
55405
+ beforeInvoke: () => bootstrap.ensureReady()
55271
55406
  });
55272
- const mcpServer = new TwakMcpServer(registry, context);
55273
55407
  console.error("TW Agent MCP server starting...");
55274
55408
  await mcpServer.start();
55275
55409
  }
@@ -55654,11 +55788,9 @@ ${url}`);
55654
55788
  warn(
55655
55789
  `About to send ${opts.amount} ${opts.asset} from ${address} to third-party off-ramp deposit address ${opts.to}${opts.memo ? ` (memo: ${opts.memo})` : ""}. This address is not under your control \u2014 verify it matches what the provider displayed before continuing.`
55656
55790
  );
55657
- const client = provider.getBlockchainClient();
55658
- const hdWallet = provider.getSigner().getHDWallet();
55659
55791
  let result;
55660
55792
  try {
55661
- result = await client.transfer({
55793
+ result = await provider.transfer({
55662
55794
  assetId: opts.asset,
55663
55795
  to: opts.to,
55664
55796
  amount: opts.amount,
@@ -55666,8 +55798,7 @@ ${url}`);
55666
55798
  memo: opts.memo,
55667
55799
  // Mainnet by construction — onramp has no --chain input and derives
55668
55800
  // chainKey from the asset's coinId via the CHAINS-only resolver.
55669
- chainKey,
55670
- hdWallet
55801
+ chainKey
55671
55802
  });
55672
55803
  } catch (err) {
55673
55804
  mapBroadcastError(err);
@@ -56181,7 +56312,7 @@ var crypto8 = __toESM(require("crypto"));
56181
56312
  // package.json
56182
56313
  var package_default = {
56183
56314
  name: "@trustwallet/cli",
56184
- version: "0.22.0",
56315
+ version: "0.23.0",
56185
56316
  description: "TWAK CLI \u2014 command-line interface for Trust Wallet Agent Kit",
56186
56317
  main: "dist/index.js",
56187
56318
  types: "dist/index.d.ts",
@@ -56395,6 +56526,7 @@ function registerWatchCommand(program3) {
56395
56526
  });
56396
56527
  }
56397
56528
  executor = new SwapExecutor(swapClient, provider, {
56529
+ chainClient: new ChainClient(),
56398
56530
  onStatus: (msg) => {
56399
56531
  info(`[${ts3()}] ${msg}`);
56400
56532
  }