@trustwallet/cli 0.17.0 → 0.19.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
@@ -3134,6 +3134,7 @@ __export(dist_exports, {
3134
3134
  SOLANA_NATIVE_ADDRESS: () => SOLANA_NATIVE_ADDRESS,
3135
3135
  SWAP_ERROR_CODES: () => SWAP_ERROR_CODES,
3136
3136
  SwapError: () => SwapError,
3137
+ TESTNET_CHAINS: () => TESTNET_CHAINS,
3137
3138
  TRANSFER_ERROR_CODES: () => TRANSFER_ERROR_CODES,
3138
3139
  TransferError: () => TransferError,
3139
3140
  TwakApiError: () => TwakApiError,
@@ -3178,6 +3179,7 @@ __export(dist_exports, {
3178
3179
  isEvmAddress: () => isEvmAddress,
3179
3180
  isNativeTokenId: () => isNativeTokenId,
3180
3181
  isRateLimited: () => isRateLimited,
3182
+ isTestnetChain: () => isTestnetChain,
3181
3183
  isZeroAddress: () => isZeroAddress,
3182
3184
  lookupKnownTokenDecimals: () => lookupKnownTokenDecimals,
3183
3185
  parseAmount: () => parseAmount,
@@ -3200,13 +3202,17 @@ __export(dist_exports, {
3200
3202
  });
3201
3203
  function getChainById(chainKey) {
3202
3204
  const lower = chainKey.toLowerCase();
3203
- return CHAINS[lower] ?? Object.values(CHAINS).find((c3) => c3.id === lower);
3205
+ return CHAINS[lower] ?? TESTNET_CHAINS[lower] ?? Object.values(CHAINS).find((c3) => c3.id === lower) ?? Object.values(TESTNET_CHAINS).find((c3) => c3.id === lower);
3204
3206
  }
3205
3207
  function getChainByCoinId(coinId) {
3206
3208
  return Object.values(CHAINS).find((chain2) => chain2.coinId === coinId);
3207
3209
  }
3208
3210
  function getChainByEvmId(evmChainId) {
3209
- return Object.values(CHAINS).find((chain2) => chain2.chainId === evmChainId);
3211
+ return Object.values(CHAINS).find((chain2) => chain2.chainId === evmChainId) ?? Object.values(TESTNET_CHAINS).find((chain2) => chain2.chainId === evmChainId);
3212
+ }
3213
+ function isTestnetChain(chainKey) {
3214
+ if (!chainKey) return false;
3215
+ return Object.hasOwn(TESTNET_CHAINS, chainKey.toLowerCase());
3210
3216
  }
3211
3217
  function getSupportedChains() {
3212
3218
  return Object.values(CHAINS);
@@ -3231,10 +3237,10 @@ function getChainDomainId(chainKey) {
3231
3237
  if (!entry) throw new Error(`Unsupported chain: ${chainKey}`);
3232
3238
  return entry[1].id;
3233
3239
  }
3234
- function getExplorerTxUrl(chainKey, txHash) {
3240
+ function getExplorerTxUrl(chainKey, hash) {
3235
3241
  const chain2 = getChainById(chainKey);
3236
3242
  if (!chain2) return "";
3237
- return `${chain2.explorerUrl}/tx/${txHash}`;
3243
+ return `${chain2.explorerUrl}/tx/${hash}`;
3238
3244
  }
3239
3245
  function getSampleAccount(chainKey) {
3240
3246
  return getChainById(chainKey)?.sampleAccount || "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
@@ -3338,6 +3344,9 @@ function classifyApiError(err) {
3338
3344
  return { code: "NETWORK_ERROR", message: err.message };
3339
3345
  }
3340
3346
  if (err instanceof TwakNetworkError) {
3347
+ if (EXECUTION_REVERTED_RE.test(err.message)) {
3348
+ return { code: "TX_FAILED", message: err.message };
3349
+ }
3341
3350
  return { code: "NETWORK_ERROR", message: err.message };
3342
3351
  }
3343
3352
  if (err instanceof TwakValidationError) {
@@ -3349,6 +3358,9 @@ function classifyApiError(err) {
3349
3358
  }
3350
3359
  return { code: "NETWORK_ERROR", message: err.message };
3351
3360
  }
3361
+ if (err instanceof Error && EXECUTION_REVERTED_RE.test(err.message)) {
3362
+ return { code: "TX_FAILED", message: err.message };
3363
+ }
3352
3364
  return null;
3353
3365
  }
3354
3366
  function classifyStorageError(err) {
@@ -3431,7 +3443,7 @@ function resolveChain(input2) {
3431
3443
  if (colonIdx > 0 && colonIdx < lower.length - 1) {
3432
3444
  const namespace = lower.slice(0, colonIdx);
3433
3445
  const reference = lower.slice(colonIdx + 1);
3434
- for (const [key, chain2] of Object.entries(CHAINS)) {
3446
+ for (const [key, chain2] of Object.entries(RESOLVABLE_CHAINS)) {
3435
3447
  if (chain2.namespace === namespace && chain2.chainId.toLowerCase() === reference) {
3436
3448
  return key;
3437
3449
  }
@@ -3448,10 +3460,10 @@ function resolveChain(input2) {
3448
3460
  throw chainUnsupported(input2);
3449
3461
  }
3450
3462
  }
3451
- if (CHAINS[lower]) return lower;
3463
+ if (RESOLVABLE_CHAINS[lower]) return lower;
3452
3464
  const aliased = ALIASES[lower];
3453
3465
  if (aliased) return aliased;
3454
- for (const [key, chain2] of Object.entries(CHAINS)) {
3466
+ for (const [key, chain2] of Object.entries(RESOLVABLE_CHAINS)) {
3455
3467
  if (chain2.id === lower) return key;
3456
3468
  }
3457
3469
  if (/^0x[0-9a-f]+$/.test(lower)) {
@@ -3464,14 +3476,14 @@ function resolveChain(input2) {
3464
3476
  throw chainUnsupported(input2);
3465
3477
  }
3466
3478
  function lookupEvm(chainId) {
3467
- for (const [key, chain2] of Object.entries(CHAINS)) {
3479
+ for (const [key, chain2] of Object.entries(RESOLVABLE_CHAINS)) {
3468
3480
  if (chain2.namespace === "eip155" && chain2.chainId === chainId) return key;
3469
3481
  }
3470
3482
  return void 0;
3471
3483
  }
3472
3484
  function resolveAsset(chain2, tokenAddress) {
3473
3485
  const chainKey = resolveChain(chain2);
3474
- const coinId = CHAINS[chainKey].coinId;
3486
+ const coinId = RESOLVABLE_CHAINS[chainKey].coinId;
3475
3487
  return { chainKey, coinId, assetId: formatTokenId(coinId, tokenAddress) };
3476
3488
  }
3477
3489
  function chainUnsupported(input2) {
@@ -3723,7 +3735,7 @@ async function fetchBalance(httpClient, assetId, address, currency = "USD") {
3723
3735
  throw new BalanceError("NETWORK_ERROR", "Could not fetch balance. Please try again later.");
3724
3736
  }
3725
3737
  }
3726
- var import_crypto, NATIVE_TOKEN_ADDRESS, SOLANA_NATIVE_ADDRESS, CHAINS, COMMON_TOKENS, NATIVE_SYMBOLS, CELO_NATIVE_ADDRESS, TwakError, TwakApiError, TwakNetworkError, TwakValidationError, CommandError, COMMON_ERROR_CODES, FS_ERRNO_CODES, SWAP_ERROR_CODES, SwapError, TRANSFER_ERROR_CODES, TransferError, WALLET_ERROR_CODES, WalletError, QUERY_ERROR_CODES, QueryError, BALANCE_ERROR_CODES, BalanceError, ALERT_ERROR_CODES, AlertError, ERC20_ERROR_CODES, Erc20Error, WATCH_ERROR_CODES, WatchError, AUTOMATION_ERROR_CODES, AutomationError, X402_ERROR_CODES, X402Error, ALIASES, DECIMAL_AMOUNT_RE, UNSAFE_KEYS, NAMING_API, EVM_ADDRESS_RE, ERC20_TRANSFER_SIG, ZERO_ADDRESS, DEFAULT_GATEWAY_URL, DEFAULT_MARKET_URL, DEFAULT_TIMEOUT, DEFAULT_USER_AGENT, DEFAULT_X_ORIGIN, TwakHttpClient, ALLOWED_OFFRAMP_PROVIDERS, McpResponse;
3738
+ var import_crypto, NATIVE_TOKEN_ADDRESS, SOLANA_NATIVE_ADDRESS, CHAINS, TESTNET_CHAINS, COMMON_TOKENS, NATIVE_SYMBOLS, CELO_NATIVE_ADDRESS, TwakError, TwakApiError, TwakNetworkError, TwakValidationError, CommandError, COMMON_ERROR_CODES, EXECUTION_REVERTED_RE, FS_ERRNO_CODES, SWAP_ERROR_CODES, SwapError, TRANSFER_ERROR_CODES, TransferError, WALLET_ERROR_CODES, WalletError, QUERY_ERROR_CODES, QueryError, BALANCE_ERROR_CODES, BalanceError, ALERT_ERROR_CODES, AlertError, ERC20_ERROR_CODES, Erc20Error, WATCH_ERROR_CODES, WatchError, AUTOMATION_ERROR_CODES, AutomationError, X402_ERROR_CODES, X402Error, RESOLVABLE_CHAINS, ALIASES, DECIMAL_AMOUNT_RE, UNSAFE_KEYS, NAMING_API, EVM_ADDRESS_RE, ERC20_TRANSFER_SIG, ZERO_ADDRESS, DEFAULT_GATEWAY_URL, DEFAULT_MARKET_URL, DEFAULT_TIMEOUT, DEFAULT_USER_AGENT, DEFAULT_X_ORIGIN, TwakHttpClient, ALLOWED_OFFRAMP_PROVIDERS, McpResponse;
3727
3739
  var init_dist = __esm({
3728
3740
  "../core/dist/index.mjs"() {
3729
3741
  "use strict";
@@ -4064,6 +4076,21 @@ var init_dist = __esm({
4064
4076
  sampleAccount: "0x8d1f3c7b5e9a2047c6b3d80f1e4a9258c7d6b3e5f8a0c2d4e6f8a0b2c4d6e8f"
4065
4077
  }
4066
4078
  };
4079
+ TESTNET_CHAINS = {
4080
+ bsctestnet: {
4081
+ id: "smartchain-testnet",
4082
+ name: "BNB Smart Chain Testnet",
4083
+ coinId: 20000714,
4084
+ namespace: "eip155",
4085
+ chainId: "97",
4086
+ symbol: "tBNB",
4087
+ decimals: 18,
4088
+ explorerUrl: "https://testnet.bscscan.com",
4089
+ rpcUrl: "https://bsc-testnet.twnodes.com",
4090
+ isEVM: true,
4091
+ sampleAccount: "0x8894E0a0c962CB723c1976a4421c95949bE2D4E3"
4092
+ }
4093
+ };
4067
4094
  COMMON_TOKENS = {
4068
4095
  ethereum: {
4069
4096
  USDT: { address: "0xdAC17F958D2ee523a2206206994597C13D831ec7", decimals: 6 },
@@ -4209,6 +4236,7 @@ var init_dist = __esm({
4209
4236
  WALLET_AUTH_FAILED: "WALLET_AUTH_FAILED",
4210
4237
  STORAGE_ERROR: "STORAGE_ERROR"
4211
4238
  };
4239
+ EXECUTION_REVERTED_RE = /\bexecution reverted\b/i;
4212
4240
  FS_ERRNO_CODES = /* @__PURE__ */ new Set([
4213
4241
  "EACCES",
4214
4242
  "EEXIST",
@@ -4347,6 +4375,7 @@ var init_dist = __esm({
4347
4375
  this.name = "X402Error";
4348
4376
  }
4349
4377
  };
4378
+ RESOLVABLE_CHAINS = { ...CHAINS, ...TESTNET_CHAINS };
4350
4379
  ALIASES = {
4351
4380
  "binance-smart-chain": "bsc",
4352
4381
  matic: "polygon",
@@ -6750,13 +6779,47 @@ function patchNodeStubs() {
6750
6779
  if (resolvedUrl.includes("gateway.us.trustwallet.com")) {
6751
6780
  resolvedUrl = resolvedUrl.replace("gateway.us.trustwallet.com", "tws.trustwallet.com");
6752
6781
  }
6782
+ let finalInput = input2;
6753
6783
  if (resolvedUrl !== url) {
6754
- const finalInput = typeof input2 === "string" ? resolvedUrl : input2 instanceof URL ? new URL(resolvedUrl) : new Request(resolvedUrl, input2);
6755
- return originalFetch(finalInput, init3);
6784
+ finalInput = typeof input2 === "string" ? resolvedUrl : input2 instanceof URL ? new URL(resolvedUrl) : new Request(resolvedUrl, input2);
6785
+ }
6786
+ const bodyText = typeof init3?.body === "string" ? init3.body : init3?.body instanceof Uint8Array ? new TextDecoder().decode(init3.body) : void 0;
6787
+ if (bodyText && /"method":"eth_(call|estimateGas|sendRawTransaction)"/.test(bodyText)) {
6788
+ return originalFetch(finalInput, init3).then(appendRpcRevertData);
6756
6789
  }
6757
- return originalFetch(input2, init3);
6790
+ return originalFetch(finalInput, init3);
6758
6791
  };
6759
6792
  }
6793
+ function revertDataSuffix(message, data) {
6794
+ return typeof data === "string" && /^0x[0-9a-fA-F]+$/.test(data) && !message.includes(data) ? `: ${data}` : "";
6795
+ }
6796
+ async function appendRpcRevertData(response) {
6797
+ const text = await response.text();
6798
+ let body = text;
6799
+ if (text.includes('"error"') && text.includes('"data":"0x')) {
6800
+ try {
6801
+ const json = JSON.parse(text);
6802
+ const error2 = json.error;
6803
+ if (error2 && typeof error2.message === "string") {
6804
+ const suffix = revertDataSuffix(error2.message, error2.data);
6805
+ if (suffix) {
6806
+ error2.message = `${error2.message}${suffix}`;
6807
+ body = JSON.stringify(json);
6808
+ }
6809
+ }
6810
+ } catch {
6811
+ }
6812
+ }
6813
+ const headers = new Headers(response.headers);
6814
+ if (body !== text) {
6815
+ headers.delete("content-length");
6816
+ }
6817
+ return new Response(body === "" ? null : body, {
6818
+ status: response.status,
6819
+ statusText: response.statusText,
6820
+ headers
6821
+ });
6822
+ }
6760
6823
  function patchNaasSession(sessionId) {
6761
6824
  if (naasSessionPatched) return;
6762
6825
  naasSessionPatched = true;
@@ -6810,19 +6873,40 @@ async function initTrustSDK(config) {
6810
6873
  "com.trustwallet.agent",
6811
6874
  ""
6812
6875
  );
6813
- new import_trust_sdk.TrustSDKConfig.Builder().setContext(new import_trust_sdk.TrustSDKContext(
6814
- clientPlatform,
6815
- authCredentials,
6816
- config.logEnabled ? import_trust_sdk.LogMode.Debug : import_trust_sdk.LogMode.None,
6817
- config.logEnabled ? import_trust_sdk.LogSeverity.Debug : import_trust_sdk.LogSeverity.Error
6818
- )).addFeatureModule().addTrustSDKModule().init();
6876
+ new import_trust_sdk.TrustSDKConfig.Builder().setContext(
6877
+ new import_trust_sdk.TrustSDKContext(
6878
+ clientPlatform,
6879
+ authCredentials,
6880
+ config.logEnabled ? import_trust_sdk.LogMode.Debug : import_trust_sdk.LogMode.None,
6881
+ config.logEnabled ? import_trust_sdk.LogSeverity.Debug : import_trust_sdk.LogSeverity.Error
6882
+ )
6883
+ ).addFeatureModule().addTrustSDKModule().init();
6819
6884
  const nodeSyncMode = naasSessionId ? new import_trust_sdk.NodeSyncMode(import_trust_sdk.NodeSyncMode.NodeSyncModeType.NaaS) : new import_trust_sdk.NodeSyncMode(import_trust_sdk.NodeSyncMode.NodeSyncModeType.Default);
6820
- new import_trust_sdk.WalletKitConfig.Builder().setContext(new import_trust_sdk.WalletKitContext(clientPlatform, nodeSyncMode, void 0, authCredentials, config.logEnabled ? import_trust_sdk.LoggerMode.Debug : import_trust_sdk.LoggerMode.None)).addWalletKitModule().addSwapModule().init();
6885
+ new import_trust_sdk.WalletKitConfig.Builder().setContext(
6886
+ new import_trust_sdk.WalletKitContext(
6887
+ clientPlatform,
6888
+ nodeSyncMode,
6889
+ void 0,
6890
+ authCredentials,
6891
+ config.logEnabled ? import_trust_sdk.LoggerMode.Debug : import_trust_sdk.LoggerMode.None
6892
+ )
6893
+ ).addWalletKitModule().addSwapModule().init();
6821
6894
  walletCore = await import_trust_sdk.WalletCoreKotlin.init();
6822
6895
  import_trust_sdk.FeatureModule.setEnvironment(import_trust_sdk.TwEnvironmentProduction);
6823
6896
  await import_trust_sdk.AmberService.setEnvironment(import_trust_sdk.TwEnvironmentProduction);
6824
6897
  await import_trust_sdk.BlockchainService.useBitcoinV2();
6825
6898
  buildCoinTypeMap();
6899
+ for (const cfg of Object.values(TESTNET_CHAINS)) {
6900
+ try {
6901
+ await import_trust_sdk.BlockchainService.setNode(
6902
+ new import_trust_sdk.CustomChain(getCoinType(cfg.coinId), cfg.chainId),
6903
+ new import_trust_sdk.Node(cfg.rpcUrl)
6904
+ );
6905
+ } catch (err) {
6906
+ process.stderr.write(`twak: could not register testnet node for ${cfg.id}: ${String(err)}
6907
+ `);
6908
+ }
6909
+ }
6826
6910
  initialized = true;
6827
6911
  }
6828
6912
  function getWalletCore() {
@@ -6951,7 +7035,12 @@ function chainKeyToDefinedChain(chainKey) {
6951
7035
  if (!ct5) {
6952
7036
  throw new TwakValidationError(`No CoinType mapping for coinId ${coinId}`, { chainKey, coinId });
6953
7037
  }
6954
- return { chain: new import_trust_sdk3.DefinedChain(ct5), chainConfig: CHAINS[canonicalKey] };
7038
+ const chainConfig = getChainById(canonicalKey);
7039
+ if (!chainConfig) {
7040
+ throw new TwakValidationError(`No chain config for ${canonicalKey}`, { chainKey });
7041
+ }
7042
+ const chain2 = isTestnetChain(canonicalKey) ? new import_trust_sdk3.CustomChain(ct5, chainConfig.chainId) : new import_trust_sdk3.DefinedChain(ct5);
7043
+ return { chain: chain2, chainConfig };
6955
7044
  }
6956
7045
  function humanAmountToWei(amount, decimals = 18) {
6957
7046
  const parts = amount.split(".");
@@ -6960,11 +7049,11 @@ function humanAmountToWei(amount, decimals = 18) {
6960
7049
  const frac = parts[1].padEnd(decimals, "0").slice(0, decimals);
6961
7050
  return whole * 10n ** BigInt(decimals) + BigInt(frac);
6962
7051
  }
6963
- function createPendingTransaction(client, chainKey, txHash) {
7052
+ function createPendingTransaction(client, chainKey, hash) {
6964
7053
  return {
6965
- txHash,
7054
+ hash,
6966
7055
  chainKey,
6967
- wait: (opts) => client.waitForReceipt(chainKey, txHash, opts)
7056
+ wait: (opts) => client.waitForReceipt(chainKey, hash, opts)
6968
7057
  };
6969
7058
  }
6970
7059
  function bscSponsoredBaseUrl() {
@@ -7188,6 +7277,7 @@ var init_dist3 = __esm({
7188
7277
  fs2 = __toESM(require("fs"), 1);
7189
7278
  path2 = __toESM(require("path"), 1);
7190
7279
  import_trust_sdk = require("@trustwallet-trust-sdk/trust-sdk");
7280
+ init_dist();
7191
7281
  import_trust_sdk2 = require("@trustwallet-trust-sdk/trust-sdk");
7192
7282
  init_dist();
7193
7283
  import_trust_sdk3 = require("@trustwallet-trust-sdk/trust-sdk");
@@ -7230,10 +7320,10 @@ var init_dist3 = __esm({
7230
7320
  setSigner(signer) {
7231
7321
  this.signer = signer;
7232
7322
  }
7233
- async getBalance(assetId, address) {
7323
+ async getBalance(assetId, address, chainKey) {
7234
7324
  const { coinId, contractAddress } = parseTokenId(assetId);
7235
7325
  const ct5 = requireCoinType(coinId);
7236
- const account = this.createAccountForAddress(coinId, address);
7326
+ const account = this.createAccountForAddress(coinId, address, chainKey);
7237
7327
  const asset = contractAddress ? new import_trust_sdk2.Asset.Token(account, contractAddress, tokenTypeForCoinId(coinId)) : new import_trust_sdk2.Asset.Coin(account);
7238
7328
  let results;
7239
7329
  try {
@@ -7268,8 +7358,8 @@ var init_dist3 = __esm({
7268
7358
  total: total.toString()
7269
7359
  };
7270
7360
  }
7271
- async getTokenBalance(assetId, ownerAddress) {
7272
- return this.getBalance(assetId, ownerAddress);
7361
+ async getTokenBalance(assetId, ownerAddress, chainKey) {
7362
+ return this.getBalance(assetId, ownerAddress, chainKey);
7273
7363
  }
7274
7364
  async getBalancesBatch(assetIds, address) {
7275
7365
  if (assetIds.length === 0) return /* @__PURE__ */ new Map();
@@ -7329,10 +7419,7 @@ var init_dist3 = __esm({
7329
7419
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
7330
7420
  async transfer(params) {
7331
7421
  const { coinId, contractAddress } = parseTokenId(params.assetId);
7332
- const chainKey = coinTypeToChainKey(coinId);
7333
- if (!chainKey) {
7334
- throw new Error(`Unsupported coin type: ${coinId}`);
7335
- }
7422
+ const chainKey = params.chainKey;
7336
7423
  const ct5 = requireCoinType(coinId);
7337
7424
  const account = this.getAccountFromSigner(chainKey);
7338
7425
  const asset = contractAddress ? new import_trust_sdk2.Asset.Token(account, contractAddress, tokenTypeForCoinId(coinId)) : new import_trust_sdk2.Asset.Coin(account);
@@ -7362,10 +7449,7 @@ var init_dist3 = __esm({
7362
7449
  }
7363
7450
  async executeContract(params) {
7364
7451
  const { coinId } = parseTokenId(params.assetId);
7365
- const chainKey = coinTypeToChainKey(coinId);
7366
- if (!chainKey) {
7367
- throw new Error(`Unsupported coin type: ${coinId}`);
7368
- }
7452
+ const chainKey = params.chainKey;
7369
7453
  const ct5 = requireCoinType(coinId);
7370
7454
  const account = this.getAccountFromSigner(chainKey);
7371
7455
  const asset = new import_trust_sdk2.Asset.Coin(account);
@@ -7390,7 +7474,7 @@ var init_dist3 = __esm({
7390
7474
  * `receiptTimeoutMs` config when set, otherwise the global constants
7391
7475
  * (1.5s poll, 60s timeout). Caller-supplied opts override both.
7392
7476
  */
7393
- async waitForReceipt(chainKey, txHash, opts) {
7477
+ async waitForReceipt(chainKey, hash, opts) {
7394
7478
  const { chain: chain2, chainConfig } = chainKeyToDefinedChain(chainKey);
7395
7479
  if (chainConfig?.namespace && chainConfig.namespace !== "eip155") {
7396
7480
  throw new TwakValidationError(
@@ -7416,15 +7500,15 @@ var init_dist3 = __esm({
7416
7500
  const deadline = Date.now() + timeoutMs;
7417
7501
  while (Date.now() < deadline) {
7418
7502
  const remainingMs = Math.max(1, deadline - Date.now());
7419
- const result = await this.postJsonRpc(rpcUrl, "eth_getTransactionReceipt", [txHash], {
7503
+ const result = await this.postJsonRpc(rpcUrl, "eth_getTransactionReceipt", [hash], {
7420
7504
  timeoutMs: Math.min(RPC_TIMEOUT_MS, remainingMs)
7421
7505
  });
7422
7506
  const receipt = result ?? null;
7423
7507
  if (receipt) return receipt;
7424
7508
  await new Promise((resolve2) => setTimeout(resolve2, pollMs));
7425
7509
  }
7426
- throw new TwakNetworkError(`Timed out waiting for receipt ${txHash} on ${chainKey}`, {
7427
- txHash,
7510
+ throw new TwakNetworkError(`Timed out waiting for receipt ${hash} on ${chainKey}`, {
7511
+ hash,
7428
7512
  chainKey
7429
7513
  });
7430
7514
  }
@@ -7515,13 +7599,13 @@ var init_dist3 = __esm({
7515
7599
  currency: chainKey
7516
7600
  };
7517
7601
  }
7518
- async findTransactionStatus(chainKey, address, txHash) {
7602
+ async findTransactionStatus(chainKey, address, hash) {
7519
7603
  const coinId = chainKeyToCoinType(chainKey);
7520
7604
  if (coinId === void 0) throw new Error(`Unsupported chain: ${chainKey}`);
7521
7605
  const account = this.createAccountForAddress(coinId, address);
7522
7606
  let result;
7523
7607
  try {
7524
- result = await import_trust_sdk2.BlockchainService.findTransaction(account, txHash);
7608
+ result = await import_trust_sdk2.BlockchainService.findTransaction(account, hash);
7525
7609
  } catch {
7526
7610
  return { confirmed: false, pending: false, failed: false, error: "Transaction not found" };
7527
7611
  }
@@ -7629,8 +7713,9 @@ var init_dist3 = __esm({
7629
7713
  * EIP-7702 token-as-gas sponsorship.
7630
7714
  */
7631
7715
  async signAndBroadcast(transaction, account, coinId, chainKey, ct5, hdWallet) {
7632
- const chain2 = new import_trust_sdk2.DefinedChain(ct5);
7633
- const sponsoredUrl = this.getSponsoredRpcUrl(coinId);
7716
+ const testnet = isTestnetChain(chainKey);
7717
+ const chain2 = testnet ? chainKeyToDefinedChain(chainKey).chain : new import_trust_sdk2.DefinedChain(ct5);
7718
+ const sponsoredUrl = testnet ? void 0 : this.getSponsoredRpcUrl(coinId);
7634
7719
  if (sponsoredUrl) {
7635
7720
  try {
7636
7721
  const zeroFee = zeroGasFee(await import_trust_sdk2.BlockchainService.calculateFee(transaction));
@@ -7659,7 +7744,8 @@ var init_dist3 = __esm({
7659
7744
  const { fee, transaction7702 } = await this.calculateFeeWithSponsorship(
7660
7745
  transaction,
7661
7746
  account,
7662
- coinId
7747
+ coinId,
7748
+ chainKey
7663
7749
  );
7664
7750
  const tx = transaction7702 ?? transaction;
7665
7751
  const nonce = await import_trust_sdk2.BlockchainService.fetchNonce(account);
@@ -7729,8 +7815,8 @@ var init_dist3 = __esm({
7729
7815
  * Sets is7702=true and energy=tokenAsset on the transaction so the
7730
7816
  * Trust SDK routes through the Smart Wallet fee service internally.
7731
7817
  */
7732
- async calculateFeeWithSponsorship(transaction, account, coinId) {
7733
- if (!_BlockchainClient.SPONSORABLE_COINS.includes(coinId)) {
7818
+ async calculateFeeWithSponsorship(transaction, account, coinId, chainKey) {
7819
+ if (!_BlockchainClient.SPONSORABLE_COINS.includes(coinId) || chainKey !== void 0 && isTestnetChain(chainKey)) {
7734
7820
  return { fee: await import_trust_sdk2.BlockchainService.calculateFee(transaction) };
7735
7821
  }
7736
7822
  let supports7702 = false;
@@ -7866,18 +7952,18 @@ var init_dist3 = __esm({
7866
7952
  }
7867
7953
  return this.signer.getAccount(chainKey);
7868
7954
  }
7869
- createAccountForAddress(coinId, address) {
7955
+ createAccountForAddress(coinId, address, chainKey) {
7870
7956
  if (this.signer) {
7871
- const chainKey = coinTypeToChainKey(coinId);
7872
- if (chainKey) {
7957
+ const key = chainKey ?? coinTypeToChainKey(coinId);
7958
+ if (key) {
7873
7959
  try {
7874
- return this.signer.getAccount(chainKey);
7960
+ return this.signer.getAccount(key);
7875
7961
  } catch {
7876
7962
  }
7877
7963
  }
7878
7964
  }
7879
7965
  const ct5 = requireCoinType(coinId);
7880
- const chain2 = new import_trust_sdk2.DefinedChain(ct5);
7966
+ const chain2 = chainKey !== void 0 && isTestnetChain(chainKey) ? chainKeyToDefinedChain(chainKey).chain : new import_trust_sdk2.DefinedChain(ct5);
7881
7967
  const wc3 = getWalletCore();
7882
7968
  const derivation = coinId === 0 && address.startsWith("bc1p") ? wc3.Derivation.bitcoinTaproot : coinId === 0 && address.startsWith("bc1") ? wc3.Derivation.bitcoinSegwit : wc3.Derivation.default;
7883
7969
  return new import_trust_sdk2.Account(address, derivation, "", "", "", chain2);
@@ -7941,7 +8027,9 @@ var init_dist3 = __esm({
7941
8027
  );
7942
8028
  }
7943
8029
  if (json.error) {
7944
- throw new TwakNetworkError(`RPC error on ${method}: ${json.error.message}`, {
8030
+ const { message, data } = json.error;
8031
+ const suffix = revertDataSuffix(message, data);
8032
+ throw new TwakNetworkError(`RPC error on ${method}: ${message}${suffix}`, {
7945
8033
  method,
7946
8034
  rpcUrl,
7947
8035
  rpcError: json.error
@@ -7974,7 +8062,7 @@ var init_dist3 = __esm({
7974
8062
  getAddress(chainKey) {
7975
8063
  const cached = this.addressCache.get(chainKey);
7976
8064
  if (cached) return cached;
7977
- const coinId = chainKeyToCoinType(chainKey);
8065
+ const coinId = chainKeyToCoinType(chainKey) ?? getChainById(chainKey)?.coinId;
7978
8066
  if (coinId === void 0) {
7979
8067
  throw new Error(`Unsupported chain: ${chainKey}`);
7980
8068
  }
@@ -7994,7 +8082,8 @@ var init_dist3 = __esm({
7994
8082
  }
7995
8083
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
7996
8084
  getAccount(chainKey) {
7997
- const coinId = chainKeyToCoinType(chainKey);
8085
+ const testnet = isTestnetChain(chainKey);
8086
+ const coinId = chainKeyToCoinType(chainKey) ?? getChainById(chainKey)?.coinId;
7998
8087
  if (coinId === void 0) {
7999
8088
  throw new Error(`Unsupported chain: ${chainKey}`);
8000
8089
  }
@@ -8002,12 +8091,21 @@ var init_dist3 = __esm({
8002
8091
  if (!ct5) {
8003
8092
  throw new Error(`No CoinType mapping for chain: ${chainKey} (coinId: ${coinId})`);
8004
8093
  }
8005
- const chain2 = new import_trust_sdk4.DefinedChain(ct5);
8094
+ const chain2 = testnet ? new import_trust_sdk4.CustomChain(ct5, getChainById(chainKey).chainId) : new import_trust_sdk4.DefinedChain(ct5);
8006
8095
  const accounts = import_trust_sdk4.AccountManagementService.createDefaultAccountsHdWallet(chain2, this.hdWallet);
8007
8096
  if (accounts.length === 0) {
8008
8097
  throw new Error(`No accounts for chain: ${chainKey}`);
8009
8098
  }
8010
- return coinId === 0 && accounts.length > 1 ? accounts[1] : accounts[0];
8099
+ const account = coinId === 0 && accounts.length > 1 ? accounts[1] : accounts[0];
8100
+ if (!testnet) return account;
8101
+ return new import_trust_sdk4.Account(
8102
+ account.address,
8103
+ account.derivation,
8104
+ account.publicKey,
8105
+ account.extendedPublicKey,
8106
+ account.derivationPath,
8107
+ chain2
8108
+ );
8011
8109
  }
8012
8110
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
8013
8111
  getPrivateKey(chainKey) {
@@ -8217,7 +8315,7 @@ var init_dist3 = __esm({
8217
8315
  async sendTransaction(params) {
8218
8316
  await this.ensureConnected();
8219
8317
  this.resetAutoLockTimer();
8220
- const { getChainByEvmId: getChainByEvmId2, chainKeyToCoinType: chainKeyToCoinType3 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
8318
+ const { getChainByEvmId: getChainByEvmId2, chainKeyToCoinType: chainKeyToCoinType3, resolveChain: resolveChain2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
8221
8319
  let assetId;
8222
8320
  if (params.chainId.startsWith("eip155:")) {
8223
8321
  const chain2 = getChainByEvmId2(params.chainId.replace("eip155:", ""));
@@ -8230,6 +8328,7 @@ var init_dist3 = __esm({
8230
8328
  if (coinId === void 0) throw new Error(`Unsupported chain: ${params.chainId}`);
8231
8329
  assetId = `c${coinId}`;
8232
8330
  }
8331
+ const chainKey = resolveChain2(params.chainId);
8233
8332
  const hdWallet = this.signer.getHDWallet();
8234
8333
  const hasCalldata = params.data && params.data !== "0x" && params.data !== "0x0";
8235
8334
  const hasValue = params.value && params.value !== "0x0" && params.value !== "0x" && params.value !== "0";
@@ -8238,7 +8337,7 @@ var init_dist3 = __esm({
8238
8337
  serializedMessage: params.data,
8239
8338
  hdWallet
8240
8339
  });
8241
- return result2.txHash;
8340
+ return result2.hash;
8242
8341
  }
8243
8342
  if (hasCalldata || hasValue) {
8244
8343
  const result2 = await this.blockchainClient.executeContract({
@@ -8246,17 +8345,19 @@ var init_dist3 = __esm({
8246
8345
  to: params.to,
8247
8346
  value: params.value || "0x0",
8248
8347
  data: params.data || "0x",
8249
- hdWallet
8348
+ hdWallet,
8349
+ chainKey
8250
8350
  });
8251
- return result2.txHash;
8351
+ return result2.hash;
8252
8352
  }
8253
8353
  const result = await this.blockchainClient.transfer({
8254
8354
  assetId,
8255
8355
  to: params.to,
8256
8356
  amount: params.value || "0",
8257
- hdWallet
8357
+ hdWallet,
8358
+ chainKey
8258
8359
  });
8259
- return result.txHash;
8360
+ return result.hash;
8260
8361
  }
8261
8362
  supportsChain(chainId) {
8262
8363
  return isChainSupported(chainId);
@@ -14355,7 +14456,7 @@ var require_codegen = __commonJS({
14355
14456
  AND: new code_1._Code("&&"),
14356
14457
  ADD: new code_1._Code("+")
14357
14458
  };
14358
- var Node = class {
14459
+ var Node2 = class {
14359
14460
  optimizeNodes() {
14360
14461
  return this;
14361
14462
  }
@@ -14363,7 +14464,7 @@ var require_codegen = __commonJS({
14363
14464
  return this;
14364
14465
  }
14365
14466
  };
14366
- var Def = class extends Node {
14467
+ var Def = class extends Node2 {
14367
14468
  constructor(varKind, name2, rhs) {
14368
14469
  super();
14369
14470
  this.varKind = varKind;
@@ -14386,7 +14487,7 @@ var require_codegen = __commonJS({
14386
14487
  return this.rhs instanceof code_1._CodeOrName ? this.rhs.names : {};
14387
14488
  }
14388
14489
  };
14389
- var Assign = class extends Node {
14490
+ var Assign = class extends Node2 {
14390
14491
  constructor(lhs, rhs, sideEffects) {
14391
14492
  super();
14392
14493
  this.lhs = lhs;
@@ -14416,7 +14517,7 @@ var require_codegen = __commonJS({
14416
14517
  return `${this.lhs} ${this.op}= ${this.rhs};` + _n3;
14417
14518
  }
14418
14519
  };
14419
- var Label = class extends Node {
14520
+ var Label = class extends Node2 {
14420
14521
  constructor(label) {
14421
14522
  super();
14422
14523
  this.label = label;
@@ -14426,7 +14527,7 @@ var require_codegen = __commonJS({
14426
14527
  return `${this.label}:` + _n3;
14427
14528
  }
14428
14529
  };
14429
- var Break = class extends Node {
14530
+ var Break = class extends Node2 {
14430
14531
  constructor(label) {
14431
14532
  super();
14432
14533
  this.label = label;
@@ -14437,7 +14538,7 @@ var require_codegen = __commonJS({
14437
14538
  return `break${label};` + _n3;
14438
14539
  }
14439
14540
  };
14440
- var Throw = class extends Node {
14541
+ var Throw = class extends Node2 {
14441
14542
  constructor(error2) {
14442
14543
  super();
14443
14544
  this.error = error2;
@@ -14449,7 +14550,7 @@ var require_codegen = __commonJS({
14449
14550
  return this.error.names;
14450
14551
  }
14451
14552
  };
14452
- var AnyCode = class extends Node {
14553
+ var AnyCode = class extends Node2 {
14453
14554
  constructor(code2) {
14454
14555
  super();
14455
14556
  this.code = code2;
@@ -14468,7 +14569,7 @@ var require_codegen = __commonJS({
14468
14569
  return this.code instanceof code_1._CodeOrName ? this.code.names : {};
14469
14570
  }
14470
14571
  };
14471
- var ParentNode = class extends Node {
14572
+ var ParentNode = class extends Node2 {
14472
14573
  constructor(nodes = []) {
14473
14574
  super();
14474
14575
  this.nodes = nodes;
@@ -40545,756 +40646,146 @@ init_dist2();
40545
40646
  var import_chalk6 = __toESM(require_source());
40546
40647
  init_dist();
40547
40648
 
40548
- // src/branding.ts
40549
- var import_chalk5 = __toESM(require_source());
40550
- var boxen = require_boxen();
40551
- var SHIELD = [
40552
- " @@=+ ",
40553
- " @@@@@@@======= ",
40554
- " @@@@@@@@@@@@@============+ ",
40555
- " @@@@@@@@@@@@@@@@================ ",
40556
- " @@@@@@@@@@@@@@@@+=============== ",
40557
- " @@@@@@@@@@@@@@@@++++============ ",
40558
- " @@@@@@@@@@@@@@@@++++++++++=====+ ",
40559
- " @@@@@@@@@@@@@@@@**++++++++++++++ ",
40560
- " @@@@@@@@@@@@@@@@********++++++++ ",
40561
- " @@@@@@@@@@@@@@@@**************++ ",
40562
- " @@@@@@@@@@@@@@@@#####*********** ",
40563
- " @@@@@@@@@@@@@@@###########**** ",
40564
- " @@@@@@@@@@@@@@%############## ",
40565
- " @@@@@@@@@@@@@@%%%%%%%####### ",
40566
- " @@@@@@@@@@@@@%%%%%%%%%%%%% ",
40567
- " @@@@@@@@@@@%%%%%%%%%%% ",
40568
- " @@@@@@@@@@@@@%%%%% ",
40569
- " @@@@@@@@@@@@@@ ",
40570
- " @@@@@@@@@@ ",
40571
- " @@ "
40572
- ];
40573
- function printBanner(version3) {
40574
- const logo = SHIELD.map((l4) => import_chalk5.default.green(l4)).join("\n");
40575
- const title = import_chalk5.default.green.bold(" Trust Wallet Agent Kit");
40576
- const ver = import_chalk5.default.dim(` v${version3}`);
40577
- console.log(
40578
- boxen(`${logo}
40579
-
40580
- ${title}
40581
- ${ver}`, {
40582
- borderColor: "green",
40583
- borderStyle: "round",
40584
- padding: { top: 1, bottom: 1, left: 2, right: 2 }
40585
- })
40586
- );
40649
+ // ../contracts/dist/index.mjs
40650
+ init_dist();
40651
+ init_dist();
40652
+ init_dist();
40653
+ init_dist();
40654
+ init_dist3();
40655
+ init_dist();
40656
+ init_dist();
40657
+ init_dist();
40658
+ var HEX_PATTERN = /^0x[0-9a-fA-F]*$/;
40659
+ function isHex(value) {
40660
+ return HEX_PATTERN.test(value);
40587
40661
  }
40588
- function box(content) {
40589
- return boxen(content, {
40590
- borderColor: "green",
40591
- borderStyle: "round",
40592
- padding: { top: 0, bottom: 0, left: 1, right: 1 }
40593
- });
40662
+ function hexToBytes3(hex2) {
40663
+ if (!isHex(hex2)) {
40664
+ throw new TwakValidationError(`Invalid hex string: ${hex2}`, { kind: "invalidHex" });
40665
+ }
40666
+ const clean2 = hex2.slice(2);
40667
+ if (clean2.length % 2 !== 0) {
40668
+ throw new TwakValidationError(`Hex string has odd length: ${hex2}`, { kind: "invalidHex" });
40669
+ }
40670
+ const out = new Uint8Array(clean2.length / 2);
40671
+ for (let i2 = 0; i2 < out.length; i2++) {
40672
+ out[i2] = parseInt(clean2.slice(i2 * 2, i2 * 2 + 2), 16);
40673
+ }
40674
+ return out;
40594
40675
  }
40595
-
40596
- // src/commands/wallet.ts
40597
- function getQRString(address) {
40598
- try {
40599
- const qrcode = require("qrcode-terminal");
40600
- let result = "";
40601
- qrcode.generate(address, { small: true }, (code2) => {
40602
- result = code2;
40603
- });
40604
- return result;
40605
- } catch {
40606
- return "";
40676
+ function bytesToHex3(bytes) {
40677
+ let out = "0x";
40678
+ for (let i2 = 0; i2 < bytes.length; i2++) {
40679
+ out += bytes[i2].toString(16).padStart(2, "0");
40607
40680
  }
40681
+ return out;
40608
40682
  }
40609
- function registerWalletCommand(program3) {
40610
- const wallet = program3.command("wallet").description("Manage agent wallet");
40611
- wallet.command("create").description("Create a new agent wallet").requiredOption("--password <password>", "Password to encrypt the wallet").option("--no-keychain", "Do not save password to OS keychain").option("--skip-password-check", "Skip password strength validation").option("--json", "JSON output").action(async (opts) => {
40612
- try {
40613
- if (!opts.skipPasswordCheck) {
40614
- const weakness = getPasswordWeaknesses(opts.password);
40615
- if (weakness) {
40616
- throw new WalletError("VALIDATION_ERROR", `Password too weak: must ${weakness}.`);
40617
- }
40618
- }
40619
- await ensureTrustSDK();
40620
- const store = new WalletConfigStore();
40621
- if (await store.exists()) {
40622
- throw new WalletError(
40623
- "WALLET_EXISTS",
40624
- "Wallet already exists. Back up ~/.twak/wallet.json, then delete it to create a new one."
40625
- );
40626
- }
40627
- const provider = await AgentWalletProvider.create(opts.password);
40628
- const addresses = await provider.getAddresses();
40629
- if (opts.keychain !== false) {
40630
- const kc2 = new KeychainManager();
40631
- if (kc2.isAvailable()) {
40632
- await kc2.saveWalletPassword(opts.password);
40633
- if (!opts.json) info("Password saved to OS keychain (use --no-keychain to skip)");
40634
- }
40635
- }
40636
- if (!opts.json) success("Agent wallet created successfully");
40637
- try {
40638
- const creds = await getCredentials();
40639
- await registerWalletWithBackend(creds, provider);
40640
- if (!opts.json) info("Wallet registered with backend");
40641
- } catch {
40642
- if (!opts.json) warn("Backend registration failed (holdings may be limited)");
40643
- }
40644
- if (opts.json) {
40645
- output({ addresses }, true);
40646
- } else {
40647
- info(`Generated addresses for ${addresses.length} chains`);
40648
- }
40649
- } catch (err) {
40650
- handleCommandError(err, opts.json, "Wallet creation failed");
40683
+ function hexOrUtf8ToBytesHex(value) {
40684
+ const looksHex = value.length >= 2 && value[0] === "0" && (value[1] === "x" || value[1] === "X");
40685
+ if (looksHex) {
40686
+ const canonical = "0x" + value.slice(2);
40687
+ if (!isHex(canonical)) {
40688
+ throw new TwakValidationError(
40689
+ `Value starts with "0x" but is not valid hex: ${value}. Use only [0-9a-fA-F] characters, or drop the "0x" prefix to send a UTF-8 string.`,
40690
+ { kind: "invalidHex" }
40691
+ );
40651
40692
  }
40652
- safeExit();
40653
- });
40654
- wallet.command("address").description("Get wallet address for a chain").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").option(
40655
- "--password <password>",
40656
- "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
40657
- ).option("--json", "JSON output").action(async (opts) => {
40658
- try {
40659
- if (!getChainById(opts.chain)) {
40660
- throw new WalletError("CHAIN_UNSUPPORTED", `Unknown chain: ${opts.chain}`);
40661
- }
40662
- await ensureTrustSDK();
40663
- const password3 = await resolvePassword(opts);
40664
- const provider = await AgentWalletProvider.fromStorage(password3);
40665
- const address = await provider.getAddress(opts.chain);
40666
- if (opts.json) {
40667
- output({ chain: opts.chain, address }, true);
40668
- } else {
40669
- const qr3 = getQRString(address);
40670
- const content = `${import_chalk6.default.dim("Chain")} ${import_chalk6.default.bold(opts.chain)}
40671
- ${import_chalk6.default.dim("Address")} ${import_chalk6.default.green(address)}
40672
-
40673
- ${qr3}`;
40674
- console.log(box(content));
40675
- }
40676
- } catch (err) {
40677
- handleCommandError(err, opts.json, "Wallet address failed");
40693
+ if ((canonical.length - 2) % 2 !== 0) {
40694
+ throw new TwakValidationError(
40695
+ `Hex value has odd length: ${value}. Solidity bytes must be byte-aligned.`,
40696
+ { kind: "invalidHex" }
40697
+ );
40678
40698
  }
40679
- safeExit();
40680
- });
40681
- wallet.command("addresses").description("List all wallet addresses").option(
40682
- "--password <password>",
40683
- "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
40684
- ).option("--json", "JSON output").action(async (opts) => {
40685
- try {
40686
- await ensureTrustSDK();
40687
- const password3 = await resolvePassword(opts);
40688
- const provider = await AgentWalletProvider.fromStorage(password3);
40689
- const addresses = await provider.getAddresses();
40690
- if (opts.json) {
40691
- output({ addresses }, true);
40692
- } else {
40693
- const grouped = /* @__PURE__ */ new Map();
40694
- for (const addr of addresses) {
40695
- const chains = grouped.get(addr.address) ?? [];
40696
- chains.push(addr.chainId);
40697
- grouped.set(addr.address, chains);
40698
- }
40699
- for (const [address, chains] of grouped) {
40700
- const qr3 = getQRString(address);
40701
- const maxWidth = Math.max(address.length + 9, 50);
40702
- const chainLabel = `${import_chalk6.default.dim("Chains")} `;
40703
- const chainLabelLen = 9;
40704
- const chainLines = [];
40705
- let line = "";
40706
- for (const c3 of chains) {
40707
- const sep2 = line ? ", " : "";
40708
- if (line && line.length + sep2.length + c3.length + chainLabelLen > maxWidth) {
40709
- chainLines.push(line);
40710
- line = c3;
40711
- } else {
40712
- line += sep2 + c3;
40713
- }
40714
- }
40715
- if (line) chainLines.push(line);
40716
- const chainsStr = chainLines.map((l4, i2) => (i2 === 0 ? chainLabel : " ".repeat(chainLabelLen)) + import_chalk6.default.bold(l4)).join("\n");
40717
- const content = `${chainsStr}
40718
- ${import_chalk6.default.dim("Address")} ${import_chalk6.default.green(address)}
40719
-
40720
- ${qr3}`;
40721
- console.log(box(content));
40722
- }
40723
- }
40724
- } catch (err) {
40725
- handleCommandError(err, opts.json, "Wallet addresses failed");
40699
+ return canonical;
40700
+ }
40701
+ return bytesToHex3(new TextEncoder().encode(value));
40702
+ }
40703
+ function wc2() {
40704
+ return getWalletCore();
40705
+ }
40706
+ var BRIDGE = {
40707
+ createAbiFunction(name2) {
40708
+ const ctor = wc2().EthereumAbiFunction;
40709
+ if (typeof ctor?.createWithString === "function") {
40710
+ return ctor.createWithString(name2);
40726
40711
  }
40727
- safeExit();
40712
+ return new ctor(name2);
40713
+ },
40714
+ encode(fn2) {
40715
+ return wc2().EthereumAbi.encode(fn2);
40716
+ },
40717
+ decodeOutput(fn2, data) {
40718
+ return wc2().EthereumAbi.decodeOutput(fn2, data);
40719
+ },
40720
+ keccak256(data) {
40721
+ return wc2().Hash.keccak256(data);
40722
+ }
40723
+ };
40724
+ function getAbiBridge() {
40725
+ return BRIDGE;
40726
+ }
40727
+ function findAbiFunction(abi, methodName, argCount) {
40728
+ const matches = abi.filter(
40729
+ (e2) => e2.type === "function" && e2.name === methodName
40730
+ );
40731
+ if (matches.length === 0) {
40732
+ throw new TwakValidationError(`Unknown ABI method: ${methodName}`, {
40733
+ kind: "unknownMethod",
40734
+ methodName
40735
+ });
40736
+ }
40737
+ if (matches.length === 1 || argCount === void 0) {
40738
+ return matches[0];
40739
+ }
40740
+ const byArity = matches.filter((m2) => m2.inputs.length === argCount);
40741
+ if (byArity.length === 1) {
40742
+ return byArity[0];
40743
+ }
40744
+ throw new TwakValidationError(`No unique ${methodName} overload taking ${argCount} argument(s)`, {
40745
+ kind: "unknownMethod",
40746
+ methodName
40728
40747
  });
40729
- wallet.command("balance").description("Get wallet balance on a chain (native + token holdings)").option("--chain <key>", "Chain key").option("--all", "Show balances across all chains with funds").option(
40730
- "--password <password>",
40731
- "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
40732
- ).option("--no-tokens", "Skip token balance lookup").option("--json", "JSON output").action(async (opts) => {
40733
- try {
40734
- if (opts.all && opts.chain) {
40735
- throw new WalletError("VALIDATION_ERROR", "--all and --chain are mutually exclusive");
40736
- }
40737
- if (!opts.all && !opts.chain) {
40738
- throw new WalletError("VALIDATION_ERROR", "Provide --chain <key> or --all");
40739
- }
40740
- await ensureTrustSDK();
40741
- const password3 = await resolvePassword(opts);
40742
- const provider = await AgentWalletProvider.fromStorage(password3);
40743
- const httpClient = await getClient();
40744
- if (!opts.all) {
40745
- const chain2 = getChainById(opts.chain);
40746
- if (!chain2) {
40747
- throw new WalletError("CHAIN_UNSUPPORTED", `Unknown chain: ${opts.chain}`);
40748
- }
40749
- }
40750
- const allAddresses = await provider.getAddresses();
40751
- const addressMap = /* @__PURE__ */ new Map();
40752
- for (const a3 of allAddresses) addressMap.set(a3.chainId, a3.address);
40753
- const requestedChains = opts.all ? Object.keys(CHAINS) : [opts.chain];
40754
- const skipTokens = opts.tokens === false;
40755
- const chainEntries = requestedChains.flatMap((chainKey) => {
40756
- const chain2 = getChainById(chainKey);
40757
- const address = addressMap.get(chainKey);
40758
- if (!chain2 || !address) return [];
40759
- return [{ chainKey, chain: chain2, address }];
40760
- });
40761
- if (!opts.all && chainEntries.length === 0) {
40762
- throw new WalletError(
40763
- "CHAIN_UNSUPPORTED",
40764
- `Wallet does not have an address for chain: ${opts.chain}`
40765
- );
40766
- }
40767
- const nativeAssets = chainEntries.map(({ chainKey, chain: chain2, address }) => ({
40768
- chainKey,
40769
- chain: chain2,
40770
- token: void 0,
40771
- assetId: `c${chain2.coinId}`,
40772
- userAddress: address
40773
- }));
40774
- const tokenAssets = skipTokens ? [] : chainEntries.flatMap(({ chainKey, chain: chain2, address }) => {
40775
- const knownTokens = COMMON_TOKENS[chainKey];
40776
- if (!knownTokens) return [];
40777
- return Object.keys(knownTokens).flatMap((sym) => {
40778
- const resolved = resolveToken(sym, chainKey);
40779
- if (!resolved) return [];
40780
- return [
40781
- {
40782
- chainKey,
40783
- chain: chain2,
40784
- token: { ...resolved, symbol: sym },
40785
- assetId: `c${chain2.coinId}_t${resolved.address}`,
40786
- userAddress: address
40787
- }
40788
- ];
40789
- });
40790
- });
40791
- const allAssets = [...nativeAssets, ...tokenAssets];
40792
- const balances = await fetchBalances(
40793
- httpClient,
40794
- allAssets.map(({ assetId, userAddress }) => ({ assetId, address: userAddress }))
40795
- );
40796
- const assetsById = new Map(allAssets.map((a3) => [a3.assetId, a3]));
40797
- const resultsByChain = /* @__PURE__ */ new Map();
40798
- for (const { chainKey, chain: chain2, address } of chainEntries) {
40799
- resultsByChain.set(chainKey, {
40800
- chain: chainKey,
40801
- address,
40802
- symbol: chain2.symbol,
40803
- available: "0",
40804
- total: "0",
40805
- totalUsd: null,
40806
- tokens: []
40807
- });
40808
- }
40809
- for (const balance of balances) {
40810
- const assetInfo = assetsById.get(balance.assetId);
40811
- if (!assetInfo) {
40812
- throw new WalletError(
40813
- "NETWORK_ERROR",
40814
- "Could not fetch balances. Please try again later."
40815
- );
40816
- }
40817
- const result = resultsByChain.get(assetInfo.chainKey);
40818
- if (!result) continue;
40819
- const { token, chain: chain2 } = assetInfo;
40820
- const amts = balance.amounts;
40821
- if (token) {
40822
- const human = formatAmount(amts.total ?? "0", token.decimals);
40823
- if (human !== "0") {
40824
- result.tokens.push({
40825
- symbol: token.symbol,
40826
- contract: token.address,
40827
- balance: human
40828
- });
40829
- }
40830
- } else {
40831
- result.available = formatAmount(amts.available ?? "0", chain2.decimals);
40832
- result.total = formatAmount(amts.total ?? "0", chain2.decimals);
40833
- const lockedHuman = formatAmount(amts.locked ?? "0", chain2.decimals);
40834
- if (lockedHuman !== "0") result.staked = lockedHuman;
40835
- const fiat = amts.totalInFiat;
40836
- result.totalUsd = fiat && fiat !== "" ? parseFloat(fiat) : null;
40837
- }
40838
- }
40839
- const orderedResults = chainEntries.map((e2) => resultsByChain.get(e2.chainKey)).filter((r3) => r3 !== void 0);
40840
- const visible = opts.all ? orderedResults.filter((r3) => r3.total !== "0" || r3.tokens.length > 0) : orderedResults;
40841
- const renderBlock = (r3, withSeparator) => {
40842
- if (withSeparator) console.log("");
40843
- console.log(import_chalk6.default.bold.underline(`${r3.chain} (${r3.symbol})`));
40844
- console.log(` ${import_chalk6.default.dim("Address")} ${r3.address}`);
40845
- console.log(` ${import_chalk6.default.dim("Available")} ${r3.available}`);
40846
- if (r3.staked) console.log(` ${import_chalk6.default.dim("Staked")} ${r3.staked}`);
40847
- const usdSuffix = r3.totalUsd !== null ? ` (\u2248 $${r3.totalUsd.toFixed(2)})` : "";
40848
- console.log(` ${import_chalk6.default.dim("Total")} ${r3.total}${usdSuffix}`);
40849
- if (r3.tokens.length > 0) {
40850
- console.log(` ${import_chalk6.default.dim("Tokens:")}`);
40851
- for (const t of r3.tokens) {
40852
- console.log(` ${import_chalk6.default.bold(t.symbol.padEnd(10))} ${t.balance}`);
40853
- }
40854
- }
40855
- };
40856
- if (opts.all) {
40857
- if (opts.json) {
40858
- output(visible, true);
40859
- } else if (visible.length === 0) {
40860
- info("No chains with non-zero native balance found.");
40861
- } else {
40862
- visible.forEach((r3, i2) => renderBlock(r3, i2 > 0));
40863
- }
40864
- } else {
40865
- const single = visible[0];
40866
- if (opts.json) {
40867
- output(single, true);
40868
- } else {
40869
- renderBlock(single, false);
40870
- }
40748
+ }
40749
+ function encodeFunctionCall(abi, methodName, args) {
40750
+ const fnDef = findAbiFunction(abi, methodName, args.length);
40751
+ if (args.length !== fnDef.inputs.length) {
40752
+ throw new TwakValidationError(
40753
+ `Wrong arg count for ${methodName}: expected ${fnDef.inputs.length}, got ${args.length}`,
40754
+ {
40755
+ kind: "wrongArgCount",
40756
+ methodName,
40757
+ expected: fnDef.inputs.length,
40758
+ actual: args.length
40871
40759
  }
40872
- } catch (err) {
40873
- handleCommandError(err, opts.json, "Balance check failed");
40874
- }
40875
- safeExit();
40760
+ );
40761
+ }
40762
+ if (encoderRequiresHandRoll(fnDef.inputs)) {
40763
+ return encodeFunctionCallHandRoll(methodName, fnDef.inputs, args);
40764
+ }
40765
+ const bridge = getAbiBridge();
40766
+ const fn2 = bridge.createAbiFunction(methodName);
40767
+ fnDef.inputs.forEach((param, i2) => {
40768
+ pushParam(fn2, param, args[i2], methodName);
40876
40769
  });
40877
- const keychain = wallet.command("keychain").description("Manage wallet password in OS keychain");
40878
- keychain.command("save").description("Save wallet password to OS keychain").requiredOption("--password <password>", "Wallet password to store").option("--json", "JSON output").action(async (opts) => {
40879
- try {
40880
- const kc2 = new KeychainManager();
40881
- if (!kc2.isAvailable()) {
40882
- throw new WalletError("KEYCHAIN_UNAVAILABLE", "OS keychain not available on this system");
40883
- }
40884
- await kc2.saveWalletPassword(opts.password);
40885
- success("Wallet password saved to OS keychain");
40886
- } catch (err) {
40887
- handleCommandError(err, opts.json, "Keychain save failed");
40770
+ return bytesToHex3(bridge.encode(fn2));
40771
+ }
40772
+ function pushParam(fn2, param, value, methodName) {
40773
+ const type = param.type;
40774
+ const isOutput = false;
40775
+ if (type === "address") {
40776
+ fn2.addParamAddress(toAddressBytes(value, param, methodName), isOutput);
40777
+ return;
40778
+ }
40779
+ if (type === "bool") {
40780
+ if (typeof value !== "boolean") {
40781
+ throw argTypeMismatch(methodName, param, value, "boolean");
40888
40782
  }
40889
- safeExit();
40890
- });
40891
- keychain.command("delete").description("Remove wallet password from OS keychain").option("--json", "JSON output").action(async (opts) => {
40892
- try {
40893
- const kc2 = new KeychainManager();
40894
- if (!kc2.isAvailable()) {
40895
- throw new WalletError("KEYCHAIN_UNAVAILABLE", "OS keychain not available on this system");
40896
- }
40897
- await kc2.deleteWalletPassword();
40898
- success("Wallet password removed from OS keychain");
40899
- } catch (err) {
40900
- handleCommandError(err, opts.json, "Keychain delete failed");
40901
- }
40902
- safeExit();
40903
- });
40904
- keychain.command("check").description("Check if wallet password is stored in OS keychain").option("--json", "JSON output").action(async (opts) => {
40905
- try {
40906
- const kc2 = new KeychainManager();
40907
- if (!kc2.isAvailable()) {
40908
- if (opts.json) {
40909
- output({ available: false, stored: false }, true);
40910
- } else {
40911
- warn("keytar not available on this system");
40912
- }
40913
- safeExit();
40914
- return;
40915
- }
40916
- const pw = await kc2.getWalletPassword();
40917
- if (opts.json) {
40918
- output({ available: true, stored: !!pw }, true);
40919
- } else if (pw) {
40920
- success("Wallet password is stored in OS keychain");
40921
- } else {
40922
- info("No wallet password found in OS keychain");
40923
- }
40924
- } catch (err) {
40925
- handleCommandError(err, opts.json, "Keychain check failed");
40926
- }
40927
- safeExit();
40928
- });
40929
- wallet.command("connect").description("Connect external wallet via WalletConnect").option("--project-id <id>", "WalletConnect project ID (or set WALLETCONNECT_PROJECT_ID)").option(
40930
- "--chains <list>",
40931
- "Comma-separated CAIP-2 chain IDs",
40932
- "eip155:1,eip155:56,eip155:137,eip155:8453,eip155:42161"
40933
- ).option("--timeout <seconds>", "Connection timeout in seconds", "300").option("--json", "JSON output").action(async (opts) => {
40934
- try {
40935
- const projectId = opts.projectId || process.env.WALLETCONNECT_PROJECT_ID;
40936
- if (!projectId) {
40937
- throw new WalletError(
40938
- "VALIDATION_ERROR",
40939
- "WalletConnect project ID required. Use --project-id or set WALLETCONNECT_PROJECT_ID"
40940
- );
40941
- }
40942
- const provider = new WalletConnectProvider({ projectId });
40943
- const chains = opts.chains.split(",").map((s2) => s2.trim()).filter(Boolean);
40944
- info("Connecting via WalletConnect...");
40945
- const result = await provider.connect(chains);
40946
- const qr3 = getQRString(result.uri);
40947
- if (qr3) {
40948
- console.log(qr3);
40949
- }
40950
- info(`Trust Wallet: ${result.deepLinks.native}`);
40951
- info(`Universal: ${result.deepLinks.universal}`);
40952
- info("\nWaiting for wallet approval...");
40953
- const timeoutMs = parseInt(opts.timeout, 10) * 1e3;
40954
- const session = await provider.waitForSession(timeoutMs);
40955
- if (opts.json) {
40956
- output(
40957
- {
40958
- connected: true,
40959
- address: session.address,
40960
- chainId: session.chainId,
40961
- peer: session.peerName
40962
- },
40963
- true
40964
- );
40965
- } else {
40966
- success(`Connected to ${session.peerName || "wallet"}`);
40967
- info(` Address: ${session.address}`);
40968
- info(` Chain: ${session.chainId}`);
40969
- }
40970
- } catch (err) {
40971
- handleCommandError(err, opts.json, "WalletConnect failed");
40972
- }
40973
- safeExit();
40974
- });
40975
- wallet.command("status").description("Check wallet configuration status").option("--json", "JSON output").action(async (opts) => {
40976
- try {
40977
- const store = new WalletConfigStore();
40978
- const exists = await store.exists();
40979
- const kc2 = new KeychainManager();
40980
- const keychainHasPassword = kc2.isAvailable() ? !!await kc2.getWalletPassword() : false;
40981
- const totalChains = Object.keys(CHAINS).length;
40982
- let createdAt;
40983
- let addressCount;
40984
- if (exists) {
40985
- const config = await store.getConfig();
40986
- createdAt = config?.createdAt;
40987
- addressCount = config?.chains?.length ?? totalChains;
40988
- }
40989
- const result = {
40990
- agentWallet: exists ? "configured" : "not configured",
40991
- keychainPassword: keychainHasPassword ? "stored" : "not stored",
40992
- chains: totalChains,
40993
- supportedChains: totalChains,
40994
- ...createdAt ? { createdAt } : {},
40995
- ...addressCount !== void 0 ? { addressCount } : {}
40996
- };
40997
- if (opts.json) {
40998
- output(result, true);
40999
- } else {
41000
- console.log(
41001
- ` ${import_chalk6.default.dim("Wallet")} ${exists ? import_chalk6.default.green("configured") : import_chalk6.default.yellow("not configured")}`
41002
- );
41003
- if (createdAt) console.log(` ${import_chalk6.default.dim("Created")} ${createdAt}`);
41004
- console.log(
41005
- ` ${import_chalk6.default.dim("Keychain")} ${keychainHasPassword ? import_chalk6.default.green("password stored") : import_chalk6.default.yellow("no password")}`
41006
- );
41007
- console.log(` ${import_chalk6.default.dim("Chains")} ${totalChains} supported`);
41008
- if (addressCount !== void 0)
41009
- console.log(` ${import_chalk6.default.dim("Addresses")} ${addressCount}`);
41010
- if (!exists) info("\nRun `twak wallet create --password <pw>` to create a wallet.");
41011
- }
41012
- } catch (err) {
41013
- handleCommandError(err, opts.json, "Status failed");
41014
- }
41015
- safeExit();
41016
- });
41017
- wallet.command("portfolio").description("Show portfolio balances and USD values across chains, including token holdings").option("--chains <list>", "Comma-separated chain keys (default: all supported chains)").option(
41018
- "--password <password>",
41019
- "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
41020
- ).option("--json", "JSON output").action(async (opts) => {
41021
- try {
41022
- await ensureTrustSDK();
41023
- const password3 = await resolvePassword(opts);
41024
- const provider = await AgentWalletProvider.fromStorage(password3);
41025
- const httpClient = await getClient();
41026
- const allAddresses = await provider.getAddresses();
41027
- const chainsArg = opts.chains ?? Object.keys(CHAINS).join(",");
41028
- const requestedChains = chainsArg.split(",").map((s2) => s2.trim()).filter(Boolean);
41029
- if (requestedChains.length === 0) {
41030
- throw new WalletError("VALIDATION_ERROR", "--chains must include at least one chain key");
41031
- }
41032
- const chainEntries = requestedChains.flatMap((chainKey) => {
41033
- const chain2 = getChainById(chainKey);
41034
- const addrEntry = allAddresses.find((a3) => a3.chainId === chainKey);
41035
- if (!chain2 || !addrEntry) return [];
41036
- return [{ chainKey, chain: chain2, address: addrEntry.address }];
41037
- });
41038
- if (chainEntries.length === 0) {
41039
- throw new WalletError(
41040
- "CHAIN_UNSUPPORTED",
41041
- `No supported chains found in --chains "${opts.chains}"`
41042
- );
41043
- }
41044
- const chainAssets = chainEntries.map(({ chainKey, chain: chain2, address }) => ({
41045
- chainKey,
41046
- chain: chain2,
41047
- token: void 0,
41048
- assetId: `c${chain2.coinId}`,
41049
- userAddress: address
41050
- }));
41051
- const tokenAssets = chainEntries.flatMap(({ chainKey, chain: chain2, address }) => {
41052
- const knownTokens = COMMON_TOKENS[chainKey];
41053
- if (!knownTokens) return [];
41054
- return Object.keys(knownTokens).flatMap((sym) => {
41055
- const resolved = resolveToken(sym, chainKey);
41056
- if (!resolved) return [];
41057
- return [
41058
- {
41059
- chainKey,
41060
- chain: chain2,
41061
- token: { ...resolved, symbol: sym },
41062
- assetId: `c${chain2.coinId}_t${resolved.address}`,
41063
- userAddress: address
41064
- }
41065
- ];
41066
- });
41067
- });
41068
- const allAssets = [...chainAssets, ...tokenAssets];
41069
- const balances = await fetchBalances(
41070
- httpClient,
41071
- allAssets.map(({ assetId, userAddress }) => ({ assetId, address: userAddress }))
41072
- );
41073
- const assetsById = new Map(allAssets.map((a3) => [a3.assetId, a3]));
41074
- const allRows = [];
41075
- for (const balance of balances) {
41076
- const assetInfo = assetsById.get(balance.assetId);
41077
- if (!assetInfo) {
41078
- throw new WalletError(
41079
- "NETWORK_ERROR",
41080
- "Could not fetch balances. Please try again later."
41081
- );
41082
- }
41083
- const { token, chain: chain2, chainKey, userAddress } = assetInfo;
41084
- const decimals = token ? token.decimals : chain2.decimals;
41085
- const symbol = token ? token.symbol ?? token.address : chain2.symbol;
41086
- const isToken = token !== void 0;
41087
- const humanAmount = formatAmount(balance.amounts.total, decimals);
41088
- const fiatRaw = balance.amounts.totalInFiat;
41089
- const usdValue = fiatRaw && fiatRaw !== "" ? parseFloat(fiatRaw) : null;
41090
- allRows.push({
41091
- chain: chainKey,
41092
- type: isToken ? "token" : "native",
41093
- symbol,
41094
- address: userAddress,
41095
- ...token ? { contract: token.address } : {},
41096
- balance: humanAmount,
41097
- usdValue
41098
- });
41099
- }
41100
- const rows = allRows.filter((r3) => r3.type === "native" || r3.balance !== "0");
41101
- rows.sort((a3, b3) => {
41102
- const chainCmp = String(a3.chain).localeCompare(String(b3.chain));
41103
- if (chainCmp !== 0) return chainCmp;
41104
- return String(a3.type).localeCompare(String(b3.type));
41105
- });
41106
- if (opts.json) {
41107
- output(rows, true);
41108
- } else {
41109
- const totalUsd = rows.reduce(
41110
- (sum, r3) => sum + (typeof r3.usdValue === "number" ? r3.usdValue : 0),
41111
- 0
41112
- );
41113
- console.log("Chain Type Symbol Balance USD");
41114
- console.log("\u2500".repeat(72));
41115
- for (const r3 of rows) {
41116
- const usd = r3.usdValue !== null ? `$${r3.usdValue.toFixed(2)}` : "";
41117
- console.log(
41118
- `${String(r3.chain).padEnd(12)} ${String(r3.type).padEnd(7)} ${String(r3.symbol).padEnd(17)} ${String(r3.balance).padEnd(20)} ${usd}`
41119
- );
41120
- }
41121
- console.log("\u2500".repeat(72));
41122
- console.log(`Total USD: $${totalUsd.toFixed(2)}`);
41123
- }
41124
- } catch (err) {
41125
- handleCommandError(err, opts.json, "Portfolio failed");
41126
- }
41127
- safeExit();
41128
- });
41129
- wallet.command("sign-message").description("Sign a message with the agent wallet").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").requiredOption("--message <text>", "Message to sign").option(
41130
- "--password <password>",
41131
- "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
41132
- ).option("--json", "JSON output").action(async (opts) => {
41133
- try {
41134
- if (!getChainById(opts.chain)) {
41135
- throw new WalletError("CHAIN_UNSUPPORTED", `Unknown chain: ${opts.chain}`);
41136
- }
41137
- await ensureTrustSDK();
41138
- const password3 = await resolvePassword(opts);
41139
- const provider = await AgentWalletProvider.fromStorage(password3);
41140
- const address = await provider.getAddress(opts.chain);
41141
- const signature = await provider.signMessage(opts.chain, opts.message);
41142
- output({ chain: opts.chain, address, message: opts.message, signature }, opts.json);
41143
- } catch (err) {
41144
- handleCommandError(err, opts.json, "Sign failed");
41145
- }
41146
- safeExit();
41147
- });
41148
- wallet.command("register").description("Register wallet addresses with Trust Wallet backend (enables token holdings)").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (opts) => {
41149
- try {
41150
- const creds = await ensureTrustSDK();
41151
- const password3 = await resolvePassword(opts);
41152
- const provider = await AgentWalletProvider.fromStorage(password3);
41153
- await registerWalletWithBackend(creds, provider);
41154
- if (!opts.json) success("Wallet registered with backend");
41155
- const addresses = await provider.getAddresses();
41156
- output({ registered: true, chains: addresses.length }, opts.json);
41157
- } catch (err) {
41158
- handleCommandError(err, opts.json, "Registration failed");
41159
- }
41160
- safeExit();
41161
- });
41162
- }
41163
-
41164
- // src/commands/transfer.ts
41165
- init_dist();
41166
-
41167
- // ../swap/dist/index.mjs
41168
- init_dist();
41169
- init_dist();
41170
-
41171
- // ../contracts/dist/index.mjs
41172
- init_dist();
41173
- init_dist();
41174
- init_dist();
41175
- init_dist();
41176
- init_dist3();
41177
- init_dist();
41178
- init_dist();
41179
- init_dist();
41180
- var HEX_PATTERN = /^0x[0-9a-fA-F]*$/;
41181
- function isHex(value) {
41182
- return HEX_PATTERN.test(value);
41183
- }
41184
- function hexToBytes3(hex2) {
41185
- if (!isHex(hex2)) {
41186
- throw new TwakValidationError(`Invalid hex string: ${hex2}`, { kind: "invalidHex" });
41187
- }
41188
- const clean2 = hex2.slice(2);
41189
- if (clean2.length % 2 !== 0) {
41190
- throw new TwakValidationError(`Hex string has odd length: ${hex2}`, { kind: "invalidHex" });
41191
- }
41192
- const out = new Uint8Array(clean2.length / 2);
41193
- for (let i2 = 0; i2 < out.length; i2++) {
41194
- out[i2] = parseInt(clean2.slice(i2 * 2, i2 * 2 + 2), 16);
41195
- }
41196
- return out;
41197
- }
41198
- function bytesToHex3(bytes) {
41199
- let out = "0x";
41200
- for (let i2 = 0; i2 < bytes.length; i2++) {
41201
- out += bytes[i2].toString(16).padStart(2, "0");
41202
- }
41203
- return out;
41204
- }
41205
- function hexOrUtf8ToBytesHex(value) {
41206
- const looksHex = value.length >= 2 && value[0] === "0" && (value[1] === "x" || value[1] === "X");
41207
- if (looksHex) {
41208
- const canonical = "0x" + value.slice(2);
41209
- if (!isHex(canonical)) {
41210
- throw new TwakValidationError(
41211
- `Value starts with "0x" but is not valid hex: ${value}. Use only [0-9a-fA-F] characters, or drop the "0x" prefix to send a UTF-8 string.`,
41212
- { kind: "invalidHex" }
41213
- );
41214
- }
41215
- if ((canonical.length - 2) % 2 !== 0) {
41216
- throw new TwakValidationError(
41217
- `Hex value has odd length: ${value}. Solidity bytes must be byte-aligned.`,
41218
- { kind: "invalidHex" }
41219
- );
41220
- }
41221
- return canonical;
41222
- }
41223
- return bytesToHex3(new TextEncoder().encode(value));
41224
- }
41225
- function wc2() {
41226
- return getWalletCore();
41227
- }
41228
- var BRIDGE = {
41229
- createAbiFunction(name2) {
41230
- const ctor = wc2().EthereumAbiFunction;
41231
- if (typeof ctor?.createWithString === "function") {
41232
- return ctor.createWithString(name2);
41233
- }
41234
- return new ctor(name2);
41235
- },
41236
- encode(fn2) {
41237
- return wc2().EthereumAbi.encode(fn2);
41238
- },
41239
- decodeOutput(fn2, data) {
41240
- return wc2().EthereumAbi.decodeOutput(fn2, data);
41241
- },
41242
- keccak256(data) {
41243
- return wc2().Hash.keccak256(data);
41244
- }
41245
- };
41246
- function getAbiBridge() {
41247
- return BRIDGE;
41248
- }
41249
- function findAbiFunction(abi, methodName) {
41250
- const entry = abi.find(
41251
- (e2) => e2.type === "function" && e2.name === methodName
41252
- );
41253
- if (!entry) {
41254
- throw new TwakValidationError(`Unknown ABI method: ${methodName}`, {
41255
- kind: "unknownMethod",
41256
- methodName
41257
- });
41258
- }
41259
- return entry;
41260
- }
41261
- function encodeFunctionCall(abi, methodName, args) {
41262
- const fnDef = findAbiFunction(abi, methodName);
41263
- if (args.length !== fnDef.inputs.length) {
41264
- throw new TwakValidationError(
41265
- `Wrong arg count for ${methodName}: expected ${fnDef.inputs.length}, got ${args.length}`,
41266
- {
41267
- kind: "wrongArgCount",
41268
- methodName,
41269
- expected: fnDef.inputs.length,
41270
- actual: args.length
41271
- }
41272
- );
41273
- }
41274
- const bridge = getAbiBridge();
41275
- const fn2 = bridge.createAbiFunction(methodName);
41276
- fnDef.inputs.forEach((param, i2) => {
41277
- pushParam(fn2, param, args[i2], methodName);
41278
- });
41279
- return bytesToHex3(bridge.encode(fn2));
41280
- }
41281
- function pushParam(fn2, param, value, methodName) {
41282
- const type = param.type;
41283
- const isOutput = false;
41284
- if (type === "address") {
41285
- fn2.addParamAddress(toAddressBytes(value, param, methodName), isOutput);
41286
- return;
41287
- }
41288
- if (type === "bool") {
41289
- if (typeof value !== "boolean") {
41290
- throw argTypeMismatch(methodName, param, value, "boolean");
41291
- }
41292
- fn2.addParamBool(value, isOutput);
41293
- return;
41294
- }
41295
- if (type === "string") {
41296
- if (typeof value !== "string") {
41297
- throw argTypeMismatch(methodName, param, value, "string");
40783
+ fn2.addParamBool(value, isOutput);
40784
+ return;
40785
+ }
40786
+ if (type === "string") {
40787
+ if (typeof value !== "string") {
40788
+ throw argTypeMismatch(methodName, param, value, "string");
41298
40789
  }
41299
40790
  fn2.addParamString(value, isOutput);
41300
40791
  return;
@@ -41311,32 +40802,8 @@ function pushParam(fn2, param, value, methodName) {
41311
40802
  }
41312
40803
  const intMatch = /^(u?)int(\d*)$/.exec(type);
41313
40804
  if (intMatch) {
41314
- const isUnsigned = intMatch[1] === "u";
41315
- const bits = intMatch[2] ? Number(intMatch[2]) : 256;
41316
- if (bits < 8 || bits > 256 || bits % 8 !== 0) {
41317
- throw new TwakValidationError(
41318
- `Unsupported integer width ${type} for ${methodName}.${param.name} (expected 8..256, multiple of 8)`,
41319
- { kind: "argTypeMismatch", methodName, paramName: param.name, type }
41320
- );
41321
- }
41322
- const bigValue = toBigInt2(value, param, methodName);
41323
- if (isUnsigned) {
41324
- if (bigValue < 0n || bigValue >= 1n << BigInt(bits)) {
41325
- throw argTypeMismatch(methodName, param, value, `uint${bits} (0 <= x < 2^${bits})`);
41326
- }
41327
- } else {
41328
- const limit = 1n << BigInt(bits - 1);
41329
- if (bigValue < -limit || bigValue >= limit) {
41330
- throw argTypeMismatch(
41331
- methodName,
41332
- param,
41333
- value,
41334
- `int${bits} (-2^${bits - 1} <= x < 2^${bits - 1})`
41335
- );
41336
- }
41337
- }
41338
- const bytes = bigIntToBytes32(bigValue, isUnsigned);
41339
- if (isUnsigned) {
40805
+ const bytes = encodeIntWord(type, value, param, methodName);
40806
+ if (intMatch[1] === "u") {
41340
40807
  fn2.addParamUInt256(bytes, isOutput);
41341
40808
  } else {
41342
40809
  fn2.addParamInt256(bytes, isOutput);
@@ -41423,6 +40890,165 @@ function argTypeMismatch(methodName, param, value, expected) {
41423
40890
  }
41424
40891
  );
41425
40892
  }
40893
+ function encodeIntWord(type, value, param, methodName) {
40894
+ const intMatch = /^(u?)int(\d*)$/.exec(type);
40895
+ const isUnsigned = intMatch[1] === "u";
40896
+ const bits = intMatch[2] ? Number(intMatch[2]) : 256;
40897
+ if (bits < 8 || bits > 256 || bits % 8 !== 0) {
40898
+ throw new TwakValidationError(
40899
+ `Unsupported integer width ${type} for ${methodName}.${param.name} (expected 8..256, multiple of 8)`,
40900
+ { kind: "argTypeMismatch", methodName, paramName: param.name, type }
40901
+ );
40902
+ }
40903
+ const bigValue = toBigInt2(value, param, methodName);
40904
+ if (isUnsigned) {
40905
+ if (bigValue < 0n || bigValue >= 1n << BigInt(bits)) {
40906
+ throw argTypeMismatch(methodName, param, value, `uint${bits} (0 <= x < 2^${bits})`);
40907
+ }
40908
+ } else {
40909
+ const limit = 1n << BigInt(bits - 1);
40910
+ if (bigValue < -limit || bigValue >= limit) {
40911
+ throw argTypeMismatch(
40912
+ methodName,
40913
+ param,
40914
+ value,
40915
+ `int${bits} (-2^${bits - 1} <= x < 2^${bits - 1})`
40916
+ );
40917
+ }
40918
+ }
40919
+ return bigIntToBytes32(bigValue, isUnsigned);
40920
+ }
40921
+ function encoderRequiresHandRoll(params) {
40922
+ return params.some((p3) => p3.type === "tuple" || p3.type.endsWith("[]"));
40923
+ }
40924
+ function isDynamicType(param) {
40925
+ const t = param.type;
40926
+ if (t === "string" || t === "bytes") return true;
40927
+ if (t.endsWith("[]")) return true;
40928
+ if (t === "tuple") return (param.components ?? []).some(isDynamicType);
40929
+ return false;
40930
+ }
40931
+ function canonicalType(param) {
40932
+ const t = param.type;
40933
+ if (t === "tuple") return `(${(param.components ?? []).map(canonicalType).join(",")})`;
40934
+ if (t === "tuple[]") return `(${(param.components ?? []).map(canonicalType).join(",")})[]`;
40935
+ if (t.startsWith("tuple")) {
40936
+ throw new TwakValidationError(`Unsupported tuple array shape "${t}" for ${param.name}`, {
40937
+ kind: "argTypeMismatch",
40938
+ paramName: param.name,
40939
+ type: t
40940
+ });
40941
+ }
40942
+ return t;
40943
+ }
40944
+ function numberToWord(n4) {
40945
+ const out = new Uint8Array(32);
40946
+ let v5 = BigInt(n4);
40947
+ for (let i2 = 31; i2 >= 0; i2--) {
40948
+ out[i2] = Number(v5 & 0xffn);
40949
+ v5 >>= 8n;
40950
+ }
40951
+ return out;
40952
+ }
40953
+ function padRight32(bytes) {
40954
+ const out = new Uint8Array(Math.ceil(bytes.length / 32) * 32);
40955
+ out.set(bytes);
40956
+ return out;
40957
+ }
40958
+ function concatBytes3(parts) {
40959
+ const total = parts.reduce((acc, p3) => acc + p3.length, 0);
40960
+ const out = new Uint8Array(total);
40961
+ let offset = 0;
40962
+ for (const p3 of parts) {
40963
+ out.set(p3, offset);
40964
+ offset += p3.length;
40965
+ }
40966
+ return out;
40967
+ }
40968
+ function encodeHeadTail(items) {
40969
+ let headLen = 0;
40970
+ for (const it4 of items) headLen += it4.dynamic ? 32 : it4.bytes.length;
40971
+ const head = [];
40972
+ const tail = [];
40973
+ let offset = headLen;
40974
+ for (const it4 of items) {
40975
+ if (it4.dynamic) {
40976
+ head.push(numberToWord(offset));
40977
+ tail.push(it4.bytes);
40978
+ offset += it4.bytes.length;
40979
+ } else {
40980
+ head.push(it4.bytes);
40981
+ }
40982
+ }
40983
+ return concatBytes3([...head, ...tail]);
40984
+ }
40985
+ function encodeAbiValue(param, value, methodName) {
40986
+ const t = param.type;
40987
+ if (t.endsWith("[]")) {
40988
+ if (!Array.isArray(value)) {
40989
+ throw argTypeMismatch(methodName, param, value, `${t} (array)`);
40990
+ }
40991
+ const base3 = { ...param, type: t.slice(0, -2) };
40992
+ const items = value.map((v5) => encodeAbiValue(base3, v5, methodName));
40993
+ return {
40994
+ dynamic: true,
40995
+ bytes: concatBytes3([numberToWord(value.length), encodeHeadTail(items)])
40996
+ };
40997
+ }
40998
+ if (t === "tuple") {
40999
+ const components = param.components ?? [];
41000
+ if (!Array.isArray(value) || value.length !== components.length) {
41001
+ throw argTypeMismatch(methodName, param, value, `tuple of ${components.length} components`);
41002
+ }
41003
+ const items = components.map((c3, i2) => encodeAbiValue(c3, value[i2], methodName));
41004
+ return { dynamic: isDynamicType(param), bytes: encodeHeadTail(items) };
41005
+ }
41006
+ if (t === "string") {
41007
+ if (typeof value !== "string") {
41008
+ throw argTypeMismatch(methodName, param, value, "string");
41009
+ }
41010
+ const b3 = new TextEncoder().encode(value);
41011
+ return { dynamic: true, bytes: concatBytes3([numberToWord(b3.length), padRight32(b3)]) };
41012
+ }
41013
+ if (t === "bytes") {
41014
+ const b3 = toBytes4(value, param, methodName);
41015
+ return { dynamic: true, bytes: concatBytes3([numberToWord(b3.length), padRight32(b3)]) };
41016
+ }
41017
+ if (t === "address") {
41018
+ const word = new Uint8Array(32);
41019
+ word.set(toAddressBytes(value, param, methodName), 12);
41020
+ return { dynamic: false, bytes: word };
41021
+ }
41022
+ const bytesNMatch = /^bytes(\d+)$/.exec(t);
41023
+ if (bytesNMatch) {
41024
+ const size3 = Number(bytesNMatch[1]);
41025
+ return { dynamic: false, bytes: padRight32(toBytesFix(value, size3, param, methodName)) };
41026
+ }
41027
+ if (/^u?int\d*$/.test(t)) {
41028
+ return { dynamic: false, bytes: encodeIntWord(t, value, param, methodName) };
41029
+ }
41030
+ if (t === "bool") {
41031
+ if (typeof value !== "boolean") {
41032
+ throw argTypeMismatch(methodName, param, value, "boolean");
41033
+ }
41034
+ return { dynamic: false, bytes: numberToWord(value ? 1 : 0) };
41035
+ }
41036
+ throw new TwakValidationError(
41037
+ `Unsupported ABI param type "${t}" for ${methodName}.${param.name}`,
41038
+ {
41039
+ kind: "argTypeMismatch",
41040
+ methodName,
41041
+ paramName: param.name,
41042
+ type: t
41043
+ }
41044
+ );
41045
+ }
41046
+ function encodeFunctionCallHandRoll(methodName, inputs, args) {
41047
+ const signature = `${methodName}(${inputs.map(canonicalType).join(",")})`;
41048
+ const selector = getAbiBridge().keccak256(new TextEncoder().encode(signature)).slice(0, 4);
41049
+ const body = encodeHeadTail(inputs.map((p3, i2) => encodeAbiValue(p3, args[i2], methodName)));
41050
+ return bytesToHex3(concatBytes3([selector, body]));
41051
+ }
41426
41052
  function decodeFunctionResult(abi, methodName, returnHex) {
41427
41053
  const fnDef = findAbiFunction(abi, methodName);
41428
41054
  if (fnDef.outputs.length === 0) return void 0;
@@ -41806,14 +41432,14 @@ function buildWriteProxy(abi, address, chainKey, client) {
41806
41432
  });
41807
41433
  }
41808
41434
  const data = encodeFunctionCall(abi, prop, args);
41809
- const txHash = await signer.sendTransaction({
41435
+ const hash = await signer.sendTransaction({
41810
41436
  chainId: chainKeyToCaip2(chainKey),
41811
41437
  to: address,
41812
41438
  data,
41813
41439
  value: "0x0"
41814
41440
  });
41815
41441
  return makeContractPendingTransaction({
41816
- txHash,
41442
+ hash,
41817
41443
  abi,
41818
41444
  address,
41819
41445
  chainKey,
@@ -41825,22 +41451,22 @@ function buildWriteProxy(abi, address, chainKey, client) {
41825
41451
  });
41826
41452
  }
41827
41453
  function makeContractPendingTransaction(opts) {
41828
- const { txHash, abi, address, chainKey, client, methodName } = opts;
41454
+ const { hash, abi, address, chainKey, client, methodName } = opts;
41829
41455
  return {
41830
- txHash,
41456
+ hash,
41831
41457
  async wait(waitOpts) {
41832
- const receipt = await client.waitForReceipt(chainKey, txHash, {
41458
+ const receipt = await client.waitForReceipt(chainKey, hash, {
41833
41459
  timeoutMs: waitOpts?.timeoutMs
41834
41460
  });
41835
41461
  if (receipt.status === "0x0") {
41836
41462
  throw new TwakError(
41837
41463
  `Transaction reverted on-chain: ${receipt.transactionHash}`,
41838
41464
  "TX_FAILED",
41839
- { txHash: receipt.transactionHash, methodName, receipt }
41465
+ { hash: receipt.transactionHash, methodName, receipt }
41840
41466
  );
41841
41467
  }
41842
41468
  return {
41843
- txHash: receipt.transactionHash,
41469
+ hash: receipt.transactionHash,
41844
41470
  blockNumber: hexToSafeNumber(receipt.blockNumber, "blockNumber"),
41845
41471
  events: extractEvents(abi, receipt.logs, address)
41846
41472
  };
@@ -41937,6 +41563,17 @@ function lookupAddress(id, addresses, chainKey) {
41937
41563
  }
41938
41564
  return address;
41939
41565
  }
41566
+ function eip191Digest(message) {
41567
+ const messageBytes = new TextEncoder().encode(message);
41568
+ const prefixBytes = new TextEncoder().encode(
41569
+ `Ethereum Signed Message:
41570
+ ${messageBytes.length}`
41571
+ );
41572
+ const data = new Uint8Array(prefixBytes.length + messageBytes.length);
41573
+ data.set(prefixBytes);
41574
+ data.set(messageBytes, prefixBytes.length);
41575
+ return bytesToHex3(getAbiBridge().keccak256(data));
41576
+ }
41940
41577
  var ERC20_ABI = [
41941
41578
  {
41942
41579
  type: "function",
@@ -42005,6 +41642,25 @@ var ERC8004_ABI = [
42005
41642
  outputs: [{ name: "agentId", type: "uint256" }],
42006
41643
  stateMutability: "nonpayable"
42007
41644
  },
41645
+ {
41646
+ // register(agentURI, metadata[]) — mints + sets metadata atomically in one
41647
+ // tx. metadata is an array of (key, value) where value is raw bytes.
41648
+ type: "function",
41649
+ name: "register",
41650
+ inputs: [
41651
+ { name: "agentURI", type: "string" },
41652
+ {
41653
+ name: "metadata",
41654
+ type: "tuple[]",
41655
+ components: [
41656
+ { name: "key", type: "string" },
41657
+ { name: "value", type: "bytes" }
41658
+ ]
41659
+ }
41660
+ ],
41661
+ outputs: [{ name: "agentId", type: "uint256" }],
41662
+ stateMutability: "nonpayable"
41663
+ },
42008
41664
  {
42009
41665
  type: "function",
42010
41666
  name: "setAgentURI",
@@ -42100,7 +41756,8 @@ var ERC8004_ABI = [
42100
41756
  }
42101
41757
  ];
42102
41758
  var ERC8004_ADDRESSES = {
42103
- bsc: "0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"
41759
+ bsc: "0x8004a169fb4a3325136eb29fa0ceb6d2e539a432",
41760
+ bsctestnet: "0x8004a818bfb912233c491871b3d84c89a494bd9e"
42104
41761
  };
42105
41762
  var erc8004 = defineContract({
42106
41763
  id: "erc8004",
@@ -42496,6 +42153,29 @@ var ERC8183_POLICY_ABI = [
42496
42153
  outputs: [],
42497
42154
  stateMutability: "nonpayable"
42498
42155
  },
42156
+ {
42157
+ type: "function",
42158
+ name: "disputeWindow",
42159
+ inputs: [],
42160
+ outputs: [{ name: "", type: "uint64" }],
42161
+ stateMutability: "view"
42162
+ },
42163
+ {
42164
+ // When the policy recorded the job's submission (0 if not submitted).
42165
+ type: "function",
42166
+ name: "submittedAt",
42167
+ inputs: [{ name: "jobId", type: "uint256" }],
42168
+ outputs: [{ name: "", type: "uint64" }],
42169
+ stateMutability: "view"
42170
+ },
42171
+ {
42172
+ // Vote quorum snapshotted for the job when its dispute was raised.
42173
+ type: "function",
42174
+ name: "disputeQuorumSnapshot",
42175
+ inputs: [{ name: "jobId", type: "uint256" }],
42176
+ outputs: [{ name: "", type: "uint16" }],
42177
+ stateMutability: "view"
42178
+ },
42499
42179
  {
42500
42180
  type: "function",
42501
42181
  name: "disputed",
@@ -42547,13 +42227,16 @@ var ERC8183_POLICY_ABI = [
42547
42227
  }
42548
42228
  ];
42549
42229
  var ERC8183_COMMERCE_ADDRESSES = {
42550
- bsc: "0xea4daa3100a767e86fded867729ae7446476eba6"
42230
+ bsc: "0xea4daa3100a767e86fded867729ae7446476eba6",
42231
+ bsctestnet: "0xa206c0517b6371c6638cd9e4a42cc9f02a33b0de"
42551
42232
  };
42552
42233
  var ERC8183_ROUTER_ADDRESSES = {
42553
- bsc: "0x51895229e12f9876011789b04f8698af06ccd6da"
42234
+ bsc: "0x51895229e12f9876011789b04f8698af06ccd6da",
42235
+ bsctestnet: "0xd7d36d66d2f1b608a0f943f722d27e3744f66f25"
42554
42236
  };
42555
42237
  var ERC8183_POLICY_ADDRESSES = {
42556
- bsc: "0x9c01845705b3078aa2e8cff7520a6376fd766de5"
42238
+ bsc: "0x9c01845705b3078aa2e8cff7520a6376fd766de5",
42239
+ bsctestnet: "0x4f4678d4439fec812ac7674bb3efb4c8f5fb78a6"
42557
42240
  };
42558
42241
  var erc8183Commerce = defineContract({
42559
42242
  id: "erc8183-commerce",
@@ -42604,8 +42287,640 @@ function resolveErc8183Contracts(client, chainInput) {
42604
42287
  };
42605
42288
  }
42606
42289
 
42290
+ // src/branding.ts
42291
+ var import_chalk5 = __toESM(require_source());
42292
+ var boxen = require_boxen();
42293
+ var SHIELD = [
42294
+ " @@=+ ",
42295
+ " @@@@@@@======= ",
42296
+ " @@@@@@@@@@@@@============+ ",
42297
+ " @@@@@@@@@@@@@@@@================ ",
42298
+ " @@@@@@@@@@@@@@@@+=============== ",
42299
+ " @@@@@@@@@@@@@@@@++++============ ",
42300
+ " @@@@@@@@@@@@@@@@++++++++++=====+ ",
42301
+ " @@@@@@@@@@@@@@@@**++++++++++++++ ",
42302
+ " @@@@@@@@@@@@@@@@********++++++++ ",
42303
+ " @@@@@@@@@@@@@@@@**************++ ",
42304
+ " @@@@@@@@@@@@@@@@#####*********** ",
42305
+ " @@@@@@@@@@@@@@@###########**** ",
42306
+ " @@@@@@@@@@@@@@%############## ",
42307
+ " @@@@@@@@@@@@@@%%%%%%%####### ",
42308
+ " @@@@@@@@@@@@@%%%%%%%%%%%%% ",
42309
+ " @@@@@@@@@@@%%%%%%%%%%% ",
42310
+ " @@@@@@@@@@@@@%%%%% ",
42311
+ " @@@@@@@@@@@@@@ ",
42312
+ " @@@@@@@@@@ ",
42313
+ " @@ "
42314
+ ];
42315
+ function printBanner(version3) {
42316
+ const logo = SHIELD.map((l4) => import_chalk5.default.green(l4)).join("\n");
42317
+ const title = import_chalk5.default.green.bold(" Trust Wallet Agent Kit");
42318
+ const ver = import_chalk5.default.dim(` v${version3}`);
42319
+ console.log(
42320
+ boxen(`${logo}
42321
+
42322
+ ${title}
42323
+ ${ver}`, {
42324
+ borderColor: "green",
42325
+ borderStyle: "round",
42326
+ padding: { top: 1, bottom: 1, left: 2, right: 2 }
42327
+ })
42328
+ );
42329
+ }
42330
+ function box(content) {
42331
+ return boxen(content, {
42332
+ borderColor: "green",
42333
+ borderStyle: "round",
42334
+ padding: { top: 0, bottom: 0, left: 1, right: 1 }
42335
+ });
42336
+ }
42337
+
42338
+ // src/commands/wallet.ts
42339
+ function getQRString(address) {
42340
+ try {
42341
+ const qrcode = require("qrcode-terminal");
42342
+ let result = "";
42343
+ qrcode.generate(address, { small: true }, (code2) => {
42344
+ result = code2;
42345
+ });
42346
+ return result;
42347
+ } catch {
42348
+ return "";
42349
+ }
42350
+ }
42351
+ function registerWalletCommand(program3) {
42352
+ const wallet = program3.command("wallet").description("Manage agent wallet");
42353
+ wallet.command("create").description("Create a new agent wallet").requiredOption("--password <password>", "Password to encrypt the wallet").option("--no-keychain", "Do not save password to OS keychain").option("--skip-password-check", "Skip password strength validation").option("--json", "JSON output").action(async (opts) => {
42354
+ try {
42355
+ if (!opts.skipPasswordCheck) {
42356
+ const weakness = getPasswordWeaknesses(opts.password);
42357
+ if (weakness) {
42358
+ throw new WalletError("VALIDATION_ERROR", `Password too weak: must ${weakness}.`);
42359
+ }
42360
+ }
42361
+ await ensureTrustSDK();
42362
+ const store = new WalletConfigStore();
42363
+ if (await store.exists()) {
42364
+ throw new WalletError(
42365
+ "WALLET_EXISTS",
42366
+ "Wallet already exists. Back up ~/.twak/wallet.json, then delete it to create a new one."
42367
+ );
42368
+ }
42369
+ const provider = await AgentWalletProvider.create(opts.password);
42370
+ const addresses = await provider.getAddresses();
42371
+ if (opts.keychain !== false) {
42372
+ const kc2 = new KeychainManager();
42373
+ if (kc2.isAvailable()) {
42374
+ await kc2.saveWalletPassword(opts.password);
42375
+ if (!opts.json) info("Password saved to OS keychain (use --no-keychain to skip)");
42376
+ }
42377
+ }
42378
+ if (!opts.json) success("Agent wallet created successfully");
42379
+ try {
42380
+ const creds = await getCredentials();
42381
+ await registerWalletWithBackend(creds, provider);
42382
+ if (!opts.json) info("Wallet registered with backend");
42383
+ } catch {
42384
+ if (!opts.json) warn("Backend registration failed (holdings may be limited)");
42385
+ }
42386
+ if (opts.json) {
42387
+ output({ addresses }, true);
42388
+ } else {
42389
+ info(`Generated addresses for ${addresses.length} chains`);
42390
+ }
42391
+ } catch (err) {
42392
+ handleCommandError(err, opts.json, "Wallet creation failed");
42393
+ }
42394
+ safeExit();
42395
+ });
42396
+ wallet.command("address").description("Get wallet address for a chain").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").option(
42397
+ "--password <password>",
42398
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
42399
+ ).option("--json", "JSON output").action(async (opts) => {
42400
+ try {
42401
+ if (!getChainById(opts.chain)) {
42402
+ throw new WalletError("CHAIN_UNSUPPORTED", `Unknown chain: ${opts.chain}`);
42403
+ }
42404
+ await ensureTrustSDK();
42405
+ const password3 = await resolvePassword(opts);
42406
+ const provider = await AgentWalletProvider.fromStorage(password3);
42407
+ const address = await provider.getAddress(opts.chain);
42408
+ if (opts.json) {
42409
+ output({ chain: opts.chain, address }, true);
42410
+ } else {
42411
+ const qr3 = getQRString(address);
42412
+ const content = `${import_chalk6.default.dim("Chain")} ${import_chalk6.default.bold(opts.chain)}
42413
+ ${import_chalk6.default.dim("Address")} ${import_chalk6.default.green(address)}
42414
+
42415
+ ${qr3}`;
42416
+ console.log(box(content));
42417
+ }
42418
+ } catch (err) {
42419
+ handleCommandError(err, opts.json, "Wallet address failed");
42420
+ }
42421
+ safeExit();
42422
+ });
42423
+ wallet.command("addresses").description("List all wallet addresses").option(
42424
+ "--password <password>",
42425
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
42426
+ ).option("--json", "JSON output").action(async (opts) => {
42427
+ try {
42428
+ await ensureTrustSDK();
42429
+ const password3 = await resolvePassword(opts);
42430
+ const provider = await AgentWalletProvider.fromStorage(password3);
42431
+ const addresses = await provider.getAddresses();
42432
+ if (opts.json) {
42433
+ output({ addresses }, true);
42434
+ } else {
42435
+ const grouped = /* @__PURE__ */ new Map();
42436
+ for (const addr of addresses) {
42437
+ const chains = grouped.get(addr.address) ?? [];
42438
+ chains.push(addr.chainId);
42439
+ grouped.set(addr.address, chains);
42440
+ }
42441
+ for (const [address, chains] of grouped) {
42442
+ const qr3 = getQRString(address);
42443
+ const maxWidth = Math.max(address.length + 9, 50);
42444
+ const chainLabel = `${import_chalk6.default.dim("Chains")} `;
42445
+ const chainLabelLen = 9;
42446
+ const chainLines = [];
42447
+ let line = "";
42448
+ for (const c3 of chains) {
42449
+ const sep2 = line ? ", " : "";
42450
+ if (line && line.length + sep2.length + c3.length + chainLabelLen > maxWidth) {
42451
+ chainLines.push(line);
42452
+ line = c3;
42453
+ } else {
42454
+ line += sep2 + c3;
42455
+ }
42456
+ }
42457
+ if (line) chainLines.push(line);
42458
+ const chainsStr = chainLines.map((l4, i2) => (i2 === 0 ? chainLabel : " ".repeat(chainLabelLen)) + import_chalk6.default.bold(l4)).join("\n");
42459
+ const content = `${chainsStr}
42460
+ ${import_chalk6.default.dim("Address")} ${import_chalk6.default.green(address)}
42461
+
42462
+ ${qr3}`;
42463
+ console.log(box(content));
42464
+ }
42465
+ }
42466
+ } catch (err) {
42467
+ handleCommandError(err, opts.json, "Wallet addresses failed");
42468
+ }
42469
+ safeExit();
42470
+ });
42471
+ wallet.command("balance").description("Get wallet balance on a chain (native + token holdings)").option("--chain <key>", "Chain key").option("--all", "Show balances across all chains with funds").option(
42472
+ "--password <password>",
42473
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
42474
+ ).option("--no-tokens", "Skip token balance lookup").option("--json", "JSON output").action(async (opts) => {
42475
+ try {
42476
+ if (opts.all && opts.chain) {
42477
+ throw new WalletError("VALIDATION_ERROR", "--all and --chain are mutually exclusive");
42478
+ }
42479
+ if (!opts.all && !opts.chain) {
42480
+ throw new WalletError("VALIDATION_ERROR", "Provide --chain <key> or --all");
42481
+ }
42482
+ await ensureTrustSDK();
42483
+ const password3 = await resolvePassword(opts);
42484
+ const provider = await AgentWalletProvider.fromStorage(password3);
42485
+ const httpClient = await getClient();
42486
+ if (!opts.all) {
42487
+ const chain2 = getChainById(opts.chain);
42488
+ if (!chain2) {
42489
+ throw new WalletError("CHAIN_UNSUPPORTED", `Unknown chain: ${opts.chain}`);
42490
+ }
42491
+ }
42492
+ const allAddresses = await provider.getAddresses();
42493
+ const addressMap = /* @__PURE__ */ new Map();
42494
+ for (const a3 of allAddresses) addressMap.set(a3.chainId, a3.address);
42495
+ const requestedChains = opts.all ? Object.keys(CHAINS) : [opts.chain];
42496
+ const skipTokens = opts.tokens === false;
42497
+ const chainEntries = requestedChains.flatMap((chainKey) => {
42498
+ const chain2 = getChainById(chainKey);
42499
+ const address = addressMap.get(chainKey);
42500
+ if (!chain2 || !address) return [];
42501
+ return [{ chainKey, chain: chain2, address }];
42502
+ });
42503
+ if (!opts.all && chainEntries.length === 0) {
42504
+ throw new WalletError(
42505
+ "CHAIN_UNSUPPORTED",
42506
+ `Wallet does not have an address for chain: ${opts.chain}`
42507
+ );
42508
+ }
42509
+ const nativeAssets = chainEntries.map(({ chainKey, chain: chain2, address }) => ({
42510
+ chainKey,
42511
+ chain: chain2,
42512
+ token: void 0,
42513
+ assetId: `c${chain2.coinId}`,
42514
+ userAddress: address
42515
+ }));
42516
+ const tokenAssets = skipTokens ? [] : chainEntries.flatMap(({ chainKey, chain: chain2, address }) => {
42517
+ const knownTokens = COMMON_TOKENS[chainKey];
42518
+ if (!knownTokens) return [];
42519
+ return Object.keys(knownTokens).flatMap((sym) => {
42520
+ const resolved = resolveToken(sym, chainKey);
42521
+ if (!resolved) return [];
42522
+ return [
42523
+ {
42524
+ chainKey,
42525
+ chain: chain2,
42526
+ token: { ...resolved, symbol: sym },
42527
+ assetId: `c${chain2.coinId}_t${resolved.address}`,
42528
+ userAddress: address
42529
+ }
42530
+ ];
42531
+ });
42532
+ });
42533
+ const allAssets = [...nativeAssets, ...tokenAssets];
42534
+ const balances = await fetchBalances(
42535
+ httpClient,
42536
+ allAssets.map(({ assetId, userAddress }) => ({ assetId, address: userAddress }))
42537
+ );
42538
+ const assetsById = new Map(allAssets.map((a3) => [a3.assetId, a3]));
42539
+ const resultsByChain = /* @__PURE__ */ new Map();
42540
+ for (const { chainKey, chain: chain2, address } of chainEntries) {
42541
+ resultsByChain.set(chainKey, {
42542
+ chain: chainKey,
42543
+ address,
42544
+ symbol: chain2.symbol,
42545
+ available: "0",
42546
+ total: "0",
42547
+ totalUsd: null,
42548
+ tokens: []
42549
+ });
42550
+ }
42551
+ for (const balance of balances) {
42552
+ const assetInfo = assetsById.get(balance.assetId);
42553
+ if (!assetInfo) {
42554
+ throw new WalletError(
42555
+ "NETWORK_ERROR",
42556
+ "Could not fetch balances. Please try again later."
42557
+ );
42558
+ }
42559
+ const result = resultsByChain.get(assetInfo.chainKey);
42560
+ if (!result) continue;
42561
+ const { token, chain: chain2 } = assetInfo;
42562
+ const amts = balance.amounts;
42563
+ if (token) {
42564
+ const human = formatAmount(amts.total ?? "0", token.decimals);
42565
+ if (human !== "0") {
42566
+ result.tokens.push({
42567
+ symbol: token.symbol,
42568
+ contract: token.address,
42569
+ balance: human
42570
+ });
42571
+ }
42572
+ } else {
42573
+ result.available = formatAmount(amts.available ?? "0", chain2.decimals);
42574
+ result.total = formatAmount(amts.total ?? "0", chain2.decimals);
42575
+ const lockedHuman = formatAmount(amts.locked ?? "0", chain2.decimals);
42576
+ if (lockedHuman !== "0") result.staked = lockedHuman;
42577
+ const fiat = amts.totalInFiat;
42578
+ result.totalUsd = fiat && fiat !== "" ? parseFloat(fiat) : null;
42579
+ }
42580
+ }
42581
+ const orderedResults = chainEntries.map((e2) => resultsByChain.get(e2.chainKey)).filter((r3) => r3 !== void 0);
42582
+ const visible = opts.all ? orderedResults.filter((r3) => r3.total !== "0" || r3.tokens.length > 0) : orderedResults;
42583
+ const renderBlock = (r3, withSeparator) => {
42584
+ if (withSeparator) console.log("");
42585
+ console.log(import_chalk6.default.bold.underline(`${r3.chain} (${r3.symbol})`));
42586
+ console.log(` ${import_chalk6.default.dim("Address")} ${r3.address}`);
42587
+ console.log(` ${import_chalk6.default.dim("Available")} ${r3.available}`);
42588
+ if (r3.staked) console.log(` ${import_chalk6.default.dim("Staked")} ${r3.staked}`);
42589
+ const usdSuffix = r3.totalUsd !== null ? ` (\u2248 $${r3.totalUsd.toFixed(2)})` : "";
42590
+ console.log(` ${import_chalk6.default.dim("Total")} ${r3.total}${usdSuffix}`);
42591
+ if (r3.tokens.length > 0) {
42592
+ console.log(` ${import_chalk6.default.dim("Tokens:")}`);
42593
+ for (const t of r3.tokens) {
42594
+ console.log(` ${import_chalk6.default.bold(t.symbol.padEnd(10))} ${t.balance}`);
42595
+ }
42596
+ }
42597
+ };
42598
+ if (opts.all) {
42599
+ if (opts.json) {
42600
+ output(visible, true);
42601
+ } else if (visible.length === 0) {
42602
+ info("No chains with non-zero native balance found.");
42603
+ } else {
42604
+ visible.forEach((r3, i2) => renderBlock(r3, i2 > 0));
42605
+ }
42606
+ } else {
42607
+ const single = visible[0];
42608
+ if (opts.json) {
42609
+ output(single, true);
42610
+ } else {
42611
+ renderBlock(single, false);
42612
+ }
42613
+ }
42614
+ } catch (err) {
42615
+ handleCommandError(err, opts.json, "Balance check failed");
42616
+ }
42617
+ safeExit();
42618
+ });
42619
+ const keychain = wallet.command("keychain").description("Manage wallet password in OS keychain");
42620
+ keychain.command("save").description("Save wallet password to OS keychain").requiredOption("--password <password>", "Wallet password to store").option("--json", "JSON output").action(async (opts) => {
42621
+ try {
42622
+ const kc2 = new KeychainManager();
42623
+ if (!kc2.isAvailable()) {
42624
+ throw new WalletError("KEYCHAIN_UNAVAILABLE", "OS keychain not available on this system");
42625
+ }
42626
+ await kc2.saveWalletPassword(opts.password);
42627
+ success("Wallet password saved to OS keychain");
42628
+ } catch (err) {
42629
+ handleCommandError(err, opts.json, "Keychain save failed");
42630
+ }
42631
+ safeExit();
42632
+ });
42633
+ keychain.command("delete").description("Remove wallet password from OS keychain").option("--json", "JSON output").action(async (opts) => {
42634
+ try {
42635
+ const kc2 = new KeychainManager();
42636
+ if (!kc2.isAvailable()) {
42637
+ throw new WalletError("KEYCHAIN_UNAVAILABLE", "OS keychain not available on this system");
42638
+ }
42639
+ await kc2.deleteWalletPassword();
42640
+ success("Wallet password removed from OS keychain");
42641
+ } catch (err) {
42642
+ handleCommandError(err, opts.json, "Keychain delete failed");
42643
+ }
42644
+ safeExit();
42645
+ });
42646
+ keychain.command("check").description("Check if wallet password is stored in OS keychain").option("--json", "JSON output").action(async (opts) => {
42647
+ try {
42648
+ const kc2 = new KeychainManager();
42649
+ if (!kc2.isAvailable()) {
42650
+ if (opts.json) {
42651
+ output({ available: false, stored: false }, true);
42652
+ } else {
42653
+ warn("keytar not available on this system");
42654
+ }
42655
+ safeExit();
42656
+ return;
42657
+ }
42658
+ const pw = await kc2.getWalletPassword();
42659
+ if (opts.json) {
42660
+ output({ available: true, stored: !!pw }, true);
42661
+ } else if (pw) {
42662
+ success("Wallet password is stored in OS keychain");
42663
+ } else {
42664
+ info("No wallet password found in OS keychain");
42665
+ }
42666
+ } catch (err) {
42667
+ handleCommandError(err, opts.json, "Keychain check failed");
42668
+ }
42669
+ safeExit();
42670
+ });
42671
+ wallet.command("connect").description("Connect external wallet via WalletConnect").option("--project-id <id>", "WalletConnect project ID (or set WALLETCONNECT_PROJECT_ID)").option(
42672
+ "--chains <list>",
42673
+ "Comma-separated CAIP-2 chain IDs",
42674
+ "eip155:1,eip155:56,eip155:137,eip155:8453,eip155:42161"
42675
+ ).option("--timeout <seconds>", "Connection timeout in seconds", "300").option("--json", "JSON output").action(async (opts) => {
42676
+ try {
42677
+ const projectId = opts.projectId || process.env.WALLETCONNECT_PROJECT_ID;
42678
+ if (!projectId) {
42679
+ throw new WalletError(
42680
+ "VALIDATION_ERROR",
42681
+ "WalletConnect project ID required. Use --project-id or set WALLETCONNECT_PROJECT_ID"
42682
+ );
42683
+ }
42684
+ const provider = new WalletConnectProvider({ projectId });
42685
+ const chains = opts.chains.split(",").map((s2) => s2.trim()).filter(Boolean);
42686
+ info("Connecting via WalletConnect...");
42687
+ const result = await provider.connect(chains);
42688
+ const qr3 = getQRString(result.uri);
42689
+ if (qr3) {
42690
+ console.log(qr3);
42691
+ }
42692
+ info(`Trust Wallet: ${result.deepLinks.native}`);
42693
+ info(`Universal: ${result.deepLinks.universal}`);
42694
+ info("\nWaiting for wallet approval...");
42695
+ const timeoutMs = parseInt(opts.timeout, 10) * 1e3;
42696
+ const session = await provider.waitForSession(timeoutMs);
42697
+ if (opts.json) {
42698
+ output(
42699
+ {
42700
+ connected: true,
42701
+ address: session.address,
42702
+ chainId: session.chainId,
42703
+ peer: session.peerName
42704
+ },
42705
+ true
42706
+ );
42707
+ } else {
42708
+ success(`Connected to ${session.peerName || "wallet"}`);
42709
+ info(` Address: ${session.address}`);
42710
+ info(` Chain: ${session.chainId}`);
42711
+ }
42712
+ } catch (err) {
42713
+ handleCommandError(err, opts.json, "WalletConnect failed");
42714
+ }
42715
+ safeExit();
42716
+ });
42717
+ wallet.command("status").description("Check wallet configuration status").option("--json", "JSON output").action(async (opts) => {
42718
+ try {
42719
+ const store = new WalletConfigStore();
42720
+ const exists = await store.exists();
42721
+ const kc2 = new KeychainManager();
42722
+ const keychainHasPassword = kc2.isAvailable() ? !!await kc2.getWalletPassword() : false;
42723
+ const totalChains = Object.keys(CHAINS).length;
42724
+ let createdAt;
42725
+ let addressCount;
42726
+ if (exists) {
42727
+ const config = await store.getConfig();
42728
+ createdAt = config?.createdAt;
42729
+ addressCount = config?.chains?.length ?? totalChains;
42730
+ }
42731
+ const result = {
42732
+ agentWallet: exists ? "configured" : "not configured",
42733
+ keychainPassword: keychainHasPassword ? "stored" : "not stored",
42734
+ chains: totalChains,
42735
+ supportedChains: totalChains,
42736
+ ...createdAt ? { createdAt } : {},
42737
+ ...addressCount !== void 0 ? { addressCount } : {}
42738
+ };
42739
+ if (opts.json) {
42740
+ output(result, true);
42741
+ } else {
42742
+ console.log(
42743
+ ` ${import_chalk6.default.dim("Wallet")} ${exists ? import_chalk6.default.green("configured") : import_chalk6.default.yellow("not configured")}`
42744
+ );
42745
+ if (createdAt) console.log(` ${import_chalk6.default.dim("Created")} ${createdAt}`);
42746
+ console.log(
42747
+ ` ${import_chalk6.default.dim("Keychain")} ${keychainHasPassword ? import_chalk6.default.green("password stored") : import_chalk6.default.yellow("no password")}`
42748
+ );
42749
+ console.log(` ${import_chalk6.default.dim("Chains")} ${totalChains} supported`);
42750
+ if (addressCount !== void 0)
42751
+ console.log(` ${import_chalk6.default.dim("Addresses")} ${addressCount}`);
42752
+ if (!exists) info("\nRun `twak wallet create --password <pw>` to create a wallet.");
42753
+ }
42754
+ } catch (err) {
42755
+ handleCommandError(err, opts.json, "Status failed");
42756
+ }
42757
+ safeExit();
42758
+ });
42759
+ wallet.command("portfolio").description("Show portfolio balances and USD values across chains, including token holdings").option("--chains <list>", "Comma-separated chain keys (default: all supported chains)").option(
42760
+ "--password <password>",
42761
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
42762
+ ).option("--json", "JSON output").action(async (opts) => {
42763
+ try {
42764
+ await ensureTrustSDK();
42765
+ const password3 = await resolvePassword(opts);
42766
+ const provider = await AgentWalletProvider.fromStorage(password3);
42767
+ const httpClient = await getClient();
42768
+ const allAddresses = await provider.getAddresses();
42769
+ const chainsArg = opts.chains ?? Object.keys(CHAINS).join(",");
42770
+ const requestedChains = chainsArg.split(",").map((s2) => s2.trim()).filter(Boolean);
42771
+ if (requestedChains.length === 0) {
42772
+ throw new WalletError("VALIDATION_ERROR", "--chains must include at least one chain key");
42773
+ }
42774
+ const chainEntries = requestedChains.flatMap((chainKey) => {
42775
+ const chain2 = getChainById(chainKey);
42776
+ const addrEntry = allAddresses.find((a3) => a3.chainId === chainKey);
42777
+ if (!chain2 || !addrEntry) return [];
42778
+ return [{ chainKey, chain: chain2, address: addrEntry.address }];
42779
+ });
42780
+ if (chainEntries.length === 0) {
42781
+ throw new WalletError(
42782
+ "CHAIN_UNSUPPORTED",
42783
+ `No supported chains found in --chains "${opts.chains}"`
42784
+ );
42785
+ }
42786
+ const chainAssets = chainEntries.map(({ chainKey, chain: chain2, address }) => ({
42787
+ chainKey,
42788
+ chain: chain2,
42789
+ token: void 0,
42790
+ assetId: `c${chain2.coinId}`,
42791
+ userAddress: address
42792
+ }));
42793
+ const tokenAssets = chainEntries.flatMap(({ chainKey, chain: chain2, address }) => {
42794
+ const knownTokens = COMMON_TOKENS[chainKey];
42795
+ if (!knownTokens) return [];
42796
+ return Object.keys(knownTokens).flatMap((sym) => {
42797
+ const resolved = resolveToken(sym, chainKey);
42798
+ if (!resolved) return [];
42799
+ return [
42800
+ {
42801
+ chainKey,
42802
+ chain: chain2,
42803
+ token: { ...resolved, symbol: sym },
42804
+ assetId: `c${chain2.coinId}_t${resolved.address}`,
42805
+ userAddress: address
42806
+ }
42807
+ ];
42808
+ });
42809
+ });
42810
+ const allAssets = [...chainAssets, ...tokenAssets];
42811
+ const balances = await fetchBalances(
42812
+ httpClient,
42813
+ allAssets.map(({ assetId, userAddress }) => ({ assetId, address: userAddress }))
42814
+ );
42815
+ const assetsById = new Map(allAssets.map((a3) => [a3.assetId, a3]));
42816
+ const allRows = [];
42817
+ for (const balance of balances) {
42818
+ const assetInfo = assetsById.get(balance.assetId);
42819
+ if (!assetInfo) {
42820
+ throw new WalletError(
42821
+ "NETWORK_ERROR",
42822
+ "Could not fetch balances. Please try again later."
42823
+ );
42824
+ }
42825
+ const { token, chain: chain2, chainKey, userAddress } = assetInfo;
42826
+ const decimals = token ? token.decimals : chain2.decimals;
42827
+ const symbol = token ? token.symbol ?? token.address : chain2.symbol;
42828
+ const isToken = token !== void 0;
42829
+ const humanAmount = formatAmount(balance.amounts.total, decimals);
42830
+ const fiatRaw = balance.amounts.totalInFiat;
42831
+ const usdValue = fiatRaw && fiatRaw !== "" ? parseFloat(fiatRaw) : null;
42832
+ allRows.push({
42833
+ chain: chainKey,
42834
+ type: isToken ? "token" : "native",
42835
+ symbol,
42836
+ address: userAddress,
42837
+ ...token ? { contract: token.address } : {},
42838
+ balance: humanAmount,
42839
+ usdValue
42840
+ });
42841
+ }
42842
+ const rows = allRows.filter((r3) => r3.type === "native" || r3.balance !== "0");
42843
+ rows.sort((a3, b3) => {
42844
+ const chainCmp = String(a3.chain).localeCompare(String(b3.chain));
42845
+ if (chainCmp !== 0) return chainCmp;
42846
+ return String(a3.type).localeCompare(String(b3.type));
42847
+ });
42848
+ if (opts.json) {
42849
+ output(rows, true);
42850
+ } else {
42851
+ const totalUsd = rows.reduce(
42852
+ (sum, r3) => sum + (typeof r3.usdValue === "number" ? r3.usdValue : 0),
42853
+ 0
42854
+ );
42855
+ console.log("Chain Type Symbol Balance USD");
42856
+ console.log("\u2500".repeat(72));
42857
+ for (const r3 of rows) {
42858
+ const usd = r3.usdValue !== null ? `$${r3.usdValue.toFixed(2)}` : "";
42859
+ console.log(
42860
+ `${String(r3.chain).padEnd(12)} ${String(r3.type).padEnd(7)} ${String(r3.symbol).padEnd(17)} ${String(r3.balance).padEnd(20)} ${usd}`
42861
+ );
42862
+ }
42863
+ console.log("\u2500".repeat(72));
42864
+ console.log(`Total USD: $${totalUsd.toFixed(2)}`);
42865
+ }
42866
+ } catch (err) {
42867
+ handleCommandError(err, opts.json, "Portfolio failed");
42868
+ }
42869
+ safeExit();
42870
+ });
42871
+ wallet.command("sign-message").description("Sign a message with the agent wallet").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").requiredOption("--message <text>", "Message to sign").option(
42872
+ "--password <password>",
42873
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
42874
+ ).option("--json", "JSON output").action(async (opts) => {
42875
+ try {
42876
+ const chain2 = getChainById(opts.chain);
42877
+ if (!chain2) {
42878
+ throw new WalletError("CHAIN_UNSUPPORTED", `Unknown chain: ${opts.chain}`);
42879
+ }
42880
+ await ensureTrustSDK();
42881
+ const password3 = await resolvePassword(opts);
42882
+ const provider = await AgentWalletProvider.fromStorage(password3);
42883
+ const address = await provider.getAddress(opts.chain);
42884
+ const rawSignature = await provider.signMessage(opts.chain, opts.message);
42885
+ const signature = chain2.isEVM && !rawSignature.startsWith("0x") ? `0x${rawSignature}` : rawSignature;
42886
+ output(
42887
+ {
42888
+ chain: opts.chain,
42889
+ address,
42890
+ message: opts.message,
42891
+ signature,
42892
+ ...chain2.isEVM ? { digest: eip191Digest(opts.message) } : {}
42893
+ },
42894
+ opts.json
42895
+ );
42896
+ } catch (err) {
42897
+ handleCommandError(err, opts.json, "Sign failed");
42898
+ }
42899
+ safeExit();
42900
+ });
42901
+ wallet.command("register").description("Register wallet addresses with Trust Wallet backend (enables token holdings)").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (opts) => {
42902
+ try {
42903
+ const creds = await ensureTrustSDK();
42904
+ const password3 = await resolvePassword(opts);
42905
+ const provider = await AgentWalletProvider.fromStorage(password3);
42906
+ await registerWalletWithBackend(creds, provider);
42907
+ if (!opts.json) success("Wallet registered with backend");
42908
+ const addresses = await provider.getAddresses();
42909
+ output({ registered: true, chains: addresses.length }, opts.json);
42910
+ } catch (err) {
42911
+ handleCommandError(err, opts.json, "Registration failed");
42912
+ }
42913
+ safeExit();
42914
+ });
42915
+ }
42916
+
42917
+ // src/commands/transfer.ts
42918
+ init_dist();
42919
+
42607
42920
  // ../swap/dist/index.mjs
42608
42921
  init_dist();
42922
+ init_dist();
42923
+ init_dist();
42609
42924
  var SwapClient = class {
42610
42925
  constructor(httpClient) {
42611
42926
  this.httpClient = httpClient;
@@ -42887,9 +43202,9 @@ var SwapExecutor = class {
42887
43202
  );
42888
43203
  const chainConfig = getChainById(params.fromChain);
42889
43204
  const transactions = await withRetry(() => this.swapClient.getSwapTransactions(route));
42890
- let approvalTxHash;
43205
+ let approvalHash;
42891
43206
  try {
42892
- approvalTxHash = await this.ensureStepApproval(
43207
+ approvalHash = await this.ensureStepApproval(
42893
43208
  route,
42894
43209
  transactions,
42895
43210
  chainConfig,
@@ -42897,10 +43212,10 @@ var SwapExecutor = class {
42897
43212
  address,
42898
43213
  parsedAmount
42899
43214
  );
42900
- if (approvalTxHash) {
42901
- await this.pollForConfirmation(params.fromChain, address, approvalTxHash, 6e4);
43215
+ if (approvalHash) {
43216
+ await this.pollForConfirmation(params.fromChain, address, approvalHash, 6e4);
42902
43217
  }
42903
- let txHash = "";
43218
+ let hash = "";
42904
43219
  for (const txData of transactions) {
42905
43220
  const txType = txData.transaction.type;
42906
43221
  const isApproval = txType === "approval" || txType === "revokeApproval";
@@ -42909,30 +43224,30 @@ var SwapExecutor = class {
42909
43224
  txType === "revokeApproval" ? "Revoking prior approval..." : "Sending token approval..."
42910
43225
  );
42911
43226
  }
42912
- txHash = await this.sendTransaction(txData, chainConfig, params.fromChain);
42913
- if (isApproval) approvalTxHash = txHash;
43227
+ hash = await this.sendTransaction(txData, chainConfig, params.fromChain);
43228
+ if (isApproval) approvalHash = hash;
42914
43229
  this.log(
42915
- `${isApproval ? "Approval" : "Swap"} tx: ${getExplorerTxUrl(params.fromChain, txHash)}`
43230
+ `${isApproval ? "Approval" : "Swap"} tx: ${getExplorerTxUrl(params.fromChain, hash)}`
42916
43231
  );
42917
43232
  await this.pollForConfirmation(
42918
43233
  params.fromChain,
42919
43234
  address,
42920
- txHash,
43235
+ hash,
42921
43236
  isApproval ? 6e4 : 12e4
42922
43237
  );
42923
43238
  }
42924
43239
  return {
42925
43240
  success: true,
42926
- txHash,
43241
+ hash,
42927
43242
  route,
42928
- explorerUrl: getExplorerTxUrl(params.fromChain, txHash)
43243
+ explorerUrl: getExplorerTxUrl(params.fromChain, hash)
42929
43244
  };
42930
43245
  } catch (err) {
42931
43246
  let msg = err instanceof Error ? err.message : "Unknown error";
42932
43247
  let code2 = classifySwapError(msg);
42933
- if (approvalTxHash) {
43248
+ if (approvalHash) {
42934
43249
  code2 = "APPROVAL_SENT_SWAP_FAILED";
42935
- msg += `. Approval was sent (tx: ${approvalTxHash}). Check allowance before retrying.`;
43250
+ msg += `. Approval was sent (tx: ${approvalHash}). Check allowance before retrying.`;
42936
43251
  }
42937
43252
  return { success: false, route, error: msg, code: code2 };
42938
43253
  }
@@ -42964,8 +43279,8 @@ var SwapExecutor = class {
42964
43279
  if (allowance >= required) return void 0;
42965
43280
  this.log("Sending token approval...");
42966
43281
  const approveResult = await blockchainClient.contract(erc20).withAddress(fromAssetAddr, fromChain).write.approve([stepApproval.operatorAddress, required], this.walletProvider);
42967
- this.log(`Approval tx: ${getExplorerTxUrl(fromChain, approveResult.txHash)}`);
42968
- return approveResult.txHash;
43282
+ this.log(`Approval tx: ${getExplorerTxUrl(fromChain, approveResult.hash)}`);
43283
+ return approveResult.hash;
42969
43284
  }
42970
43285
  getBlockchainClient() {
42971
43286
  const blockchainClient = this.walletProvider.getBlockchainClient?.() ?? this.options.blockchainClient;
@@ -43049,7 +43364,13 @@ function mapBroadcastError(err) {
43049
43364
  throw err;
43050
43365
  }
43051
43366
  function registerTransferCommand(program3) {
43052
- program3.command("transfer").description("Transfer tokens to an address").requiredOption("--to <address>", "Destination address or ENS name (e.g., vitalik.eth)").requiredOption("--amount <n>", "Amount to transfer in human-readable format (e.g., 1.5)").requiredOption("--token <assetId>", "Token asset ID (e.g., c60 for ETH, c60_t0xA0b8...)").option(
43367
+ program3.command("transfer").description("Transfer tokens to an address").requiredOption("--to <address>", "Destination address or ENS name (e.g., vitalik.eth)").requiredOption("--amount <n>", "Amount to transfer in human-readable format (e.g., 1.5)").option(
43368
+ "--token <assetIdOrAddress>",
43369
+ "Without --chain: asset ID (e.g., c60, c60_t0xA0b8...). With --chain: a token contract address (omit for the native coin)."
43370
+ ).option(
43371
+ "--chain <key>",
43372
+ "Chain key (e.g., base, ethereum). When set, --token is a token contract address; omit --token to send the native coin."
43373
+ ).option(
43053
43374
  "--decimals <n>",
43054
43375
  "Token decimals (overrides auto-resolution for tokens not in the registry)"
43055
43376
  ).option(
@@ -43066,19 +43387,40 @@ function registerTransferCommand(program3) {
43066
43387
  await ensureTrustSDK();
43067
43388
  const password3 = await resolvePassword(opts);
43068
43389
  const provider = await AgentWalletProvider.fromStorage(password3);
43069
- let coinId;
43390
+ let assetId;
43391
+ let chainKey;
43070
43392
  let contractAddress;
43071
- try {
43072
- ({ coinId, contractAddress } = parseTokenId(opts.token));
43073
- } catch {
43074
- throw new TransferError("VALIDATION_ERROR", `Invalid token ID: ${opts.token}`);
43075
- }
43076
- const chainKey = coinTypeToChainKey(coinId);
43077
- if (!chainKey) {
43078
- throw new TransferError(
43079
- "CHAIN_UNSUPPORTED",
43080
- `Unsupported coin type in asset ID: ${opts.token}`
43081
- );
43393
+ if (opts.chain) {
43394
+ if (opts.token && /^c\d+(_t.+)?$/i.test(opts.token)) {
43395
+ throw new TransferError(
43396
+ "VALIDATION_ERROR",
43397
+ "Pass either --token <assetId> on its own, or --chain with --token <contractAddress> \u2014 not an asset ID together with --chain."
43398
+ );
43399
+ }
43400
+ ({ chainKey, assetId } = resolveAsset(opts.chain, opts.token));
43401
+ contractAddress = opts.token;
43402
+ } else {
43403
+ if (!opts.token) {
43404
+ throw new TransferError(
43405
+ "VALIDATION_ERROR",
43406
+ "Provide --token <assetId>, or --chain <key> with an optional --token <contractAddress>."
43407
+ );
43408
+ }
43409
+ let coinId;
43410
+ try {
43411
+ ({ coinId, contractAddress } = parseTokenId(opts.token));
43412
+ } catch {
43413
+ throw new TransferError("VALIDATION_ERROR", `Invalid token ID: ${opts.token}`);
43414
+ }
43415
+ const resolvedKey = coinTypeToChainKey(coinId);
43416
+ if (!resolvedKey) {
43417
+ throw new TransferError(
43418
+ "CHAIN_UNSUPPORTED",
43419
+ `Unsupported coin type in asset ID: ${opts.token}`
43420
+ );
43421
+ }
43422
+ assetId = opts.token;
43423
+ chainKey = resolvedKey;
43082
43424
  }
43083
43425
  let resolvedTo;
43084
43426
  try {
@@ -43101,15 +43443,17 @@ function registerTransferCommand(program3) {
43101
43443
  }
43102
43444
  const httpClient = await getClient();
43103
43445
  const swapClient = new SwapClient(httpClient);
43104
- const tokenInfo = resolveToken(opts.token, chainKey);
43446
+ const tokenInfo = resolveToken(assetId, chainKey);
43105
43447
  if (!tokenInfo) {
43106
43448
  throw new TransferError(
43107
43449
  "TOKEN_NOT_FOUND",
43108
- `Unknown token: ${opts.token} on ${chainKey}. Use a contract address (0x...) or a supported symbol.`
43450
+ `Unknown token: ${assetId} on ${chainKey}. Use a contract address (0x...) or a supported symbol.`
43109
43451
  );
43110
43452
  }
43111
43453
  const { decimals } = await resolveTokenDecimals(swapClient, chainKey, {
43112
- token: opts.token,
43454
+ // assetId is always a string (never undefined in --chain native mode) and
43455
+ // is equivalent to opts.token for raw-address detection.
43456
+ token: assetId,
43113
43457
  address: tokenInfo.address,
43114
43458
  isNative: !contractAddress,
43115
43459
  explicitDecimals: opts.decimals
@@ -43151,25 +43495,26 @@ function registerTransferCommand(program3) {
43151
43495
  let result;
43152
43496
  try {
43153
43497
  result = await client.transfer({
43154
- assetId: opts.token,
43498
+ assetId,
43155
43499
  to: resolvedTo,
43156
43500
  amount: opts.amount,
43157
43501
  decimals,
43158
- hdWallet
43502
+ hdWallet,
43503
+ chainKey
43159
43504
  });
43160
43505
  } catch (err) {
43161
43506
  mapBroadcastError(err);
43162
43507
  }
43163
- const explorerUrl = getExplorerTxUrl(result.chainKey, result.txHash);
43508
+ const explorerUrl = getExplorerTxUrl(result.chainKey, result.hash);
43164
43509
  success("Transfer sent!");
43165
43510
  output(
43166
43511
  {
43167
- hash: result.txHash,
43512
+ hash: result.hash,
43168
43513
  chain: result.chainKey,
43169
43514
  from: address,
43170
43515
  to: resolvedTo,
43171
43516
  amount: opts.amount,
43172
- token: opts.token,
43517
+ token: assetId,
43173
43518
  explorer: explorerUrl
43174
43519
  },
43175
43520
  opts.json
@@ -43381,7 +43726,7 @@ function registerSwapCommand(program3) {
43381
43726
  output(
43382
43727
  {
43383
43728
  ...quoteData,
43384
- hash: result.txHash,
43729
+ hash: result.hash,
43385
43730
  fromChain,
43386
43731
  toChain,
43387
43732
  explorer: result.explorerUrl
@@ -43401,9 +43746,41 @@ function registerSwapCommand(program3) {
43401
43746
  // src/commands/erc20.ts
43402
43747
  init_dist();
43403
43748
  init_dist3();
43749
+ function resolveErc20WriteTarget(opts, nativeMsg) {
43750
+ if (opts.chain) {
43751
+ if (/^c\d+(_t.+)?$/i.test(opts.token)) {
43752
+ throw new Erc20Error(
43753
+ "VALIDATION_ERROR",
43754
+ "With --chain, pass --token as a token contract address (0x\u2026), not an asset ID."
43755
+ );
43756
+ }
43757
+ return { chainKey: resolveChain(opts.chain), contractAddress: opts.token };
43758
+ }
43759
+ let coinId;
43760
+ let contractAddress;
43761
+ try {
43762
+ ({ coinId, contractAddress } = parseTokenId(opts.token));
43763
+ } catch {
43764
+ throw new Erc20Error("VALIDATION_ERROR", `Invalid token ID: ${opts.token}`);
43765
+ }
43766
+ if (!contractAddress) {
43767
+ throw new Erc20Error("NOT_ERC20", nativeMsg);
43768
+ }
43769
+ const chainKey = coinTypeToChainKey(coinId);
43770
+ if (!chainKey) {
43771
+ throw new Erc20Error("CHAIN_UNSUPPORTED", `Unsupported coin type in asset ID: ${opts.token}`);
43772
+ }
43773
+ return { chainKey, contractAddress };
43774
+ }
43404
43775
  function registerErc20Command(program3) {
43405
43776
  const erc202 = program3.command("erc20").description("ERC-20 token operations");
43406
- erc202.command("approve").description("Approve a spender for token spending").requiredOption("--token <assetId>", "Token asset ID (e.g., c60_t0xA0b8...)").requiredOption("--spender <address>", "Spender address").requiredOption("--amount <n>", 'Amount to approve (in smallest unit, or "unlimited")').option(
43777
+ erc202.command("approve").description("Approve a spender for token spending").requiredOption(
43778
+ "--token <assetIdOrAddress>",
43779
+ "Without --chain: asset ID (e.g., c60_t0xA0b8...). With --chain: the token contract address."
43780
+ ).requiredOption("--spender <address>", "Spender address").requiredOption("--amount <n>", 'Amount to approve (in smallest unit, or "unlimited")').option(
43781
+ "--chain <key>",
43782
+ "Chain key (e.g., base). When set, --token is the token contract address."
43783
+ ).option(
43407
43784
  "--password <password>",
43408
43785
  "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
43409
43786
  ).option(
@@ -43414,26 +43791,10 @@ function registerErc20Command(program3) {
43414
43791
  await ensureTrustSDK();
43415
43792
  const password3 = await resolvePassword(opts);
43416
43793
  const provider = await AgentWalletProvider.fromStorage(password3);
43417
- let coinId;
43418
- let contractAddress;
43419
- try {
43420
- ({ coinId, contractAddress } = parseTokenId(opts.token));
43421
- } catch {
43422
- throw new Erc20Error("VALIDATION_ERROR", `Invalid token ID: ${opts.token}`);
43423
- }
43424
- if (!contractAddress) {
43425
- throw new Erc20Error(
43426
- "NOT_ERC20",
43427
- "Cannot approve native tokens. Provide an ERC-20 asset ID (e.g., c60_t0xA0b8...)"
43428
- );
43429
- }
43430
- const chainKey = coinTypeToChainKey(coinId);
43431
- if (!chainKey) {
43432
- throw new Erc20Error(
43433
- "CHAIN_UNSUPPORTED",
43434
- `Unsupported coin type in asset ID: ${opts.token}`
43435
- );
43436
- }
43794
+ const { chainKey, contractAddress } = resolveErc20WriteTarget(
43795
+ opts,
43796
+ "Cannot approve native tokens. Provide an ERC-20 asset ID (e.g., c60_t0xA0b8...)"
43797
+ );
43437
43798
  const address = await provider.getAddress(chainKey);
43438
43799
  const amount = opts.amount === "unlimited" ? "115792089237316195423570985008687907853269984665640564039457584007913129639935" : opts.amount;
43439
43800
  if (opts.amount === "unlimited") {
@@ -43448,11 +43809,11 @@ function registerErc20Command(program3) {
43448
43809
  info(`Approving ${opts.spender} to spend from ${address} on ${chainKey}...`);
43449
43810
  const client = provider.getBlockchainClient();
43450
43811
  const pending = await client.contract(erc20).withAddress(contractAddress, chainKey).write.approve([opts.spender, BigInt(amount)], provider);
43451
- const explorerUrl = getExplorerTxUrl(chainKey, pending.txHash);
43812
+ const explorerUrl = getExplorerTxUrl(chainKey, pending.hash);
43452
43813
  success("Approval sent!");
43453
43814
  output(
43454
43815
  {
43455
- hash: pending.txHash,
43816
+ hash: pending.hash,
43456
43817
  chain: chainKey,
43457
43818
  owner: address,
43458
43819
  spender: opts.spender,
@@ -43467,7 +43828,13 @@ function registerErc20Command(program3) {
43467
43828
  }
43468
43829
  safeExit();
43469
43830
  });
43470
- erc202.command("revoke").description("Revoke token approval (set allowance to 0)").requiredOption("--token <assetId>", "Token asset ID (e.g., c60_t0xA0b8...)").requiredOption("--spender <address>", "Spender address to revoke").option(
43831
+ erc202.command("revoke").description("Revoke token approval (set allowance to 0)").requiredOption(
43832
+ "--token <assetIdOrAddress>",
43833
+ "Without --chain: asset ID (e.g., c60_t0xA0b8...). With --chain: the token contract address."
43834
+ ).requiredOption("--spender <address>", "Spender address to revoke").option(
43835
+ "--chain <key>",
43836
+ "Chain key (e.g., base). When set, --token is the token contract address."
43837
+ ).option(
43471
43838
  "--password <password>",
43472
43839
  "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
43473
43840
  ).option("--json", "JSON output").action(async (opts) => {
@@ -43475,35 +43842,19 @@ function registerErc20Command(program3) {
43475
43842
  await ensureTrustSDK();
43476
43843
  const password3 = await resolvePassword(opts);
43477
43844
  const provider = await AgentWalletProvider.fromStorage(password3);
43478
- let coinId;
43479
- let contractAddress;
43480
- try {
43481
- ({ coinId, contractAddress } = parseTokenId(opts.token));
43482
- } catch {
43483
- throw new Erc20Error("VALIDATION_ERROR", `Invalid token ID: ${opts.token}`);
43484
- }
43485
- if (!contractAddress) {
43486
- throw new Erc20Error(
43487
- "NOT_ERC20",
43488
- "Cannot revoke approval for native tokens. Provide an ERC-20 asset ID."
43489
- );
43490
- }
43491
- const chainKey = coinTypeToChainKey(coinId);
43492
- if (!chainKey) {
43493
- throw new Erc20Error(
43494
- "CHAIN_UNSUPPORTED",
43495
- `Unsupported coin type in asset ID: ${opts.token}`
43496
- );
43497
- }
43845
+ const { chainKey, contractAddress } = resolveErc20WriteTarget(
43846
+ opts,
43847
+ "Cannot revoke approval for native tokens. Provide an ERC-20 asset ID."
43848
+ );
43498
43849
  const address = await provider.getAddress(chainKey);
43499
43850
  info(`Revoking approval for ${opts.spender} on ${chainKey}...`);
43500
43851
  const client = provider.getBlockchainClient();
43501
43852
  const pending = await client.contract(erc20).withAddress(contractAddress, chainKey).write.approve([opts.spender, 0n], provider);
43502
- const explorerUrl = getExplorerTxUrl(chainKey, pending.txHash);
43853
+ const explorerUrl = getExplorerTxUrl(chainKey, pending.hash);
43503
43854
  success("Approval revoked!");
43504
43855
  output(
43505
43856
  {
43506
- hash: pending.txHash,
43857
+ hash: pending.hash,
43507
43858
  chain: chainKey,
43508
43859
  owner: address,
43509
43860
  spender: opts.spender,
@@ -43517,44 +43868,65 @@ function registerErc20Command(program3) {
43517
43868
  }
43518
43869
  safeExit();
43519
43870
  });
43520
- erc202.command("allowance").description("Check token allowance").requiredOption("--token <assetId>", "Token asset ID (e.g., c60_t0xA0b8...)").requiredOption("--owner <address>", "Owner address").requiredOption("--spender <address>", "Spender address").option("--json", "JSON output").action(async (opts) => {
43871
+ erc202.command("allowance").description("Check token allowance").requiredOption(
43872
+ "--token <assetIdOrAddress>",
43873
+ "Without --chain: asset ID (e.g., c60_t0xA0b8...). With --chain: the token contract address."
43874
+ ).requiredOption("--owner <address>", "Owner address").requiredOption("--spender <address>", "Spender address").option(
43875
+ "--chain <key>",
43876
+ "Chain key (e.g., base). When set, --token is the token contract address."
43877
+ ).option("--json", "JSON output").action(async (opts) => {
43521
43878
  try {
43522
43879
  await ensureTrustSDK();
43523
- let coinId;
43524
43880
  let contractAddress;
43525
- try {
43526
- ({ coinId, contractAddress } = parseTokenId(opts.token));
43527
- } catch (err) {
43528
- throw new Erc20Error(
43529
- "VALIDATION_ERROR",
43530
- err instanceof Error ? err.message : `Invalid token ID: ${opts.token}`
43531
- );
43532
- }
43533
- if (!contractAddress) {
43534
- throw new Erc20Error(
43535
- "NOT_ERC20",
43536
- `Not an ERC-20 asset ID (missing _t<address>): ${opts.token}`
43537
- );
43538
- }
43539
- try {
43540
- const { getWalletCore: getWalletCore2, getCoinType: getCoinType2 } = await Promise.resolve().then(() => (init_dist3(), dist_exports2));
43541
- const wc3 = getWalletCore2();
43542
- const ct5 = getCoinType2(coinId);
43543
- if (ct5 && !wc3.CoinTypeExt.validate(ct5, contractAddress)) {
43881
+ let chainKey;
43882
+ if (opts.chain) {
43883
+ if (/^c\d+(_t.+)?$/i.test(opts.token)) {
43544
43884
  throw new Erc20Error(
43545
43885
  "VALIDATION_ERROR",
43546
- `Invalid contract address in asset ID: ${contractAddress}`
43886
+ "With --chain, pass --token as a token contract address (0x\u2026), not an asset ID."
43547
43887
  );
43548
43888
  }
43549
- } catch (err) {
43550
- if (err instanceof CommandError) throw err;
43551
- }
43552
- const chainKey = coinTypeToChainKey(coinId);
43553
- if (!chainKey) {
43554
- throw new Erc20Error(
43555
- "CHAIN_UNSUPPORTED",
43556
- `Unsupported coin type in asset ID: ${opts.token}`
43557
- );
43889
+ chainKey = resolveChain(opts.chain);
43890
+ contractAddress = opts.token;
43891
+ } else {
43892
+ let coinId;
43893
+ let parsed;
43894
+ try {
43895
+ ({ coinId, contractAddress: parsed } = parseTokenId(opts.token));
43896
+ } catch (err) {
43897
+ throw new Erc20Error(
43898
+ "VALIDATION_ERROR",
43899
+ err instanceof Error ? err.message : `Invalid token ID: ${opts.token}`
43900
+ );
43901
+ }
43902
+ if (!parsed) {
43903
+ throw new Erc20Error(
43904
+ "NOT_ERC20",
43905
+ `Not an ERC-20 asset ID (missing _t<address>): ${opts.token}`
43906
+ );
43907
+ }
43908
+ try {
43909
+ const { getWalletCore: getWalletCore2, getCoinType: getCoinType2 } = await Promise.resolve().then(() => (init_dist3(), dist_exports2));
43910
+ const wc3 = getWalletCore2();
43911
+ const ct5 = getCoinType2(coinId);
43912
+ if (ct5 && !wc3.CoinTypeExt.validate(ct5, parsed)) {
43913
+ throw new Erc20Error(
43914
+ "VALIDATION_ERROR",
43915
+ `Invalid contract address in asset ID: ${parsed}`
43916
+ );
43917
+ }
43918
+ } catch (err) {
43919
+ if (err instanceof CommandError) throw err;
43920
+ }
43921
+ const k4 = coinTypeToChainKey(coinId);
43922
+ if (!k4) {
43923
+ throw new Erc20Error(
43924
+ "CHAIN_UNSUPPORTED",
43925
+ `Unsupported coin type in asset ID: ${opts.token}`
43926
+ );
43927
+ }
43928
+ contractAddress = parsed;
43929
+ chainKey = k4;
43558
43930
  }
43559
43931
  const client = new BlockchainClient();
43560
43932
  const allowance = await client.contract(erc20).withAddress(contractAddress, chainKey).read.allowance([opts.owner, opts.spender]);
@@ -43583,11 +43955,29 @@ function parseAgentId(value) {
43583
43955
  }
43584
43956
  return BigInt(value);
43585
43957
  }
43958
+ function collectKeyValue(value, previous) {
43959
+ previous.push(value);
43960
+ return previous;
43961
+ }
43962
+ function parseMetadataPairs(pairs) {
43963
+ return pairs.map((pair) => {
43964
+ const eq = pair.indexOf("=");
43965
+ if (eq <= 0) {
43966
+ throw new WalletError("VALIDATION_ERROR", `--metadata must be key=value (got "${pair}")`);
43967
+ }
43968
+ return [pair.slice(0, eq), hexOrUtf8ToBytesHex(pair.slice(eq + 1))];
43969
+ });
43970
+ }
43586
43971
  function registerErc8004Command(program3) {
43587
43972
  const erc8004Cmd = program3.command("erc8004").description("ERC-8004 Identity Registry \u2014 manage AI-agent identities");
43588
- erc8004Cmd.command("register").description("Mint a new ERC-8004 agent identity (NFT)").requiredOption("--uri <url>", "Agent registration URI (https:// or ipfs://)").option(
43973
+ erc8004Cmd.command("register").description("Mint a new ERC-8004 agent identity (NFT)").requiredOption("--uri <url>", "Agent registration URI (https://, ipfs://, or data: inline)").option(
43974
+ "--metadata <key=value>",
43975
+ "Metadata entry to set atomically with the mint (repeatable). value is 0x-hex bytes or a UTF-8 string",
43976
+ collectKeyValue,
43977
+ []
43978
+ ).option(
43589
43979
  "--chain <key>",
43590
- "EVM chain key (only `bsc` has a known deployment; set ERC8004_REGISTRY_ADDRESS for custom deployments)",
43980
+ "EVM chain key (`bsc` and `bsctestnet` have known deployments; set ERC8004_REGISTRY_ADDRESS for other EVM chains)",
43591
43981
  "bsc"
43592
43982
  ).option(
43593
43983
  "--password <password>",
@@ -43601,14 +43991,20 @@ function registerErc8004Command(program3) {
43601
43991
  provider.getBlockchainClient(),
43602
43992
  opts.chain
43603
43993
  );
43604
- info(`Minting agent on ${chainKey}...`);
43605
- const pending = await contract.write.register([opts.uri], provider);
43994
+ const metadata = parseMetadataPairs(opts.metadata);
43995
+ info(
43996
+ `Minting agent on ${chainKey}${metadata.length > 0 ? ` with ${metadata.length} metadata entr${metadata.length === 1 ? "y" : "ies"}` : ""}...`
43997
+ );
43998
+ const pending = await contract.write.register(
43999
+ metadata.length > 0 ? [opts.uri, metadata] : [opts.uri],
44000
+ provider
44001
+ );
43606
44002
  const confirmed = await pending.wait();
43607
44003
  const registered = confirmed.events.Registered?.[0];
43608
44004
  if (!registered) {
43609
44005
  throw new WalletError(
43610
44006
  "TX_FAILED",
43611
- `No Registered event emitted (txHash=${confirmed.txHash})`
44007
+ `No Registered event emitted (hash=${confirmed.hash})`
43612
44008
  );
43613
44009
  }
43614
44010
  success("Agent registered!");
@@ -43617,9 +44013,10 @@ function registerErc8004Command(program3) {
43617
44013
  agentId: registered.agentId.toString(),
43618
44014
  owner: registered.owner,
43619
44015
  agentURI: registered.agentURI,
43620
- hash: confirmed.txHash,
44016
+ ...metadata.length > 0 ? { metadata: Object.fromEntries(metadata) } : {},
44017
+ hash: confirmed.hash,
43621
44018
  chain: chainKey,
43622
- explorer: getExplorerTxUrl(chainKey, confirmed.txHash)
44019
+ explorer: getExplorerTxUrl(chainKey, confirmed.hash)
43623
44020
  },
43624
44021
  opts.json
43625
44022
  );
@@ -43628,9 +44025,12 @@ function registerErc8004Command(program3) {
43628
44025
  }
43629
44026
  safeExit();
43630
44027
  });
43631
- erc8004Cmd.command("set-uri <agentId>").description("Update the agentURI of an existing identity").requiredOption("--uri <url>", "New agent registration URI (https:// or ipfs://)").option(
44028
+ erc8004Cmd.command("set-uri <agentId>").description("Update the agentURI of an existing identity").requiredOption(
44029
+ "--uri <url>",
44030
+ "New agent registration URI (https://, ipfs://, or data: inline)"
44031
+ ).option(
43632
44032
  "--chain <key>",
43633
- "EVM chain key (only `bsc` has a known deployment; set ERC8004_REGISTRY_ADDRESS for custom deployments)",
44033
+ "EVM chain key (`bsc` and `bsctestnet` have known deployments; set ERC8004_REGISTRY_ADDRESS for other EVM chains)",
43634
44034
  "bsc"
43635
44035
  ).option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (agentId, opts) => {
43636
44036
  try {
@@ -43650,9 +44050,9 @@ function registerErc8004Command(program3) {
43650
44050
  {
43651
44051
  agentId,
43652
44052
  agentURI: opts.uri,
43653
- hash: confirmed.txHash,
44053
+ hash: confirmed.hash,
43654
44054
  chain: chainKey,
43655
- explorer: getExplorerTxUrl(chainKey, confirmed.txHash)
44055
+ explorer: getExplorerTxUrl(chainKey, confirmed.hash)
43656
44056
  },
43657
44057
  opts.json
43658
44058
  );
@@ -43663,7 +44063,7 @@ function registerErc8004Command(program3) {
43663
44063
  });
43664
44064
  erc8004Cmd.command("set-metadata <agentId>").description("Set a metadata entry on an identity (value can be 0x-hex or plain string)").requiredOption("--key <string>", "Metadata key").requiredOption("--value <hex|string>", "0x-hex bytes or plain string (UTF-8 encoded)").option(
43665
44065
  "--chain <key>",
43666
- "EVM chain key (only `bsc` has a known deployment; set ERC8004_REGISTRY_ADDRESS for custom deployments)",
44066
+ "EVM chain key (`bsc` and `bsctestnet` have known deployments; set ERC8004_REGISTRY_ADDRESS for other EVM chains)",
43667
44067
  "bsc"
43668
44068
  ).option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (agentId, opts) => {
43669
44069
  try {
@@ -43685,9 +44085,9 @@ function registerErc8004Command(program3) {
43685
44085
  agentId,
43686
44086
  key: opts.key,
43687
44087
  value: valueBytes,
43688
- hash: confirmed.txHash,
44088
+ hash: confirmed.hash,
43689
44089
  chain: chainKey,
43690
- explorer: getExplorerTxUrl(chainKey, confirmed.txHash)
44090
+ explorer: getExplorerTxUrl(chainKey, confirmed.hash)
43691
44091
  },
43692
44092
  opts.json
43693
44093
  );
@@ -43698,7 +44098,7 @@ function registerErc8004Command(program3) {
43698
44098
  });
43699
44099
  erc8004Cmd.command("get-metadata <agentId>").description("Read a metadata entry from an identity (returns raw bytes as 0x-hex)").requiredOption("--key <string>", "Metadata key").option(
43700
44100
  "--chain <key>",
43701
- "EVM chain key (only `bsc` has a known deployment; set ERC8004_REGISTRY_ADDRESS for custom deployments)",
44101
+ "EVM chain key (`bsc` and `bsctestnet` have known deployments; set ERC8004_REGISTRY_ADDRESS for other EVM chains)",
43702
44102
  "bsc"
43703
44103
  ).option("--json", "JSON output").action(async (agentId, opts) => {
43704
44104
  try {
@@ -43714,7 +44114,7 @@ function registerErc8004Command(program3) {
43714
44114
  });
43715
44115
  erc8004Cmd.command("show <agentId>").description("Show on-chain state of an ERC-8004 agent identity").option(
43716
44116
  "--chain <key>",
43717
- "EVM chain key (only `bsc` has a known deployment; set ERC8004_REGISTRY_ADDRESS for custom deployments)",
44117
+ "EVM chain key (`bsc` and `bsctestnet` have known deployments; set ERC8004_REGISTRY_ADDRESS for other EVM chains)",
43718
44118
  "bsc"
43719
44119
  ).option("--json", "JSON output").action(async (agentId, opts) => {
43720
44120
  try {
@@ -43746,7 +44146,9 @@ function registerErc8004Command(program3) {
43746
44146
  // src/commands/erc8183.ts
43747
44147
  init_dist();
43748
44148
  init_dist3();
43749
- var CHAIN_OPT = "EVM chain key (only `bsc` has a known deployment)";
44149
+ var CHAIN_OPT = "EVM chain key (`bsc` and `bsctestnet` have known deployments)";
44150
+ var OPT_PARAMS_OPT = "Optional opaque optParams bytes passed through raw, as 0x-prefixed hex (defaults to empty)";
44151
+ var SUBMIT_OPT_PARAMS_OPT = `${OPT_PARAMS_OPT}. Evaluators may expect deliverable metadata here, e.g. hex-encoded UTF-8 JSON like {"deliverable_url": "..."}`;
43750
44152
  var ZERO_BYTES32 = `0x${"0".repeat(64)}`;
43751
44153
  function parseUint(value, label) {
43752
44154
  if (!/^\d+$/.test(value)) {
@@ -43757,6 +44159,16 @@ function parseUint(value, label) {
43757
44159
  }
43758
44160
  return BigInt(value);
43759
44161
  }
44162
+ function parseOptParams(value) {
44163
+ if (value === void 0) return "0x";
44164
+ if (!/^0x([0-9a-fA-F]{2})*$/.test(value)) {
44165
+ throw new WalletError(
44166
+ "VALIDATION_ERROR",
44167
+ `--opt-params must be 0x-prefixed bytes (even-length hex): ${value}`
44168
+ );
44169
+ }
44170
+ return value;
44171
+ }
43760
44172
  function registerErc8183Command(program3) {
43761
44173
  const cmd = program3.command("erc8183").description("ERC-8183 Agentic Commerce \u2014 on-chain job escrows between AI agents");
43762
44174
  cmd.command("create-job").description("Create a new job (caller becomes the client)").requiredOption("--provider <address>", "Provider (worker) address").requiredOption("--evaluator <address>", "Evaluator address").requiredOption("--expires-at <unix>", "Expiry as a unix timestamp (seconds)").requiredOption("--description <text>", "Human-readable job description").option("--hook <address>", "Optional hook contract (defaults to none)").option("--chain <key>", CHAIN_OPT, "bsc").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (opts) => {
@@ -43779,7 +44191,7 @@ function registerErc8183Command(program3) {
43779
44191
  if (!created) {
43780
44192
  throw new WalletError(
43781
44193
  "TX_FAILED",
43782
- `No JobCreated event emitted (txHash=${confirmed.txHash})`
44194
+ `No JobCreated event emitted (hash=${confirmed.hash})`
43783
44195
  );
43784
44196
  }
43785
44197
  success("Job created!");
@@ -43787,9 +44199,9 @@ function registerErc8183Command(program3) {
43787
44199
  {
43788
44200
  jobId: created.jobId.toString(),
43789
44201
  status: "Open",
43790
- hash: confirmed.txHash,
44202
+ hash: confirmed.hash,
43791
44203
  chain: chainKey,
43792
- explorer: getExplorerTxUrl(chainKey, confirmed.txHash)
44204
+ explorer: getExplorerTxUrl(chainKey, confirmed.hash)
43793
44205
  },
43794
44206
  opts.json
43795
44207
  );
@@ -43798,9 +44210,10 @@ function registerErc8183Command(program3) {
43798
44210
  }
43799
44211
  safeExit();
43800
44212
  });
43801
- cmd.command("set-provider <jobId>").description("Set or change the provider (client, while the job is Open)").requiredOption("--provider <address>", "New provider (worker) address").option("--chain <key>", CHAIN_OPT, "bsc").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (jobId, opts) => {
44213
+ cmd.command("set-provider <jobId>").description("Set or change the provider (client, while the job is Open)").requiredOption("--provider <address>", "New provider (worker) address").option("--opt-params <hex>", OPT_PARAMS_OPT).option("--chain <key>", CHAIN_OPT, "bsc").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (jobId, opts) => {
43802
44214
  try {
43803
44215
  const id = parseUint(jobId, "jobId");
44216
+ const optParams = parseOptParams(opts.optParams);
43804
44217
  await ensureTrustSDK();
43805
44218
  const password3 = await resolvePassword(opts);
43806
44219
  const provider = await AgentWalletProvider.fromStorage(password3);
@@ -43809,16 +44222,16 @@ function registerErc8183Command(program3) {
43809
44222
  opts.chain
43810
44223
  );
43811
44224
  info(`Setting provider for job ${jobId} on ${chainKey}...`);
43812
- const pending = await commerce.write.setProvider([id, opts.provider, "0x"], provider);
44225
+ const pending = await commerce.write.setProvider([id, opts.provider, optParams], provider);
43813
44226
  const confirmed = await pending.wait();
43814
44227
  success("Provider set!");
43815
44228
  output(
43816
44229
  {
43817
44230
  jobId,
43818
44231
  provider: opts.provider,
43819
- hash: confirmed.txHash,
44232
+ hash: confirmed.hash,
43820
44233
  chain: chainKey,
43821
- explorer: getExplorerTxUrl(chainKey, confirmed.txHash)
44234
+ explorer: getExplorerTxUrl(chainKey, confirmed.hash)
43822
44235
  },
43823
44236
  opts.json
43824
44237
  );
@@ -43827,10 +44240,11 @@ function registerErc8183Command(program3) {
43827
44240
  }
43828
44241
  safeExit();
43829
44242
  });
43830
- cmd.command("set-budget <jobId>").description("Set the escrow budget (atomic units of the payment token)").requiredOption("--amount <atomic>", "Budget in atomic units").option("--chain <key>", CHAIN_OPT, "bsc").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (jobId, opts) => {
44243
+ cmd.command("set-budget <jobId>").description("Set the escrow budget (atomic units of the payment token)").requiredOption("--amount <atomic>", "Budget in atomic units").option("--opt-params <hex>", OPT_PARAMS_OPT).option("--chain <key>", CHAIN_OPT, "bsc").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (jobId, opts) => {
43831
44244
  try {
43832
44245
  const id = parseUint(jobId, "jobId");
43833
44246
  const amount = parseUint(opts.amount, "amount");
44247
+ const optParams = parseOptParams(opts.optParams);
43834
44248
  await ensureTrustSDK();
43835
44249
  const password3 = await resolvePassword(opts);
43836
44250
  const provider = await AgentWalletProvider.fromStorage(password3);
@@ -43839,16 +44253,16 @@ function registerErc8183Command(program3) {
43839
44253
  opts.chain
43840
44254
  );
43841
44255
  info(`Setting budget for job ${jobId} on ${chainKey}...`);
43842
- const pending = await commerce.write.setBudget([id, amount, "0x"], provider);
44256
+ const pending = await commerce.write.setBudget([id, amount, optParams], provider);
43843
44257
  const confirmed = await pending.wait();
43844
44258
  success("Budget set!");
43845
44259
  output(
43846
44260
  {
43847
44261
  jobId,
43848
44262
  amount: opts.amount,
43849
- hash: confirmed.txHash,
44263
+ hash: confirmed.hash,
43850
44264
  chain: chainKey,
43851
- explorer: getExplorerTxUrl(chainKey, confirmed.txHash)
44265
+ explorer: getExplorerTxUrl(chainKey, confirmed.hash)
43852
44266
  },
43853
44267
  opts.json
43854
44268
  );
@@ -43857,37 +44271,53 @@ function registerErc8183Command(program3) {
43857
44271
  }
43858
44272
  safeExit();
43859
44273
  });
43860
- cmd.command("fund <jobId>").description("Fund a job: approve the budget then deposit it into escrow (two txs)").option("--chain <key>", CHAIN_OPT, "bsc").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (jobId, opts) => {
44274
+ cmd.command("fund <jobId>").description("Fund a job: approve the budget then deposit it into escrow (two txs)").option(
44275
+ "--expected-budget <atomic>",
44276
+ "Pin the budget (atomic units): approve and fund exactly this amount instead of reading it on-chain; the contract reverts with BudgetMismatch if the on-chain budget differs"
44277
+ ).option("--opt-params <hex>", OPT_PARAMS_OPT).option("--chain <key>", CHAIN_OPT, "bsc").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (jobId, opts) => {
43861
44278
  try {
43862
44279
  const id = parseUint(jobId, "jobId");
44280
+ const optParams = parseOptParams(opts.optParams);
43863
44281
  await ensureTrustSDK();
43864
44282
  const password3 = await resolvePassword(opts);
43865
44283
  const provider = await AgentWalletProvider.fromStorage(password3);
43866
44284
  const client = provider.getBlockchainClient();
43867
44285
  const { commerce, chainKey } = resolveErc8183Contracts(client, opts.chain);
43868
- const job = await commerce.read.getJob([id]);
43869
- if (job.budget === 0n) {
43870
- throw new WalletError(
43871
- "VALIDATION_ERROR",
43872
- `Job ${jobId} has no budget set \u2014 run \`twak erc8183 set-budget\` before funding.`
43873
- );
44286
+ let budget;
44287
+ if (opts.expectedBudget !== void 0) {
44288
+ budget = parseUint(opts.expectedBudget, "expected-budget");
44289
+ if (budget === 0n) {
44290
+ throw new WalletError(
44291
+ "VALIDATION_ERROR",
44292
+ "--expected-budget must be greater than zero."
44293
+ );
44294
+ }
44295
+ } else {
44296
+ const job = await commerce.read.getJob([id]);
44297
+ if (job.budget === 0n) {
44298
+ throw new WalletError(
44299
+ "VALIDATION_ERROR",
44300
+ `Job ${jobId} has no budget set \u2014 run \`twak erc8183 set-budget\` before funding.`
44301
+ );
44302
+ }
44303
+ budget = job.budget;
43874
44304
  }
43875
44305
  const token = await commerce.read.paymentToken([]);
43876
- info(`Approving ${job.budget.toString()} for the escrow on ${chainKey}...`);
43877
- const approvePending = await erc20.using(client).withAddress(token, chainKey).write.approve([commerce.address, job.budget], provider);
44306
+ info(`Approving ${budget.toString()} for the escrow on ${chainKey}...`);
44307
+ const approvePending = await erc20.using(client).withAddress(token, chainKey).write.approve([commerce.address, budget], provider);
43878
44308
  const approveConfirmed = await approvePending.wait();
43879
44309
  info(`Funding job ${jobId}...`);
43880
- const fundPending = await commerce.write.fund([id, job.budget, "0x"], provider);
44310
+ const fundPending = await commerce.write.fund([id, budget, optParams], provider);
43881
44311
  const fundConfirmed = await fundPending.wait();
43882
44312
  success("Job funded!");
43883
44313
  output(
43884
44314
  {
43885
44315
  jobId,
43886
44316
  status: "Funded",
43887
- approveHash: approveConfirmed.txHash,
43888
- hash: fundConfirmed.txHash,
44317
+ approveHash: approveConfirmed.hash,
44318
+ hash: fundConfirmed.hash,
43889
44319
  chain: chainKey,
43890
- explorer: getExplorerTxUrl(chainKey, fundConfirmed.txHash)
44320
+ explorer: getExplorerTxUrl(chainKey, fundConfirmed.hash)
43891
44321
  },
43892
44322
  opts.json
43893
44323
  );
@@ -43896,9 +44326,10 @@ function registerErc8183Command(program3) {
43896
44326
  }
43897
44327
  safeExit();
43898
44328
  });
43899
- cmd.command("submit <jobId>").description("Submit a deliverable (bytes32 commitment) for a funded job").requiredOption("--deliverable <bytes32>", "0x-prefixed 32-byte deliverable commitment").option("--chain <key>", CHAIN_OPT, "bsc").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (jobId, opts) => {
44329
+ cmd.command("submit <jobId>").description("Submit a deliverable (bytes32 commitment) for a funded job").requiredOption("--deliverable <bytes32>", "0x-prefixed 32-byte deliverable commitment").option("--opt-params <hex>", SUBMIT_OPT_PARAMS_OPT).option("--chain <key>", CHAIN_OPT, "bsc").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (jobId, opts) => {
43900
44330
  try {
43901
44331
  const id = parseUint(jobId, "jobId");
44332
+ const optParams = parseOptParams(opts.optParams);
43902
44333
  await ensureTrustSDK();
43903
44334
  const password3 = await resolvePassword(opts);
43904
44335
  const provider = await AgentWalletProvider.fromStorage(password3);
@@ -43907,16 +44338,16 @@ function registerErc8183Command(program3) {
43907
44338
  opts.chain
43908
44339
  );
43909
44340
  info(`Submitting deliverable for job ${jobId} on ${chainKey}...`);
43910
- const pending = await commerce.write.submit([id, opts.deliverable, "0x"], provider);
44341
+ const pending = await commerce.write.submit([id, opts.deliverable, optParams], provider);
43911
44342
  const confirmed = await pending.wait();
43912
44343
  success("Deliverable submitted!");
43913
44344
  output(
43914
44345
  {
43915
44346
  jobId,
43916
44347
  status: "Submitted",
43917
- hash: confirmed.txHash,
44348
+ hash: confirmed.hash,
43918
44349
  chain: chainKey,
43919
- explorer: getExplorerTxUrl(chainKey, confirmed.txHash)
44350
+ explorer: getExplorerTxUrl(chainKey, confirmed.hash)
43920
44351
  },
43921
44352
  opts.json
43922
44353
  );
@@ -43925,9 +44356,10 @@ function registerErc8183Command(program3) {
43925
44356
  }
43926
44357
  safeExit();
43927
44358
  });
43928
- cmd.command("complete <jobId>").description("Mark a submitted job complete (evaluator) \u2014 releases escrow").option("--reason <bytes32>", "Optional bytes32 attestation (defaults to zero)").option("--chain <key>", CHAIN_OPT, "bsc").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (jobId, opts) => {
44359
+ cmd.command("complete <jobId>").description("Mark a submitted job complete (evaluator) \u2014 releases escrow").option("--reason <bytes32>", "Optional bytes32 attestation (defaults to zero)").option("--opt-params <hex>", OPT_PARAMS_OPT).option("--chain <key>", CHAIN_OPT, "bsc").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (jobId, opts) => {
43929
44360
  try {
43930
44361
  const id = parseUint(jobId, "jobId");
44362
+ const optParams = parseOptParams(opts.optParams);
43931
44363
  await ensureTrustSDK();
43932
44364
  const password3 = await resolvePassword(opts);
43933
44365
  const provider = await AgentWalletProvider.fromStorage(password3);
@@ -43937,7 +44369,7 @@ function registerErc8183Command(program3) {
43937
44369
  );
43938
44370
  info(`Completing job ${jobId} on ${chainKey}...`);
43939
44371
  const pending = await commerce.write.complete(
43940
- [id, opts.reason ?? ZERO_BYTES32, "0x"],
44372
+ [id, opts.reason ?? ZERO_BYTES32, optParams],
43941
44373
  provider
43942
44374
  );
43943
44375
  const confirmed = await pending.wait();
@@ -43948,9 +44380,9 @@ function registerErc8183Command(program3) {
43948
44380
  jobId,
43949
44381
  status: "Completed",
43950
44382
  released: released ? released.amount.toString() : null,
43951
- hash: confirmed.txHash,
44383
+ hash: confirmed.hash,
43952
44384
  chain: chainKey,
43953
- explorer: getExplorerTxUrl(chainKey, confirmed.txHash)
44385
+ explorer: getExplorerTxUrl(chainKey, confirmed.hash)
43954
44386
  },
43955
44387
  opts.json
43956
44388
  );
@@ -43959,9 +44391,10 @@ function registerErc8183Command(program3) {
43959
44391
  }
43960
44392
  safeExit();
43961
44393
  });
43962
- cmd.command("reject <jobId>").description("Reject a job \u2014 refunds the client if funded").option("--reason <bytes32>", "Optional bytes32 reason (defaults to zero)").option("--chain <key>", CHAIN_OPT, "bsc").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (jobId, opts) => {
44394
+ cmd.command("reject <jobId>").description("Reject a job \u2014 refunds the client if funded").option("--reason <bytes32>", "Optional bytes32 reason (defaults to zero)").option("--opt-params <hex>", OPT_PARAMS_OPT).option("--chain <key>", CHAIN_OPT, "bsc").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (jobId, opts) => {
43963
44395
  try {
43964
44396
  const id = parseUint(jobId, "jobId");
44397
+ const optParams = parseOptParams(opts.optParams);
43965
44398
  await ensureTrustSDK();
43966
44399
  const password3 = await resolvePassword(opts);
43967
44400
  const provider = await AgentWalletProvider.fromStorage(password3);
@@ -43971,7 +44404,7 @@ function registerErc8183Command(program3) {
43971
44404
  );
43972
44405
  info(`Rejecting job ${jobId} on ${chainKey}...`);
43973
44406
  const pending = await commerce.write.reject(
43974
- [id, opts.reason ?? ZERO_BYTES32, "0x"],
44407
+ [id, opts.reason ?? ZERO_BYTES32, optParams],
43975
44408
  provider
43976
44409
  );
43977
44410
  const confirmed = await pending.wait();
@@ -43982,9 +44415,9 @@ function registerErc8183Command(program3) {
43982
44415
  jobId,
43983
44416
  status: "Rejected",
43984
44417
  refunded: refunded ? refunded.amount.toString() : null,
43985
- hash: confirmed.txHash,
44418
+ hash: confirmed.hash,
43986
44419
  chain: chainKey,
43987
- explorer: getExplorerTxUrl(chainKey, confirmed.txHash)
44420
+ explorer: getExplorerTxUrl(chainKey, confirmed.hash)
43988
44421
  },
43989
44422
  opts.json
43990
44423
  );
@@ -44013,9 +44446,9 @@ function registerErc8183Command(program3) {
44013
44446
  jobId,
44014
44447
  status: "Expired",
44015
44448
  refunded: refunded ? refunded.amount.toString() : null,
44016
- hash: confirmed.txHash,
44449
+ hash: confirmed.hash,
44017
44450
  chain: chainKey,
44018
- explorer: getExplorerTxUrl(chainKey, confirmed.txHash)
44451
+ explorer: getExplorerTxUrl(chainKey, confirmed.hash)
44019
44452
  },
44020
44453
  opts.json
44021
44454
  );
@@ -44075,9 +44508,9 @@ function registerErc8183Command(program3) {
44075
44508
  {
44076
44509
  jobId,
44077
44510
  policy: policyAddr,
44078
- hash: confirmed.txHash,
44511
+ hash: confirmed.hash,
44079
44512
  chain: chainKey,
44080
- explorer: getExplorerTxUrl(chainKey, confirmed.txHash)
44513
+ explorer: getExplorerTxUrl(chainKey, confirmed.hash)
44081
44514
  },
44082
44515
  opts.json
44083
44516
  );
@@ -44103,9 +44536,9 @@ function registerErc8183Command(program3) {
44103
44536
  output(
44104
44537
  {
44105
44538
  jobId,
44106
- hash: confirmed.txHash,
44539
+ hash: confirmed.hash,
44107
44540
  chain: chainKey,
44108
- explorer: getExplorerTxUrl(chainKey, confirmed.txHash)
44541
+ explorer: getExplorerTxUrl(chainKey, confirmed.hash)
44109
44542
  },
44110
44543
  opts.json
44111
44544
  );
@@ -44132,9 +44565,9 @@ function registerErc8183Command(program3) {
44132
44565
  {
44133
44566
  jobId,
44134
44567
  status: "Expired",
44135
- hash: confirmed.txHash,
44568
+ hash: confirmed.hash,
44136
44569
  chain: chainKey,
44137
- explorer: getExplorerTxUrl(chainKey, confirmed.txHash)
44570
+ explorer: getExplorerTxUrl(chainKey, confirmed.hash)
44138
44571
  },
44139
44572
  opts.json
44140
44573
  );
@@ -44143,6 +44576,45 @@ function registerErc8183Command(program3) {
44143
44576
  }
44144
44577
  safeExit();
44145
44578
  });
44579
+ cmd.command("policy-info <jobId>").description("Show dispute-policy state for a job (read-only)").option("--chain <key>", CHAIN_OPT, "bsc").option("--json", "JSON output").action(async (jobId, opts) => {
44580
+ try {
44581
+ const id = parseUint(jobId, "jobId");
44582
+ await ensureTrustSDK();
44583
+ const client = new BlockchainClient();
44584
+ const { router, policy, chainKey } = resolveErc8183Contracts(client, opts.chain);
44585
+ const registeredPolicy = await router.read.jobPolicy([id]);
44586
+ const registered = !isZeroAddress(registeredPolicy);
44587
+ const policyContract = registered && registeredPolicy.toLowerCase() !== policy.address.toLowerCase() ? erc8183Policy.using(client).withAddress(registeredPolicy, chainKey) : policy;
44588
+ const [disputeWindow, submittedAt, disputed, rejectVotes, voteQuorum, quorumSnapshot] = await Promise.all([
44589
+ policyContract.read.disputeWindow([]),
44590
+ policyContract.read.submittedAt([id]),
44591
+ policyContract.read.disputed([id]),
44592
+ policyContract.read.rejectVotes([id]),
44593
+ policyContract.read.voteQuorum([]),
44594
+ policyContract.read.disputeQuorumSnapshot([id])
44595
+ ]);
44596
+ const settleableAt = submittedAt > 0n ? submittedAt + disputeWindow : null;
44597
+ output(
44598
+ {
44599
+ jobId,
44600
+ policy: policyContract.address,
44601
+ registered,
44602
+ disputeWindow: disputeWindow.toString(),
44603
+ submittedAt: submittedAt.toString(),
44604
+ disputed,
44605
+ rejectVotes: rejectVotes.toString(),
44606
+ voteQuorum: voteQuorum.toString(),
44607
+ quorumSnapshot: quorumSnapshot.toString(),
44608
+ settleableAt: settleableAt?.toString() ?? null,
44609
+ chain: chainKey
44610
+ },
44611
+ opts.json
44612
+ );
44613
+ } catch (err) {
44614
+ handleCommandError(err, opts.json, "policy-info failed");
44615
+ }
44616
+ safeExit();
44617
+ });
44146
44618
  cmd.command("dispute <jobId>").description("Raise a dispute on a job via the OptimisticPolicy (client)").option("--chain <key>", CHAIN_OPT, "bsc").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (jobId, opts) => {
44147
44619
  try {
44148
44620
  const id = parseUint(jobId, "jobId");
@@ -44160,9 +44632,9 @@ function registerErc8183Command(program3) {
44160
44632
  output(
44161
44633
  {
44162
44634
  jobId,
44163
- hash: confirmed.txHash,
44635
+ hash: confirmed.hash,
44164
44636
  chain: chainKey,
44165
- explorer: getExplorerTxUrl(chainKey, confirmed.txHash)
44637
+ explorer: getExplorerTxUrl(chainKey, confirmed.hash)
44166
44638
  },
44167
44639
  opts.json
44168
44640
  );
@@ -44188,9 +44660,9 @@ function registerErc8183Command(program3) {
44188
44660
  output(
44189
44661
  {
44190
44662
  jobId,
44191
- hash: confirmed.txHash,
44663
+ hash: confirmed.hash,
44192
44664
  chain: chainKey,
44193
- explorer: getExplorerTxUrl(chainKey, confirmed.txHash)
44665
+ explorer: getExplorerTxUrl(chainKey, confirmed.hash)
44194
44666
  },
44195
44667
  opts.json
44196
44668
  );
@@ -44256,7 +44728,7 @@ function registerCompeteCommand(program3) {
44256
44728
  if (!confirmed.events.Registered?.[0]) {
44257
44729
  throw new WalletError(
44258
44730
  "TX_FAILED",
44259
- `no Registered event emitted (txHash=${confirmed.txHash})`
44731
+ `no Registered event emitted (hash=${confirmed.hash})`
44260
44732
  );
44261
44733
  }
44262
44734
  success("Registered for BNB HACK: AI TRADING AGENT EDITION!");
@@ -44265,9 +44737,9 @@ function registerCompeteCommand(program3) {
44265
44737
  registered: true,
44266
44738
  participant,
44267
44739
  deadline: toIso(deadline),
44268
- hash: confirmed.txHash,
44740
+ hash: confirmed.hash,
44269
44741
  chain: chainKey,
44270
- explorer: getExplorerTxUrl(chainKey, confirmed.txHash)
44742
+ explorer: getExplorerTxUrl(chainKey, confirmed.hash)
44271
44743
  },
44272
44744
  opts.json
44273
44745
  );
@@ -45945,15 +46417,15 @@ async function ensurePermit2Allowance(args) {
45945
46417
  [PERMIT2_ADDRESS, BigInt(MAX_UINT256_DECIMAL)],
45946
46418
  args.walletProvider
45947
46419
  );
45948
- await waitForTxConfirmation(args.blockchainClient, args.chainKey, args.owner, pending.txHash);
45949
- return { token: args.tokenAddress, chain: args.chainKey, txHash: pending.txHash };
46420
+ await waitForTxConfirmation(args.blockchainClient, args.chainKey, args.owner, pending.hash);
46421
+ return { token: args.tokenAddress, chain: args.chainKey, hash: pending.hash };
45950
46422
  }
45951
46423
  var APPROVE_TIMEOUT_MS = 6e4;
45952
46424
  var APPROVE_POLL_MS = 2e3;
45953
- async function waitForTxConfirmation(bc2, chainKey, address, txHash) {
46425
+ async function waitForTxConfirmation(bc2, chainKey, address, hash) {
45954
46426
  const start = Date.now();
45955
46427
  while (Date.now() - start < APPROVE_TIMEOUT_MS) {
45956
- const status = await bc2.findTransactionStatus(chainKey, address, txHash);
46428
+ const status = await bc2.findTransactionStatus(chainKey, address, hash);
45957
46429
  if (status.confirmed) return;
45958
46430
  if (status.failed) {
45959
46431
  throw new X402Error(
@@ -45965,7 +46437,7 @@ async function waitForTxConfirmation(bc2, chainKey, address, txHash) {
45965
46437
  }
45966
46438
  throw new X402Error(
45967
46439
  "TX_FAILED",
45968
- `Permit2 approval ${txHash} not confirmed within ${APPROVE_TIMEOUT_MS / 1e3}s`
46440
+ `Permit2 approval ${hash} not confirmed within ${APPROVE_TIMEOUT_MS / 1e3}s`
45969
46441
  );
45970
46442
  }
45971
46443
  function ipv4MappedToDotted(ip) {
@@ -46482,7 +46954,7 @@ var TransferActionProvider = class extends ActionProvider {
46482
46954
  const resolvedTo = await resolveAddressOrName(input2.to);
46483
46955
  const atomicAmount = parseAmount(input2.amount, decimals);
46484
46956
  const isErc20 = Boolean(tokenAddress);
46485
- let txHash;
46957
+ let hash;
46486
46958
  if (isErc20) {
46487
46959
  if (!this.blockchainClient) {
46488
46960
  throw new TransferError(
@@ -46491,19 +46963,19 @@ var TransferActionProvider = class extends ActionProvider {
46491
46963
  );
46492
46964
  }
46493
46965
  const pending = await this.blockchainClient.contract(erc20).withAddress(tokenAddress, chainKey).write.transfer([resolvedTo, BigInt(atomicAmount)], signer);
46494
- txHash = pending.txHash;
46966
+ hash = pending.hash;
46495
46967
  } else {
46496
- txHash = await signer.sendTransaction({
46968
+ hash = await signer.sendTransaction({
46497
46969
  chainId: caip2,
46498
46970
  to: resolvedTo,
46499
46971
  data: "0x",
46500
46972
  value: "0x" + BigInt(atomicAmount).toString(16)
46501
46973
  });
46502
46974
  }
46503
- const explorerUrl = getExplorerTxUrl(chainKey, txHash);
46975
+ const explorerUrl = getExplorerTxUrl(chainKey, hash);
46504
46976
  return {
46505
46977
  success: true,
46506
- txHash,
46978
+ hash,
46507
46979
  to: resolvedTo,
46508
46980
  resolvedFrom: input2.to !== resolvedTo ? input2.to : void 0,
46509
46981
  explorer: explorerUrl || void 0
@@ -46588,7 +47060,7 @@ var SwapActionProvider = class extends ActionProvider {
46588
47060
  const outputAmt = formatAmount(lastStep.to.amount, lastStep.to.asset.metadata.decimals);
46589
47061
  return {
46590
47062
  success: true,
46591
- txHash: result.txHash,
47063
+ hash: result.hash,
46592
47064
  summary: `${inputAmt} ${firstStep.from.asset.metadata.symbol} -> ${outputAmt} ${lastStep.to.asset.metadata.symbol}`,
46593
47065
  provider: result.route.steps.map((s2) => s2.provider.name).join(" -> "),
46594
47066
  explorer: result.explorerUrl || void 0
@@ -46970,7 +47442,7 @@ var AgentWalletActionProvider = class extends ActionProvider {
46970
47442
  if (!this.blockchainClient) {
46971
47443
  return { chain: chainKey, address, balance: "BlockchainClient not available" };
46972
47444
  }
46973
- const balance = await this.blockchainClient.getBalance(assetId, address);
47445
+ const balance = await this.blockchainClient.getBalance(assetId, address, chainKey);
46974
47446
  return { chain: chainKey, address, balance };
46975
47447
  }
46976
47448
  };
@@ -47007,7 +47479,7 @@ var TokenActionProvider = class extends ActionProvider {
47007
47479
  throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
47008
47480
  }
47009
47481
  const { chainKey, assetId } = resolveAsset(input2.chain, input2.tokenAddress);
47010
- const balance = await this.blockchainClient.getBalance(assetId, input2.address);
47482
+ const balance = await this.blockchainClient.getBalance(assetId, input2.address, chainKey);
47011
47483
  return { chain: chainKey, address: input2.address, balance };
47012
47484
  }
47013
47485
  };
@@ -47047,9 +47519,10 @@ var TokenActionProvider = class extends ActionProvider {
47047
47519
  amount: input2.amount,
47048
47520
  decimals,
47049
47521
  memo: input2.memo,
47050
- hdWallet
47522
+ hdWallet,
47523
+ chainKey
47051
47524
  });
47052
- return { hash: result.txHash, chain: result.chainKey };
47525
+ return { hash: result.hash, chain: result.chainKey };
47053
47526
  }
47054
47527
  const chain2 = requireChain(chainKey);
47055
47528
  if (chain2.namespace !== "eip155") {
@@ -47061,24 +47534,24 @@ var TokenActionProvider = class extends ActionProvider {
47061
47534
  if (input2.tokenAddress) assertEvmAddress(input2.tokenAddress, "tokenAddress");
47062
47535
  const caipChainId = `${chain2.namespace}:${chain2.chainId}`;
47063
47536
  const atomicAmount = parseAmount(input2.amount, decimals);
47064
- let txHash;
47537
+ let hash;
47065
47538
  if (input2.tokenAddress) {
47066
47539
  if (!this.blockchainClient) {
47067
47540
  throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
47068
47541
  }
47069
47542
  const pending = await this.blockchainClient.contract(erc20).withAddress(input2.tokenAddress, chainKey).write.transfer([input2.to, BigInt(atomicAmount)], wallet);
47070
- txHash = pending.txHash;
47543
+ hash = pending.hash;
47071
47544
  } else {
47072
47545
  assertEvmAddress(input2.to, "to");
47073
47546
  const valueHex = "0x" + BigInt(atomicAmount).toString(16);
47074
- txHash = await wallet.sendTransaction({
47547
+ hash = await wallet.sendTransaction({
47075
47548
  chainId: caipChainId,
47076
47549
  to: input2.to,
47077
47550
  data: "0x",
47078
47551
  value: valueHex
47079
47552
  });
47080
47553
  }
47081
- return { hash: txHash, chain: chainKey };
47554
+ return { hash, chain: chainKey };
47082
47555
  }
47083
47556
  };
47084
47557
  }
@@ -47109,7 +47582,7 @@ var TokenActionProvider = class extends ActionProvider {
47109
47582
  }
47110
47583
  const amount = input2.amount === "unlimited" ? "115792089237316195423570985008687907853269984665640564039457584007913129639935" : input2.amount;
47111
47584
  const pending = await this.blockchainClient.contract(erc20).withAddress(input2.tokenAddress, chainKey).write.approve([input2.spender, BigInt(amount)], wallet);
47112
- return { hash: pending.txHash, chain: chainKey };
47585
+ return { hash: pending.hash, chain: chainKey };
47113
47586
  }
47114
47587
  };
47115
47588
  }
@@ -47138,7 +47611,7 @@ var TokenActionProvider = class extends ActionProvider {
47138
47611
  throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
47139
47612
  }
47140
47613
  const pending = await this.blockchainClient.contract(erc20).withAddress(input2.tokenAddress, chainKey).write.approve([input2.spender, 0n], wallet);
47141
- return { hash: pending.txHash, chain: chainKey };
47614
+ return { hash: pending.hash, chain: chainKey };
47142
47615
  }
47143
47616
  };
47144
47617
  }
@@ -47608,7 +48081,7 @@ var OnrampActionProvider = class extends ActionProvider {
47608
48081
  if (cap) return { success: false, ...cap };
47609
48082
  }
47610
48083
  const atomic = parseAmount(input2.cryptoAmount, decimals);
47611
- let txHash;
48084
+ let hash;
47612
48085
  if (input2.tokenAddress) {
47613
48086
  if (!this.blockchainClient) {
47614
48087
  throw new TransferError(
@@ -47617,19 +48090,19 @@ var OnrampActionProvider = class extends ActionProvider {
47617
48090
  );
47618
48091
  }
47619
48092
  const pending = await this.blockchainClient.contract(erc20).withAddress(input2.tokenAddress, chainKey).write.transfer([input2.depositAddress, BigInt(atomic)], signer);
47620
- txHash = pending.txHash;
48093
+ hash = pending.hash;
47621
48094
  } else {
47622
- txHash = await signer.sendTransaction({
48095
+ hash = await signer.sendTransaction({
47623
48096
  chainId: caip2,
47624
48097
  to: input2.depositAddress,
47625
48098
  data: "0x",
47626
48099
  value: "0x" + BigInt(atomic).toString(16)
47627
48100
  });
47628
48101
  }
47629
- const explorerUrl = getExplorerTxUrl(chainKey, txHash) || void 0;
48102
+ const explorerUrl = getExplorerTxUrl(chainKey, hash) || void 0;
47630
48103
  return {
47631
48104
  success: true,
47632
- txHash,
48105
+ hash,
47633
48106
  chain: chainKey,
47634
48107
  tokenAddress: input2.tokenAddress,
47635
48108
  to: input2.depositAddress,
@@ -47799,7 +48272,7 @@ var AutomateActionProvider = class extends ActionProvider {
47799
48272
  await this.automationStore.update(record2.id, { lastRunAt: (/* @__PURE__ */ new Date()).toISOString() });
47800
48273
  return {
47801
48274
  success: true,
47802
- txHash: result.txHash,
48275
+ hash: result.hash,
47803
48276
  automationId: record2.id,
47804
48277
  swapped: `${record2.amount} ${record2.fromToken} \u2192 ${record2.toToken}`,
47805
48278
  explorer: result.explorerUrl || void 0
@@ -47991,17 +48464,23 @@ var Erc8004ActionProvider = class extends ActionProvider {
47991
48464
  register() {
47992
48465
  return {
47993
48466
  name: "erc8004_register",
47994
- description: "Mint a new ERC-8004 agent identity on-chain. The caller becomes the owner of an NFT token whose ID is the agentId. The supplied agentURI is stored on-chain as the token URI and should point to the agent registration JSON (https or ipfs).",
48467
+ description: "Mint a new ERC-8004 agent identity on-chain. The caller becomes the owner of an NFT token whose ID is the agentId. The supplied agentURI is stored on-chain as the token URI and should point to the agent registration JSON (https, ipfs, or a data: inline). Optionally pass metadata entries to set them atomically in the same mint transaction.",
47995
48468
  schema: import_zod16.z.object({
47996
48469
  agentURI: import_zod16.z.string().min(1).describe(
47997
- "URI of the agent registration file (https:// or ipfs://). Stored on-chain as the token URI."
48470
+ "URI of the agent registration file (https://, ipfs://, or data: inline). Stored on-chain as the token URI."
48471
+ ),
48472
+ metadata: import_zod16.z.record(import_zod16.z.string().min(1), import_zod16.z.string().min(1)).optional().describe(
48473
+ "Optional metadata entries (key \u2192 value) to set atomically in the mint transaction. Each value: `0x`-prefixed hex is sent as raw bytes; any other string is UTF-8 encoded."
47998
48474
  ),
47999
48475
  chain: import_zod16.z.string().default("bsc").describe(CHAIN_DESC)
48000
48476
  }),
48001
48477
  invoke: async (_ctx, input2) => {
48002
48478
  const { contract, chainKey } = this.bind(input2.chain);
48479
+ const entries = Object.entries(input2.metadata ?? {}).map(
48480
+ ([key, value]) => [key, hexOrUtf8ToBytesHex(value)]
48481
+ );
48003
48482
  const pending = await contract.write.register(
48004
- [input2.agentURI],
48483
+ entries.length > 0 ? [input2.agentURI, entries] : [input2.agentURI],
48005
48484
  this.walletContext.activeWallet()
48006
48485
  );
48007
48486
  const confirmed = await pending.wait();
@@ -48009,15 +48488,16 @@ var Erc8004ActionProvider = class extends ActionProvider {
48009
48488
  if (!registered) {
48010
48489
  throw new WalletError(
48011
48490
  "TX_FAILED",
48012
- `erc8004_register: no Registered event emitted (txHash=${confirmed.txHash})`
48491
+ `erc8004_register: no Registered event emitted (hash=${confirmed.hash})`
48013
48492
  );
48014
48493
  }
48015
48494
  return {
48016
48495
  success: true,
48017
48496
  agentId: registered.agentId.toString(),
48018
- txHash: confirmed.txHash,
48497
+ hash: confirmed.hash,
48019
48498
  owner: registered.owner,
48020
48499
  agentURI: registered.agentURI,
48500
+ ...entries.length > 0 ? { metadata: Object.fromEntries(entries) } : {},
48021
48501
  chain: chainKey
48022
48502
  };
48023
48503
  }
@@ -48029,7 +48509,9 @@ var Erc8004ActionProvider = class extends ActionProvider {
48029
48509
  description: "Update the agentURI of an existing ERC-8004 agent identity. Caller must own the NFT.",
48030
48510
  schema: import_zod16.z.object({
48031
48511
  agentId: agentIdSchema,
48032
- agentURI: import_zod16.z.string().min(1).describe("New agent URI (https:// or ipfs://). Replaces the on-chain stored URI."),
48512
+ agentURI: import_zod16.z.string().min(1).describe(
48513
+ "New agent URI (https://, ipfs://, or data: inline). Replaces the on-chain stored URI."
48514
+ ),
48033
48515
  chain: import_zod16.z.string().default("bsc").describe(CHAIN_DESC)
48034
48516
  }),
48035
48517
  invoke: async (_ctx, input2) => {
@@ -48039,7 +48521,7 @@ var Erc8004ActionProvider = class extends ActionProvider {
48039
48521
  this.walletContext.activeWallet()
48040
48522
  );
48041
48523
  const confirmed = await pending.wait();
48042
- return { success: true, txHash: confirmed.txHash, chain: chainKey };
48524
+ return { success: true, hash: confirmed.hash, chain: chainKey };
48043
48525
  }
48044
48526
  };
48045
48527
  }
@@ -48063,7 +48545,7 @@ var Erc8004ActionProvider = class extends ActionProvider {
48063
48545
  this.walletContext.activeWallet()
48064
48546
  );
48065
48547
  const confirmed = await pending.wait();
48066
- return { success: true, txHash: confirmed.txHash, chain: chainKey };
48548
+ return { success: true, hash: confirmed.hash, chain: chainKey };
48067
48549
  }
48068
48550
  };
48069
48551
  }
@@ -48129,6 +48611,7 @@ var jobIdSchema = import_zod17.z.string().regex(/^\d+$/, "jobId must be a decima
48129
48611
  var amountSchema = import_zod17.z.string().regex(/^\d+$/, "amount must be an atomic (base-unit) integer string.");
48130
48612
  var evmAddressSchema = import_zod17.z.string().refine(isEvmAddress, "must be a 0x-prefixed 40-hex EVM address.");
48131
48613
  var bytes32Schema = import_zod17.z.string().regex(/^0x[0-9a-fA-F]{64}$/, "must be 0x-prefixed 32-byte hex.");
48614
+ var optParamsSchema = import_zod17.z.string().regex(/^0x([0-9a-fA-F]{2})*$/, "optParams must be 0x-prefixed bytes.").optional().describe("Optional opaque optParams bytes forwarded raw to the contract (defaults to empty).");
48132
48615
  var ZERO_BYTES322 = `0x${"0".repeat(64)}`;
48133
48616
  var Erc8183ActionProvider = class extends ActionProvider {
48134
48617
  constructor(blockchainClient, walletContext) {
@@ -48149,6 +48632,7 @@ var Erc8183ActionProvider = class extends ActionProvider {
48149
48632
  this.reject(),
48150
48633
  this.claimRefund(),
48151
48634
  this.status(),
48635
+ this.policyInfo(),
48152
48636
  this.registerJob(),
48153
48637
  this.settle(),
48154
48638
  this.markExpired(),
@@ -48190,13 +48674,13 @@ var Erc8183ActionProvider = class extends ActionProvider {
48190
48674
  if (!created) {
48191
48675
  throw new WalletError(
48192
48676
  "TX_FAILED",
48193
- `erc8183_create_job: no JobCreated event emitted (txHash=${confirmed.txHash})`
48677
+ `erc8183_create_job: no JobCreated event emitted (hash=${confirmed.hash})`
48194
48678
  );
48195
48679
  }
48196
48680
  return {
48197
48681
  success: true,
48198
48682
  jobId: created.jobId.toString(),
48199
- txHash: confirmed.txHash,
48683
+ hash: confirmed.hash,
48200
48684
  status: "Open",
48201
48685
  chain: chainKey
48202
48686
  };
@@ -48210,16 +48694,17 @@ var Erc8183ActionProvider = class extends ActionProvider {
48210
48694
  schema: import_zod17.z.object({
48211
48695
  jobId: jobIdSchema,
48212
48696
  provider: evmAddressSchema.describe("New provider (worker) address."),
48697
+ optParams: optParamsSchema,
48213
48698
  chain: import_zod17.z.string().default("bsc").describe(CHAIN_DESC)
48214
48699
  }),
48215
48700
  invoke: async (_ctx, input2) => {
48216
48701
  const { commerce, chainKey } = this.bind(input2.chain);
48217
48702
  const pending = await commerce.write.setProvider(
48218
- [BigInt(input2.jobId), input2.provider, "0x"],
48703
+ [BigInt(input2.jobId), input2.provider, input2.optParams ?? "0x"],
48219
48704
  this.walletContext.activeWallet()
48220
48705
  );
48221
48706
  const confirmed = await pending.wait();
48222
- return { success: true, txHash: confirmed.txHash, chain: chainKey };
48707
+ return { success: true, hash: confirmed.hash, chain: chainKey };
48223
48708
  }
48224
48709
  };
48225
48710
  }
@@ -48230,47 +48715,64 @@ var Erc8183ActionProvider = class extends ActionProvider {
48230
48715
  schema: import_zod17.z.object({
48231
48716
  jobId: jobIdSchema,
48232
48717
  amount: amountSchema.describe("Budget in atomic units of the payment token."),
48718
+ optParams: optParamsSchema,
48233
48719
  chain: import_zod17.z.string().default("bsc").describe(CHAIN_DESC)
48234
48720
  }),
48235
48721
  invoke: async (_ctx, input2) => {
48236
48722
  const { commerce, chainKey } = this.bind(input2.chain);
48237
48723
  const pending = await commerce.write.setBudget(
48238
- [BigInt(input2.jobId), BigInt(input2.amount), "0x"],
48724
+ [BigInt(input2.jobId), BigInt(input2.amount), input2.optParams ?? "0x"],
48239
48725
  this.walletContext.activeWallet()
48240
48726
  );
48241
48727
  const confirmed = await pending.wait();
48242
- return { success: true, txHash: confirmed.txHash, chain: chainKey };
48728
+ return { success: true, hash: confirmed.hash, chain: chainKey };
48243
48729
  }
48244
48730
  };
48245
48731
  }
48246
48732
  fund() {
48247
48733
  return {
48248
48734
  name: "erc8183_fund",
48249
- description: "Fund a job: deposits the on-chain budget into escrow. The escrow pulls the payment token via transferFrom, so this first sends an ERC-20 approval for the exact budget, then calls fund \u2014 two transactions. Moves the job to `Funded`.",
48735
+ description: "Fund a job: deposits the on-chain budget into escrow. The escrow pulls the payment token via transferFrom, so this first sends an ERC-20 approval for the exact budget, then calls fund \u2014 two transactions. Moves the job to `Funded`. Pass `expectedBudget` to pin the amount instead of reading it on-chain.",
48250
48736
  schema: import_zod17.z.object({
48251
48737
  jobId: jobIdSchema,
48738
+ expectedBudget: amountSchema.optional().describe(
48739
+ "Pin the budget (atomic units): approve and fund exactly this amount instead of reading it on-chain. The contract reverts (BudgetMismatch) if the on-chain budget differs."
48740
+ ),
48741
+ optParams: optParamsSchema,
48252
48742
  chain: import_zod17.z.string().default("bsc").describe(CHAIN_DESC)
48253
48743
  }),
48254
48744
  invoke: async (_ctx, input2) => {
48255
48745
  const { client, commerce, chainKey } = this.bind(input2.chain);
48256
48746
  const jobId = BigInt(input2.jobId);
48257
- const job = await commerce.read.getJob([jobId]);
48258
- if (job.budget === 0n) {
48259
- throw new WalletError(
48260
- "VALIDATION_ERROR",
48261
- `Job ${input2.jobId} has no budget set \u2014 call erc8183_set_budget before funding.`
48262
- );
48747
+ let budget;
48748
+ if (input2.expectedBudget !== void 0) {
48749
+ budget = BigInt(input2.expectedBudget);
48750
+ if (budget === 0n) {
48751
+ throw new WalletError("VALIDATION_ERROR", "expectedBudget must be greater than zero.");
48752
+ }
48753
+ } else {
48754
+ const job = await commerce.read.getJob([jobId]);
48755
+ if (job.budget === 0n) {
48756
+ throw new WalletError(
48757
+ "VALIDATION_ERROR",
48758
+ `Job ${input2.jobId} has no budget set \u2014 call erc8183_set_budget before funding.`
48759
+ );
48760
+ }
48761
+ budget = job.budget;
48263
48762
  }
48264
48763
  const token = await commerce.read.paymentToken([]);
48265
48764
  const wallet = this.walletContext.activeWallet();
48266
- const approvePending = await erc20.using(client).withAddress(token, chainKey).write.approve([commerce.address, job.budget], wallet);
48765
+ const approvePending = await erc20.using(client).withAddress(token, chainKey).write.approve([commerce.address, budget], wallet);
48267
48766
  const approveConfirmed = await approvePending.wait();
48268
- const fundPending = await commerce.write.fund([jobId, job.budget, "0x"], wallet);
48767
+ const fundPending = await commerce.write.fund(
48768
+ [jobId, budget, input2.optParams ?? "0x"],
48769
+ wallet
48770
+ );
48269
48771
  const fundConfirmed = await fundPending.wait();
48270
48772
  return {
48271
48773
  success: true,
48272
- approveHash: approveConfirmed.txHash,
48273
- txHash: fundConfirmed.txHash,
48774
+ approveHash: approveConfirmed.hash,
48775
+ hash: fundConfirmed.hash,
48274
48776
  status: "Funded",
48275
48777
  chain: chainKey
48276
48778
  };
@@ -48286,16 +48788,19 @@ var Erc8183ActionProvider = class extends ActionProvider {
48286
48788
  deliverable: bytes32Schema.describe(
48287
48789
  "bytes32 deliverable commitment (e.g. a content hash)."
48288
48790
  ),
48791
+ optParams: optParamsSchema.describe(
48792
+ 'Optional opaque optParams bytes forwarded raw to the contract (defaults to empty). Evaluators may expect deliverable metadata here, e.g. hex-encoded UTF-8 JSON like {"deliverable_url": "..."}.'
48793
+ ),
48289
48794
  chain: import_zod17.z.string().default("bsc").describe(CHAIN_DESC)
48290
48795
  }),
48291
48796
  invoke: async (_ctx, input2) => {
48292
48797
  const { commerce, chainKey } = this.bind(input2.chain);
48293
48798
  const pending = await commerce.write.submit(
48294
- [BigInt(input2.jobId), input2.deliverable, "0x"],
48799
+ [BigInt(input2.jobId), input2.deliverable, input2.optParams ?? "0x"],
48295
48800
  this.walletContext.activeWallet()
48296
48801
  );
48297
48802
  const confirmed = await pending.wait();
48298
- return { success: true, txHash: confirmed.txHash, status: "Submitted", chain: chainKey };
48803
+ return { success: true, hash: confirmed.hash, status: "Submitted", chain: chainKey };
48299
48804
  }
48300
48805
  };
48301
48806
  }
@@ -48306,19 +48811,20 @@ var Erc8183ActionProvider = class extends ActionProvider {
48306
48811
  schema: import_zod17.z.object({
48307
48812
  jobId: jobIdSchema,
48308
48813
  reason: bytes32Schema.optional().describe("Optional bytes32 reason/attestation (defaults to zero)."),
48814
+ optParams: optParamsSchema,
48309
48815
  chain: import_zod17.z.string().default("bsc").describe(CHAIN_DESC)
48310
48816
  }),
48311
48817
  invoke: async (_ctx, input2) => {
48312
48818
  const { commerce, chainKey } = this.bind(input2.chain);
48313
48819
  const pending = await commerce.write.complete(
48314
- [BigInt(input2.jobId), input2.reason ?? ZERO_BYTES322, "0x"],
48820
+ [BigInt(input2.jobId), input2.reason ?? ZERO_BYTES322, input2.optParams ?? "0x"],
48315
48821
  this.walletContext.activeWallet()
48316
48822
  );
48317
48823
  const confirmed = await pending.wait();
48318
48824
  const released = confirmed.events.PaymentReleased?.[0];
48319
48825
  return {
48320
48826
  success: true,
48321
- txHash: confirmed.txHash,
48827
+ hash: confirmed.hash,
48322
48828
  status: "Completed",
48323
48829
  released: released ? released.amount.toString() : null,
48324
48830
  chain: chainKey
@@ -48333,19 +48839,20 @@ var Erc8183ActionProvider = class extends ActionProvider {
48333
48839
  schema: import_zod17.z.object({
48334
48840
  jobId: jobIdSchema,
48335
48841
  reason: bytes32Schema.optional().describe("Optional bytes32 reason (defaults to zero)."),
48842
+ optParams: optParamsSchema,
48336
48843
  chain: import_zod17.z.string().default("bsc").describe(CHAIN_DESC)
48337
48844
  }),
48338
48845
  invoke: async (_ctx, input2) => {
48339
48846
  const { commerce, chainKey } = this.bind(input2.chain);
48340
48847
  const pending = await commerce.write.reject(
48341
- [BigInt(input2.jobId), input2.reason ?? ZERO_BYTES322, "0x"],
48848
+ [BigInt(input2.jobId), input2.reason ?? ZERO_BYTES322, input2.optParams ?? "0x"],
48342
48849
  this.walletContext.activeWallet()
48343
48850
  );
48344
48851
  const confirmed = await pending.wait();
48345
48852
  const refunded = confirmed.events.Refunded?.[0];
48346
48853
  return {
48347
48854
  success: true,
48348
- txHash: confirmed.txHash,
48855
+ hash: confirmed.hash,
48349
48856
  status: "Rejected",
48350
48857
  refunded: refunded ? refunded.amount.toString() : null,
48351
48858
  chain: chainKey
@@ -48371,7 +48878,7 @@ var Erc8183ActionProvider = class extends ActionProvider {
48371
48878
  const refunded = confirmed.events.Refunded?.[0];
48372
48879
  return {
48373
48880
  success: true,
48374
- txHash: confirmed.txHash,
48881
+ hash: confirmed.hash,
48375
48882
  status: "Expired",
48376
48883
  refunded: refunded ? refunded.amount.toString() : null,
48377
48884
  chain: chainKey
@@ -48413,6 +48920,46 @@ var Erc8183ActionProvider = class extends ActionProvider {
48413
48920
  }
48414
48921
  };
48415
48922
  }
48923
+ policyInfo() {
48924
+ return {
48925
+ name: "erc8183_policy_info",
48926
+ description: "Read the dispute-policy state of a job: the registered policy address, dispute window, submission time, dispute/vote tallies, and when the job becomes settleable (submittedAt + disputeWindow). Use before `erc8183_settle` to check timing, and before `erc8183_dispute`/`erc8183_vote_reject` to avoid opaque reverts.",
48927
+ schema: import_zod17.z.object({
48928
+ jobId: jobIdSchema,
48929
+ chain: import_zod17.z.string().default("bsc").describe(CHAIN_DESC)
48930
+ }),
48931
+ invoke: async (_ctx, input2) => {
48932
+ const { client, router, policy, chainKey } = this.bind(input2.chain);
48933
+ const jobId = BigInt(input2.jobId);
48934
+ const registeredPolicy = await router.read.jobPolicy([jobId]);
48935
+ const registered = !isZeroAddress(registeredPolicy);
48936
+ const policyContract = registered && registeredPolicy.toLowerCase() !== policy.address.toLowerCase() ? erc8183Policy.using(client).withAddress(registeredPolicy, chainKey) : policy;
48937
+ const [disputeWindow, submittedAt, disputed, rejectVotes, voteQuorum, quorumSnapshot] = await Promise.all([
48938
+ policyContract.read.disputeWindow([]),
48939
+ policyContract.read.submittedAt([jobId]),
48940
+ policyContract.read.disputed([jobId]),
48941
+ policyContract.read.rejectVotes([jobId]),
48942
+ policyContract.read.voteQuorum([]),
48943
+ policyContract.read.disputeQuorumSnapshot([jobId])
48944
+ ]);
48945
+ const settleableAt = submittedAt > 0n ? submittedAt + disputeWindow : null;
48946
+ return {
48947
+ success: true,
48948
+ jobId: input2.jobId,
48949
+ policy: policyContract.address,
48950
+ registered,
48951
+ disputeWindow: disputeWindow.toString(),
48952
+ submittedAt: submittedAt.toString(),
48953
+ disputed,
48954
+ rejectVotes: rejectVotes.toString(),
48955
+ voteQuorum: voteQuorum.toString(),
48956
+ quorumSnapshot: quorumSnapshot.toString(),
48957
+ settleableAt: settleableAt?.toString() ?? null,
48958
+ chain: chainKey
48959
+ };
48960
+ }
48961
+ };
48962
+ }
48416
48963
  // ----- Router -------------------------------------------------------------
48417
48964
  registerJob() {
48418
48965
  return {
@@ -48430,7 +48977,7 @@ var Erc8183ActionProvider = class extends ActionProvider {
48430
48977
  this.walletContext.activeWallet()
48431
48978
  );
48432
48979
  const confirmed = await pending.wait();
48433
- return { success: true, txHash: confirmed.txHash, chain: chainKey };
48980
+ return { success: true, hash: confirmed.hash, chain: chainKey };
48434
48981
  }
48435
48982
  };
48436
48983
  }
@@ -48450,7 +48997,7 @@ var Erc8183ActionProvider = class extends ActionProvider {
48450
48997
  this.walletContext.activeWallet()
48451
48998
  );
48452
48999
  const confirmed = await pending.wait();
48453
- return { success: true, txHash: confirmed.txHash, chain: chainKey };
49000
+ return { success: true, hash: confirmed.hash, chain: chainKey };
48454
49001
  }
48455
49002
  };
48456
49003
  }
@@ -48469,7 +49016,7 @@ var Erc8183ActionProvider = class extends ActionProvider {
48469
49016
  this.walletContext.activeWallet()
48470
49017
  );
48471
49018
  const confirmed = await pending.wait();
48472
- return { success: true, txHash: confirmed.txHash, status: "Expired", chain: chainKey };
49019
+ return { success: true, hash: confirmed.hash, status: "Expired", chain: chainKey };
48473
49020
  }
48474
49021
  };
48475
49022
  }
@@ -48489,7 +49036,7 @@ var Erc8183ActionProvider = class extends ActionProvider {
48489
49036
  this.walletContext.activeWallet()
48490
49037
  );
48491
49038
  const confirmed = await pending.wait();
48492
- return { success: true, txHash: confirmed.txHash, chain: chainKey };
49039
+ return { success: true, hash: confirmed.hash, chain: chainKey };
48493
49040
  }
48494
49041
  };
48495
49042
  }
@@ -48508,7 +49055,7 @@ var Erc8183ActionProvider = class extends ActionProvider {
48508
49055
  this.walletContext.activeWallet()
48509
49056
  );
48510
49057
  const confirmed = await pending.wait();
48511
- return { success: true, txHash: confirmed.txHash, chain: chainKey };
49058
+ return { success: true, hash: confirmed.hash, chain: chainKey };
48512
49059
  }
48513
49060
  };
48514
49061
  }
@@ -48579,14 +49126,14 @@ var CompetitionActionProvider = class extends ActionProvider {
48579
49126
  if (!confirmed.events.Registered?.[0]) {
48580
49127
  throw new WalletError(
48581
49128
  "TX_FAILED",
48582
- `competition_register: no Registered event emitted (txHash=${confirmed.txHash})`
49129
+ `competition_register: no Registered event emitted (hash=${confirmed.hash})`
48583
49130
  );
48584
49131
  }
48585
49132
  return {
48586
49133
  success: true,
48587
49134
  registered: true,
48588
49135
  participant,
48589
- txHash: confirmed.txHash,
49136
+ hash: confirmed.hash,
48590
49137
  deadline: toIso2(deadline),
48591
49138
  chain: CHAIN
48592
49139
  };
@@ -53591,7 +54138,7 @@ async function runAutomationsTick(deps) {
53591
54138
  runCount: (auto.runCount ?? 0) + 1
53592
54139
  });
53593
54140
  log("info", `DCA executed: ${res.explorerUrl}`);
53594
- result.executed.push({ id: auto.id, type: "dca", txHash: res.txHash });
54141
+ result.executed.push({ id: auto.id, type: "dca", hash: res.hash });
53595
54142
  } else {
53596
54143
  log("warn", `DCA ${auto.id} failed: ${res.error}`);
53597
54144
  result.errors++;
@@ -53639,7 +54186,7 @@ async function runAutomationsTick(deps) {
53639
54186
  lastRunAt: (/* @__PURE__ */ new Date()).toISOString()
53640
54187
  });
53641
54188
  log("info", `Limit order executed: ${res.explorerUrl}`);
53642
- result.executed.push({ id: auto.id, type: "limit", txHash: res.txHash });
54189
+ result.executed.push({ id: auto.id, type: "limit", hash: res.hash });
53643
54190
  } else {
53644
54191
  log("warn", `Limit order ${auto.id} failed: ${res.error}`);
53645
54192
  result.errors++;
@@ -54187,12 +54734,15 @@ ${url}`);
54187
54734
  amount: opts.amount,
54188
54735
  decimals,
54189
54736
  memo: opts.memo,
54737
+ // Mainnet by construction — onramp has no --chain input and derives
54738
+ // chainKey from the asset's coinId via the CHAINS-only resolver.
54739
+ chainKey,
54190
54740
  hdWallet
54191
54741
  });
54192
54742
  } catch (err) {
54193
54743
  mapBroadcastError(err);
54194
54744
  }
54195
- const explorerUrl = getExplorerTxUrl(result.chainKey, result.txHash);
54745
+ const explorerUrl = getExplorerTxUrl(result.chainKey, result.hash);
54196
54746
  success("Off-ramp payout broadcast!");
54197
54747
  if (!opts.json) {
54198
54748
  info(
@@ -54201,7 +54751,7 @@ ${url}`);
54201
54751
  }
54202
54752
  output(
54203
54753
  {
54204
- hash: result.txHash,
54754
+ hash: result.hash,
54205
54755
  chain: result.chainKey,
54206
54756
  from: address,
54207
54757
  to: opts.to,
@@ -54586,7 +55136,7 @@ function registerX402Command(program3) {
54586
55136
  confirmApproval: opts.autoApprove ? autoApproveConfirm : makeReadlineApprovalConfirm()
54587
55137
  });
54588
55138
  if (approval) {
54589
- info(`x402: Permit2 approved (one-time): tx ${approval.txHash}`);
55139
+ info(`x402: Permit2 approved (one-time): tx ${approval.hash}`);
54590
55140
  }
54591
55141
  if (payment) {
54592
55142
  info(
@@ -54701,7 +55251,7 @@ var crypto7 = __toESM(require("crypto"));
54701
55251
  // package.json
54702
55252
  var package_default = {
54703
55253
  name: "@trustwallet/cli",
54704
- version: "0.17.0",
55254
+ version: "0.19.0",
54705
55255
  description: "TWAK CLI \u2014 command-line interface for Trust Wallet Agent Kit",
54706
55256
  main: "dist/index.js",
54707
55257
  types: "dist/index.d.ts",
@@ -55077,7 +55627,7 @@ function registerWatchCommand(program3) {
55077
55627
  tickResults.automations.executed.push({
55078
55628
  id: auto.id,
55079
55629
  type: "dca",
55080
- txHash: result.txHash,
55630
+ hash: result.hash,
55081
55631
  explorer: result.explorerUrl
55082
55632
  });
55083
55633
  } else {
@@ -55145,7 +55695,7 @@ function registerWatchCommand(program3) {
55145
55695
  tickResults.automations.executed.push({
55146
55696
  id: auto.id,
55147
55697
  type: "limit",
55148
- txHash: result.txHash,
55698
+ hash: result.hash,
55149
55699
  explorer: result.explorerUrl
55150
55700
  });
55151
55701
  } else {