agentic-wallet-mcp 0.4.2 → 0.6.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.
@@ -2233,8 +2233,8 @@ var require_resolve = __commonJS({
2233
2233
  }
2234
2234
  return count;
2235
2235
  }
2236
- function getFullPath(resolver, id = "", normalize2) {
2237
- if (normalize2 !== false)
2236
+ function getFullPath(resolver, id = "", normalize3) {
2237
+ if (normalize3 !== false)
2238
2238
  id = normalizeId(id);
2239
2239
  const p = resolver.parse(id);
2240
2240
  return _getFullPath(resolver, p);
@@ -3630,7 +3630,7 @@ var require_fast_uri = __commonJS({
3630
3630
  "use strict";
3631
3631
  var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3632
3632
  var { SCHEMES, getSchemeHandler } = require_schemes();
3633
- function normalize2(uri, options) {
3633
+ function normalize3(uri, options) {
3634
3634
  if (typeof uri === "string") {
3635
3635
  uri = /** @type {T} */
3636
3636
  normalizeString(uri, options);
@@ -3897,7 +3897,7 @@ var require_fast_uri = __commonJS({
3897
3897
  }
3898
3898
  var fastUri = {
3899
3899
  SCHEMES,
3900
- normalize: normalize2,
3900
+ normalize: normalize3,
3901
3901
  resolve,
3902
3902
  resolveComponent,
3903
3903
  equal,
@@ -8541,13 +8541,13 @@ var require_path = __commonJS({
8541
8541
  return /^(?:\/|\w+:)/.test(path2);
8542
8542
  }
8543
8543
  );
8544
- var normalize2 = (
8544
+ var normalize3 = (
8545
8545
  /**
8546
8546
  * Normalizes the specified path.
8547
8547
  * @param {string} path Path to normalize
8548
8548
  * @returns {string} Normalized path
8549
8549
  */
8550
- path.normalize = function normalize3(path2) {
8550
+ path.normalize = function normalize4(path2) {
8551
8551
  path2 = path2.replace(/\\/g, "/").replace(/\/{2,}/g, "/");
8552
8552
  var parts = path2.split("/"), absolute = isAbsolute(path2), prefix = "";
8553
8553
  if (absolute)
@@ -8570,12 +8570,12 @@ var require_path = __commonJS({
8570
8570
  );
8571
8571
  path.resolve = function resolve(originPath, includePath, alreadyNormalized) {
8572
8572
  if (!alreadyNormalized)
8573
- includePath = normalize2(includePath);
8573
+ includePath = normalize3(includePath);
8574
8574
  if (isAbsolute(includePath))
8575
8575
  return includePath;
8576
8576
  if (!alreadyNormalized)
8577
- originPath = normalize2(originPath);
8578
- return (originPath = originPath.replace(/(?:\/|^)[^/]+$/, "")).length ? normalize2(originPath + "/" + includePath) : includePath;
8577
+ originPath = normalize3(originPath);
8578
+ return (originPath = originPath.replace(/(?:\/|^)[^/]+$/, "")).length ? normalize3(originPath + "/" + includePath) : includePath;
8579
8579
  };
8580
8580
  }
8581
8581
  });
@@ -12192,7 +12192,7 @@ var require_payment_engine = __commonJS({
12192
12192
  var blob_decoder_1 = require_blob_decoder();
12193
12193
  var prepare_client_1 = require_prepare_client();
12194
12194
  var _sdkCtor = require("zetrix-sdk-nodejs");
12195
- var InsufficientBalanceError = class _InsufficientBalanceError extends Error {
12195
+ var InsufficientBalanceError2 = class _InsufficientBalanceError extends Error {
12196
12196
  constructor(message, required2, available, asset) {
12197
12197
  super(message);
12198
12198
  this.required = required2;
@@ -12202,7 +12202,7 @@ var require_payment_engine = __commonJS({
12202
12202
  Object.setPrototypeOf(this, _InsufficientBalanceError.prototype);
12203
12203
  }
12204
12204
  };
12205
- exports2.InsufficientBalanceError = InsufficientBalanceError;
12205
+ exports2.InsufficientBalanceError = InsufficientBalanceError2;
12206
12206
  exports2.PaymentEngine = {
12207
12207
  /**
12208
12208
  * Create a Zetrix SDK instance for the given node config.
@@ -12295,20 +12295,20 @@ var require_payment_engine = __commonJS({
12295
12295
  const { balance } = await exports2.PaymentEngine.fetchAccountInfo(wallet.address, node);
12296
12296
  const required2 = amount + fee;
12297
12297
  if (BigInt(balance) < required2) {
12298
- throw new InsufficientBalanceError(`Insufficient ZTX: required ${required2}, available ${balance}`, String(required2), balance, "ZTX");
12298
+ throw new InsufficientBalanceError2(`Insufficient ZTX: required ${required2}, available ${balance}`, String(required2), balance, "ZTX");
12299
12299
  }
12300
12300
  return;
12301
12301
  }
12302
12302
  if (!opts.skipTokenCheck) {
12303
12303
  const { balance: tokenBal } = await exports2.PaymentEngine.fetchZTP20Balance(req.asset, wallet.address, node);
12304
12304
  if (BigInt(tokenBal) < amount) {
12305
- throw new InsufficientBalanceError(`Insufficient ${req.asset}: required ${req.maxAmountRequired}, available ${tokenBal}`, req.maxAmountRequired, tokenBal, req.asset);
12305
+ throw new InsufficientBalanceError2(`Insufficient ${req.asset}: required ${req.maxAmountRequired}, available ${tokenBal}`, req.maxAmountRequired, tokenBal, req.asset);
12306
12306
  }
12307
12307
  }
12308
12308
  if (fee > 0n) {
12309
12309
  const { balance: ztxBal } = await exports2.PaymentEngine.fetchAccountInfo(wallet.address, node);
12310
12310
  if (BigInt(ztxBal) < fee) {
12311
- throw new InsufficientBalanceError(`Insufficient ZTX for gas: required ${feeLimit}, available ${ztxBal}`, feeLimit, ztxBal, "ZTX");
12311
+ throw new InsufficientBalanceError2(`Insufficient ZTX for gas: required ${feeLimit}, available ${ztxBal}`, feeLimit, ztxBal, "ZTX");
12312
12312
  }
12313
12313
  }
12314
12314
  },
@@ -12547,9 +12547,17 @@ var import_node_path3 = require("node:path");
12547
12547
  // package.json
12548
12548
  var package_default = {
12549
12549
  name: "agentic-wallet-mcp",
12550
- version: "0.4.2",
12550
+ version: "0.6.0",
12551
12551
  description: "Agent-facing MCP wallet for Zetrix \u2014 orchestrates x401 identity proof, x402 payment, and MBI VC issuance",
12552
- keywords: ["mcp", "model-context-protocol", "zetrix", "wallet", "x401", "x402", "blockchain"],
12552
+ keywords: [
12553
+ "mcp",
12554
+ "model-context-protocol",
12555
+ "zetrix",
12556
+ "wallet",
12557
+ "x401",
12558
+ "x402",
12559
+ "blockchain"
12560
+ ],
12553
12561
  license: "MIT",
12554
12562
  author: "MyEG Services Berhad",
12555
12563
  homepage: "https://github.com/Zetrix-Chain/zetrix-agentic-wallet",
@@ -12570,6 +12578,7 @@ var package_default = {
12570
12578
  files: [
12571
12579
  "dist/server-bundle.cjs",
12572
12580
  "README.md",
12581
+ "CHANGELOG.md",
12573
12582
  "LICENSE"
12574
12583
  ],
12575
12584
  scripts: {
@@ -21159,7 +21168,7 @@ var StdioServerTransport = class {
21159
21168
  };
21160
21169
 
21161
21170
  // src/index.ts
21162
- var import_x402_zetrix_client = __toESM(require_dist2(), 1);
21171
+ var import_x402_zetrix_client2 = __toESM(require_dist2(), 1);
21163
21172
 
21164
21173
  // node_modules/x401-zetrix-client/dist/config.js
21165
21174
  var ZETRIX_OID4VP_URLS = {
@@ -21547,6 +21556,14 @@ function deriveMbiBaseUrl(network) {
21547
21556
  function deriveTemplateRegistryAddress(network) {
21548
21557
  return network.includes("testnet") ? "ZTX3JszqPgRUx743SAp7q7zURfjvkWuH2FMEz" : "ZTX3GqJM1U6ifMPonwD4fGvrgoTKJua7b2cKX";
21549
21558
  }
21559
+ var TOKEN_REGISTRY = {
21560
+ JMYR: { testnet: "ZTX3WeinXtt28YMyr4vUZ14ddTgEMGeuc1e6b", mainnet: "ZTX3NCkXBqbyJWjZZxciQez945Lu6tGAcjNJr" }
21561
+ };
21562
+ function resolveTokenAddress(symbol, network) {
21563
+ const entry = TOKEN_REGISTRY[symbol.toUpperCase()];
21564
+ if (!entry) return void 0;
21565
+ return network.includes("testnet") ? entry.testnet : entry.mainnet;
21566
+ }
21550
21567
  function loadConfig(env) {
21551
21568
  const req = (key, hint) => {
21552
21569
  const v = env[key];
@@ -21609,6 +21626,75 @@ async function resolveAssetSymbol(asset, query) {
21609
21626
  return info?.symbol ?? asset;
21610
21627
  }
21611
21628
 
21629
+ // src/clients/contract-query-client.ts
21630
+ async function queryContract(input, query) {
21631
+ let response;
21632
+ try {
21633
+ response = await query({
21634
+ contractAddress: input.contractAddress,
21635
+ input: JSON.stringify({ method: input.method, params: input.params ?? {} }),
21636
+ optType: 2
21637
+ });
21638
+ } catch (e) {
21639
+ return { ok: false, error: `query_contract: RPC call failed \u2014 ${e.message}` };
21640
+ }
21641
+ if (response.errorCode !== 0) {
21642
+ return { ok: false, error: `query_contract: contract call failed with errorCode ${response.errorCode}` };
21643
+ }
21644
+ const raw = response.result?.query_rets?.[0]?.result?.value;
21645
+ if (raw === void 0) {
21646
+ return { ok: false, error: "query_contract: no result value returned" };
21647
+ }
21648
+ try {
21649
+ return { ok: true, result: JSON.parse(raw) };
21650
+ } catch {
21651
+ return { ok: true, result: raw };
21652
+ }
21653
+ }
21654
+
21655
+ // src/clients/token-balance-client.ts
21656
+ var ZTX_DECIMALS = 6;
21657
+ async function fetchZTP20BalanceStrict(contractAddress, address, query) {
21658
+ const result = await query({
21659
+ contractAddress,
21660
+ input: JSON.stringify({ method: "balanceOf", params: { address } }),
21661
+ optType: 2
21662
+ });
21663
+ if (result?.errorCode !== 0) {
21664
+ throw new Error(`balanceOf failed with errorCode ${result?.errorCode}`);
21665
+ }
21666
+ const raw = result.result?.query_rets?.[0]?.result?.value;
21667
+ if (raw === void 0 || raw === null || raw === "") {
21668
+ throw new Error("balanceOf returned no value");
21669
+ }
21670
+ const parsed = JSON.parse(raw);
21671
+ const balance = parsed?.balance;
21672
+ if (typeof balance !== "string") {
21673
+ throw new Error("balanceOf returned no balance field");
21674
+ }
21675
+ return balance;
21676
+ }
21677
+ async function queryTokenBalance(deps, token) {
21678
+ const symbol = token.toUpperCase();
21679
+ if (symbol === "ZTX") {
21680
+ try {
21681
+ return { token: symbol, balance: await deps.fetchNativeBalance(deps.address), decimals: ZTX_DECIMALS };
21682
+ } catch {
21683
+ return { token: symbol, error: "query_failed" };
21684
+ }
21685
+ }
21686
+ const contractAddress = deps.resolveTokenAddress(symbol);
21687
+ if (!contractAddress) return { token: symbol, error: "unknown_token" };
21688
+ let balance;
21689
+ try {
21690
+ balance = await fetchZTP20BalanceStrict(contractAddress, deps.address, deps.query);
21691
+ } catch {
21692
+ return { token: symbol, error: "query_failed" };
21693
+ }
21694
+ const info = await fetchTokenInfo(contractAddress, deps.query);
21695
+ return { token: symbol, balance, decimals: info?.decimals ?? null };
21696
+ }
21697
+
21612
21698
  // src/clients/template-info-client.ts
21613
21699
  async function fetchTemplateFields(templateId, registryAddress, nodeBaseUrl, query) {
21614
21700
  const key = `template__${templateId}`;
@@ -21667,6 +21753,10 @@ var WalletBeClient = class {
21667
21753
  createAccount(password, label, purpose) {
21668
21754
  return this.post("/wallet/hsm/account/create", { password, label, purpose });
21669
21755
  }
21756
+ /** GET /wallet/hsm/account/activate/status — ground-truth on-chain activation check (no local state). */
21757
+ checkActivationStatus(address) {
21758
+ return this.get("/wallet/hsm/account/activate/status", { address });
21759
+ }
21670
21760
  async post(path, body) {
21671
21761
  const url = `${this.baseUrl}${path}`;
21672
21762
  let res;
@@ -21694,6 +21784,25 @@ var WalletBeClient = class {
21694
21784
  }
21695
21785
  return env.data;
21696
21786
  }
21787
+ async get(path, query) {
21788
+ const url = `${this.baseUrl}${path}?${new URLSearchParams(query).toString()}`;
21789
+ let res;
21790
+ try {
21791
+ res = await fetch(url, { headers: { Accept: "application/json" } });
21792
+ } catch (e) {
21793
+ throw new WalletBeError(`Wallet BE ${path} request failed`, void 0, e);
21794
+ }
21795
+ if (!res.ok) {
21796
+ const text = await res.text().catch(() => "");
21797
+ throw new WalletBeError(`Wallet BE ${path} HTTP ${res.status}: ${text}`);
21798
+ }
21799
+ const env = await res.json();
21800
+ if (env.errorCode !== 0) {
21801
+ const detail = env.data?.errorList?.length ? ` \u2014 ${env.data.errorList.join("; ")}` : "";
21802
+ throw new WalletBeError(`Wallet BE ${path} errorCode ${env.errorCode}: ${env.message ?? "error"}${detail}`, env.errorCode);
21803
+ }
21804
+ return env.data;
21805
+ }
21697
21806
  };
21698
21807
 
21699
21808
  // src/signer.ts
@@ -21803,16 +21912,35 @@ var MbiVpAdapter = class {
21803
21912
  // src/clients/mbi-client.ts
21804
21913
  var MbiError = class extends Error {
21805
21914
  httpStatus;
21806
- constructor(message, httpStatus) {
21915
+ /**
21916
+ * MBI's own numeric code from the ResponseWrapper body — finer-grained than the HTTP status
21917
+ * and the only reliable way to tell a *definitive* post-payment failure from an indeterminate
21918
+ * one. Undefined when the body carries no numeric `status`.
21919
+ */
21920
+ mbiStatus;
21921
+ constructor(message, httpStatus, mbiStatus) {
21807
21922
  super(message);
21808
21923
  this.name = "MbiError";
21809
21924
  this.httpStatus = httpStatus;
21925
+ this.mbiStatus = mbiStatus;
21810
21926
  }
21811
21927
  };
21928
+ var MBI_SETTLEMENT_INDETERMINATE = 4012;
21812
21929
  var MbiClient = class _MbiClient {
21813
21930
  baseUrl;
21814
- constructor(baseUrl) {
21931
+ /** Per-request deadline in ms. See {@link MbiClient.DEFAULT_TIMEOUT_MS}. */
21932
+ timeoutMs;
21933
+ /**
21934
+ * MBI's own facilitator read timeout is 60s, and `/settle` submits
21935
+ * a transaction on chain. A client deadline at or below that would abort a settle MBI is
21936
+ * still legitimately waiting on — manufacturing exactly the indeterminate outcome that MR
21937
+ * exists to prevent, except on our side where nothing records it. Sits above 60s so MBI
21938
+ * always gets to answer first; raise it in step if MBI's own timeout is ever raised.
21939
+ */
21940
+ static DEFAULT_TIMEOUT_MS = 9e4;
21941
+ constructor(baseUrl, opts) {
21815
21942
  this.baseUrl = baseUrl.replace(/\/+$/, "");
21943
+ this.timeoutMs = opts?.timeoutMs ?? _MbiClient.DEFAULT_TIMEOUT_MS;
21816
21944
  }
21817
21945
  /**
21818
21946
  * Phase 1 — POST /v1/vc/pay/apply without X-PAYMENT; expects the 402 challenge.
@@ -21862,6 +21990,7 @@ var MbiClient = class _MbiClient {
21862
21990
  return fetch(`${this.baseUrl}${path}`, {
21863
21991
  method,
21864
21992
  headers,
21993
+ signal: AbortSignal.timeout(this.timeoutMs),
21865
21994
  ...body !== void 0 ? { body: JSON.stringify(body) } : {}
21866
21995
  }).catch((e) => {
21867
21996
  throw new MbiError(`MBI ${path} request failed: ${e.message}`);
@@ -21876,13 +22005,15 @@ var MbiClient = class _MbiClient {
21876
22005
  async error(res, context) {
21877
22006
  const text = await res.text().catch(() => "");
21878
22007
  let msg = text;
22008
+ let mbiStatus;
21879
22009
  try {
21880
22010
  const j = JSON.parse(text);
22011
+ if (typeof j.status === "number") mbiStatus = j.status;
21881
22012
  const truncated = text.length > _MbiClient.ERROR_BODY_MAX_LEN ? `${text.slice(0, _MbiClient.ERROR_BODY_MAX_LEN)}\u2026 (truncated, ${text.length} bytes total)` : text;
21882
22013
  msg = `${j.message ?? j.error ?? text} | full body: ${truncated}`;
21883
22014
  } catch {
21884
22015
  }
21885
- return new MbiError(`MBI ${context} \u2014 HTTP ${res.status}: ${msg}`, res.status);
22016
+ return new MbiError(`MBI ${context} \u2014 HTTP ${res.status}: ${msg}`, res.status, mbiStatus);
21886
22017
  }
21887
22018
  };
21888
22019
 
@@ -22027,6 +22158,19 @@ function isVcValid(entry, now = /* @__PURE__ */ new Date()) {
22027
22158
  if (Number.isNaN(expiry.getTime())) return false;
22028
22159
  return expiry.getTime() > now.getTime();
22029
22160
  }
22161
+ function extractAttributeKeys(vc) {
22162
+ if (typeof vc !== "object" || vc === null) return [];
22163
+ const subject = vc.credentialSubject;
22164
+ if (typeof subject !== "object" || subject === null) return [];
22165
+ const keys = /* @__PURE__ */ new Set();
22166
+ for (const [key, value] of Object.entries(subject)) {
22167
+ if (key === "id") continue;
22168
+ if (typeof value === "object" && value !== null) {
22169
+ for (const inner of Object.keys(value)) keys.add(inner);
22170
+ }
22171
+ }
22172
+ return [...keys];
22173
+ }
22030
22174
  function extractValidUntil(vc, fallback) {
22031
22175
  if (typeof vc === "object" && vc !== null && "validUntil" in vc) {
22032
22176
  const v = vc.validUntil;
@@ -22035,36 +22179,99 @@ function extractValidUntil(vc, fallback) {
22035
22179
  return fallback;
22036
22180
  }
22037
22181
 
22182
+ // src/payment-readiness.ts
22183
+ var import_x402_zetrix_client = __toESM(require_dist2(), 1);
22184
+ var PaymentReadinessError = class extends Error {
22185
+ shortfall;
22186
+ constructor(message, shortfall) {
22187
+ super(message);
22188
+ this.name = "PaymentReadinessError";
22189
+ this.shortfall = shortfall;
22190
+ }
22191
+ };
22192
+ function toPaymentReadinessError(err, requestedAsset) {
22193
+ if (!(err instanceof import_x402_zetrix_client.InsufficientBalanceError)) return null;
22194
+ const reason = err.asset === "ZTX" && requestedAsset !== "ZTX" ? "gas" : "resource_payment";
22195
+ return new PaymentReadinessError(err.message, {
22196
+ asset: err.asset,
22197
+ required: err.required,
22198
+ available: err.available,
22199
+ reason
22200
+ });
22201
+ }
22202
+ async function payWithReadinessCheck(requestedAsset, rawPay) {
22203
+ try {
22204
+ return await rawPay();
22205
+ } catch (err) {
22206
+ const readinessError = toPaymentReadinessError(err, requestedAsset);
22207
+ if (readinessError) throw readinessError;
22208
+ throw err;
22209
+ }
22210
+ }
22211
+
22038
22212
  // src/orchestrator/subscribe.ts
22213
+ function diffAgainstTemplate(vc, fields) {
22214
+ const held = new Set(extractAttributeKeys(vc));
22215
+ const missing = fields.required.filter((k) => !held.has(k));
22216
+ const dropped = [...held].filter((k) => !fields.allKeys.includes(k));
22217
+ return missing.length > 0 || dropped.length > 0 ? { missing, dropped } : void 0;
22218
+ }
22219
+ var TERMINAL_SETTLEMENT_STATUSES = /* @__PURE__ */ new Set(["ISSUED", "FAILED"]);
22220
+ var RECOVERY_MAX_POLLS = 5;
22221
+ var RECOVERY_POLL_DELAY_MS = 2e3;
22222
+ async function pollSettlementOutcome(deps, paymentId) {
22223
+ if (!deps.mbi.getStatus) return void 0;
22224
+ const sleep = deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
22225
+ let last;
22226
+ for (let polls = 1; polls <= RECOVERY_MAX_POLLS; polls++) {
22227
+ let status;
22228
+ try {
22229
+ status = await deps.mbi.getStatus(paymentId);
22230
+ } catch {
22231
+ return { status: "UNKNOWN", ...last ? { txHash: last.txHash, vcId: last.vcId } : {}, polls };
22232
+ }
22233
+ last = { status: status.status, txHash: status.txHash, vcId: status.vcId };
22234
+ if (TERMINAL_SETTLEMENT_STATUSES.has(status.status)) {
22235
+ return { status: status.status, txHash: status.txHash, vcId: status.vcId, polls };
22236
+ }
22237
+ if (polls < RECOVERY_MAX_POLLS) await sleep(RECOVERY_POLL_DELAY_MS);
22238
+ }
22239
+ return { ...last, polls: RECOVERY_MAX_POLLS };
22240
+ }
22039
22241
  async function subscribeAndIssue(deps, opts) {
22040
22242
  if (!/^did:zid:/.test(opts.templateId)) {
22041
22243
  return { issued: false, reason: `templateId must be a did:zid:... credential-definition id, got "${opts.templateId}"` };
22042
22244
  }
22245
+ const fields = deps.resolveTemplateFields ? await deps.resolveTemplateFields(opts.templateId) : null;
22246
+ const schema = fields ? { required: fields.required, optional: fields.allKeys.filter((k) => !fields.required.includes(k)) } : void 0;
22043
22247
  if (deps.cache && !opts.forceReissue && !opts.dryRun) {
22044
22248
  const cached2 = await deps.cache.get(opts.templateId);
22045
22249
  if (cached2 && isVcValid(cached2)) {
22250
+ const originalPayment = { txHash: cached2.txHash, asset: cached2.paidAsset, amount: cached2.amountPaid };
22251
+ const paidOriginally = cached2.amountPaid !== void 0 && cached2.amountPaid !== "0";
22252
+ const staleAttributes = fields ? diffAgainstTemplate(cached2.vc, fields) : void 0;
22046
22253
  return {
22047
22254
  issued: true,
22048
22255
  vcId: cached2.vcId,
22049
22256
  vc: cached2.vc,
22050
- txHash: cached2.txHash,
22051
- paidAsset: cached2.paidAsset,
22052
- amountPaid: cached2.amountPaid,
22053
- fromCache: true
22257
+ fromCache: true,
22258
+ ...paidOriginally ? { originalPayment } : {},
22259
+ ...schema ? { schema } : {},
22260
+ ...staleAttributes ? { staleAttributes } : {}
22054
22261
  };
22055
22262
  }
22056
22263
  }
22057
- const fields = deps.resolveTemplateFields ? await deps.resolveTemplateFields(opts.templateId) : null;
22058
22264
  const { agentDid, ...rest } = opts.attributes ?? {};
22059
22265
  const shouldAutoFillAgentDid = !agentDid && fields !== null && fields.allKeys.includes("agentDid");
22060
22266
  const attributes = shouldAutoFillAgentDid ? { agentDid: deps.holderDid, ...rest } : opts.attributes ?? {};
22061
- if (!opts.dryRun && fields) {
22267
+ if (fields) {
22062
22268
  const attrs = attributes;
22063
22269
  const missing = fields.required.filter((k) => attrs[k] === void 0 || attrs[k] === null || attrs[k] === "");
22064
22270
  if (missing.length > 0) {
22065
22271
  return {
22066
22272
  issued: false,
22067
- reason: `template requires attribute(s) not supplied: ${missing.join(", ")} \u2014 no payment made`
22273
+ reason: `template requires attribute(s) not supplied: ${missing.join(", ")} \u2014 no payment made`,
22274
+ ...schema ? { schema } : {}
22068
22275
  };
22069
22276
  }
22070
22277
  }
@@ -22073,7 +22280,15 @@ async function subscribeAndIssue(deps, opts) {
22073
22280
  const { signBlob: signData, publicKey } = await deps.sign(blob);
22074
22281
  const body = { data, signData, publicKey };
22075
22282
  if (opts.expirationDate) body.expirationDate = opts.expirationDate;
22076
- const challenge = await deps.mbi.applyChallenge(body);
22283
+ let challenge;
22284
+ try {
22285
+ challenge = await deps.mbi.applyChallenge(body);
22286
+ } catch (err) {
22287
+ if (err instanceof MbiError) {
22288
+ return { issued: false, reason: err.message, httpStatus: err.httpStatus, ...schema ? { schema } : {} };
22289
+ }
22290
+ throw err;
22291
+ }
22077
22292
  if (challenge.issued) {
22078
22293
  const issued2 = challenge.issued;
22079
22294
  if (deps.cache) {
@@ -22095,11 +22310,12 @@ async function subscribeAndIssue(deps, opts) {
22095
22310
  txHash: issued2.txHash,
22096
22311
  paidAsset: "none",
22097
22312
  amountPaid: "0",
22313
+ ...schema ? { schema } : {},
22098
22314
  ...opts.dryRun ? { reason: "this template requires no payment \u2014 MBI issues synchronously at phase 1, so dryRun could not prevent this issuance" } : {}
22099
22315
  };
22100
22316
  }
22101
22317
  const accept = challenge.accepts[0];
22102
- if (!accept) return { issued: false, reason: "MBI 402 returned no payment options" };
22318
+ if (!accept) return { issued: false, reason: "MBI 402 returned no payment options", ...schema ? { schema } : {} };
22103
22319
  if (opts.dryRun) {
22104
22320
  const quotedRaw = String(accept.asset ?? "");
22105
22321
  const quotedAsset = deps.resolveSymbol ? await deps.resolveSymbol(quotedRaw) : quotedRaw;
@@ -22109,16 +22325,41 @@ async function subscribeAndIssue(deps, opts) {
22109
22325
  quote: {
22110
22326
  asset: quotedAsset,
22111
22327
  maxAmountRequired: accept.maxAmountRequired,
22112
- payTo: accept.payTo,
22113
- ...fields ? { requiredAttributes: fields.required } : {}
22114
- }
22328
+ payTo: accept.payTo
22329
+ },
22330
+ ...schema ? { schema } : {}
22115
22331
  };
22116
22332
  }
22117
- const xPayment = await deps.pay(accept);
22118
- const issued = await deps.mbi.applySettle({ ...body, paymentId: challenge.paymentId }, xPayment);
22333
+ let xPayment;
22334
+ try {
22335
+ xPayment = await deps.pay(accept);
22336
+ } catch (err) {
22337
+ if (err instanceof PaymentReadinessError) {
22338
+ return { issued: false, reason: `insufficient funds: ${err.message}`, insufficientFunds: err.shortfall };
22339
+ }
22340
+ throw err;
22341
+ }
22119
22342
  const rawAsset = String(accept.asset ?? "");
22120
22343
  const paidAsset = deps.resolveSymbol ? await deps.resolveSymbol(rawAsset) : rawAsset;
22121
22344
  const amountPaid = String(accept.maxAmountRequired ?? "");
22345
+ let issued;
22346
+ try {
22347
+ issued = await deps.mbi.applySettle({ ...body, paymentId: challenge.paymentId }, xPayment);
22348
+ } catch (err) {
22349
+ if (err instanceof MbiError) {
22350
+ const paymentAttempted = { asset: paidAsset, amount: amountPaid, paymentId: challenge.paymentId };
22351
+ const recovery = err.mbiStatus === MBI_SETTLEMENT_INDETERMINATE && challenge.paymentId ? await pollSettlementOutcome(deps, challenge.paymentId) : void 0;
22352
+ return {
22353
+ issued: false,
22354
+ reason: err.message,
22355
+ httpStatus: err.httpStatus,
22356
+ ...schema ? { schema } : {},
22357
+ paymentAttempted,
22358
+ ...recovery ? { recovery } : {}
22359
+ };
22360
+ }
22361
+ throw err;
22362
+ }
22122
22363
  if (deps.cache) {
22123
22364
  await deps.cache.set(opts.templateId, {
22124
22365
  templateId: opts.templateId,
@@ -22137,10 +22378,23 @@ async function subscribeAndIssue(deps, opts) {
22137
22378
  vc: issued.verifiableCredential,
22138
22379
  txHash: issued.txHash,
22139
22380
  paidAsset,
22140
- amountPaid
22381
+ amountPaid,
22382
+ ...schema ? { schema } : {}
22141
22383
  };
22142
22384
  }
22143
22385
 
22386
+ // src/orchestrator/wait-for-activation.ts
22387
+ async function waitForActivation(check, address, sleep, opts = {}) {
22388
+ const attempts = opts.attempts ?? 3;
22389
+ const delayMs = opts.delayMs ?? 3e3;
22390
+ for (let i = 0; i < attempts; i++) {
22391
+ await sleep(delayMs);
22392
+ const { activated } = await check(address);
22393
+ if (activated) return true;
22394
+ }
22395
+ return false;
22396
+ }
22397
+
22144
22398
  // src/orchestrator/onboard.ts
22145
22399
  function deriveHolderDid(publicKeyHex) {
22146
22400
  const hex = publicKeyHex.trim();
@@ -22158,31 +22412,383 @@ async function createHolderAccount(deps, input) {
22158
22412
  message: `An account already exists for this wallet (zetrixAddress=${existing.zetrixAddress}, holderDid=${existing.holderDid}). Ask the user whether to keep using it or create a brand-new one \u2014 call create_holder_account again with confirmNew:true to mint a new account.`
22159
22413
  };
22160
22414
  }
22161
- const { zetrixAddress, publicKeyHex } = await deps.create(input.password, input.label, input.purpose);
22415
+ const { zetrixAddress, publicKeyHex, activated } = await deps.create(input.password, input.label, input.purpose);
22162
22416
  const holderDid = deriveHolderDid(publicKeyHex);
22163
22417
  await deps.saveAccount({ zetrixAddress, holderDid, hsmPassword: input.password, label: input.label, purpose: input.purpose });
22164
- const message = `New holder HSM account created \u2014 address, DID, and password saved to the wallet's local account store. Both will be used automatically on the next server restart; no manual config edit needed. If your MCP config also sets ZETRIX_ADDRESS/HSM_PASSWORD via environment variables, update or remove those too: an explicit env ZETRIX_ADDRESS/HSM_PASSWORD always takes precedence over the saved account. ZETRIX_ADDRESS=${zetrixAddress} (HOLDER_DID=${holderDid} is optional \u2014 it re-derives automatically).`;
22165
- return { created: true, alreadyExists: Boolean(existing), zetrixAddress, holderDid, publicKeyHex, message };
22418
+ const finalActivated = activated || await waitForActivation(deps.checkActivationStatus, zetrixAddress, deps.sleep);
22419
+ const message = `New holder HSM account created \u2014 address, DID, and password saved to the wallet's local account store. Both will be used automatically on the next server restart; no manual config edit needed. If your MCP config also sets ZETRIX_ADDRESS/HSM_PASSWORD via environment variables, update or remove those too: an explicit env ZETRIX_ADDRESS/HSM_PASSWORD always takes precedence over the saved account. ZETRIX_ADDRESS=${zetrixAddress} (HOLDER_DID=${holderDid} is optional \u2014 it re-derives automatically).` + (finalActivated ? "" : ` Note: on-chain activation has not completed yet \u2014 balance/on-chain calls for this address may fail until it does; check again later.`);
22420
+ return { created: true, alreadyExists: Boolean(existing), zetrixAddress, holderDid, publicKeyHex, activated: finalActivated, message };
22421
+ }
22422
+
22423
+ // src/iso3166.ts
22424
+ var COUNTRIES = [
22425
+ { alpha2: "AD", alpha3: "AND", name: "Andorra" },
22426
+ { alpha2: "AE", alpha3: "ARE", name: "United Arab Emirates" },
22427
+ { alpha2: "AF", alpha3: "AFG", name: "Afghanistan" },
22428
+ { alpha2: "AG", alpha3: "ATG", name: "Antigua and Barbuda" },
22429
+ { alpha2: "AI", alpha3: "AIA", name: "Anguilla" },
22430
+ { alpha2: "AL", alpha3: "ALB", name: "Albania" },
22431
+ { alpha2: "AM", alpha3: "ARM", name: "Armenia" },
22432
+ { alpha2: "AO", alpha3: "AGO", name: "Angola" },
22433
+ { alpha2: "AQ", alpha3: "ATA", name: "Antarctica" },
22434
+ { alpha2: "AR", alpha3: "ARG", name: "Argentina" },
22435
+ { alpha2: "AS", alpha3: "ASM", name: "American Samoa" },
22436
+ { alpha2: "AT", alpha3: "AUT", name: "Austria" },
22437
+ { alpha2: "AU", alpha3: "AUS", name: "Australia" },
22438
+ { alpha2: "AW", alpha3: "ABW", name: "Aruba" },
22439
+ { alpha2: "AX", alpha3: "ALA", name: "\xC5land Islands" },
22440
+ { alpha2: "AZ", alpha3: "AZE", name: "Azerbaijan" },
22441
+ { alpha2: "BA", alpha3: "BIH", name: "Bosnia and Herzegovina" },
22442
+ { alpha2: "BB", alpha3: "BRB", name: "Barbados" },
22443
+ { alpha2: "BD", alpha3: "BGD", name: "Bangladesh" },
22444
+ { alpha2: "BE", alpha3: "BEL", name: "Belgium" },
22445
+ { alpha2: "BF", alpha3: "BFA", name: "Burkina Faso" },
22446
+ { alpha2: "BG", alpha3: "BGR", name: "Bulgaria" },
22447
+ { alpha2: "BH", alpha3: "BHR", name: "Bahrain" },
22448
+ { alpha2: "BI", alpha3: "BDI", name: "Burundi" },
22449
+ { alpha2: "BJ", alpha3: "BEN", name: "Benin" },
22450
+ { alpha2: "BL", alpha3: "BLM", name: "Saint Barth\xE9lemy" },
22451
+ { alpha2: "BM", alpha3: "BMU", name: "Bermuda" },
22452
+ { alpha2: "BN", alpha3: "BRN", name: "Brunei Darussalam" },
22453
+ { alpha2: "BO", alpha3: "BOL", name: "Bolivia (Plurinational State of)" },
22454
+ { alpha2: "BQ", alpha3: "BES", name: "Bonaire, Sint Eustatius and Saba" },
22455
+ { alpha2: "BR", alpha3: "BRA", name: "Brazil" },
22456
+ { alpha2: "BS", alpha3: "BHS", name: "Bahamas" },
22457
+ { alpha2: "BT", alpha3: "BTN", name: "Bhutan" },
22458
+ { alpha2: "BV", alpha3: "BVT", name: "Bouvet Island" },
22459
+ { alpha2: "BW", alpha3: "BWA", name: "Botswana" },
22460
+ { alpha2: "BY", alpha3: "BLR", name: "Belarus" },
22461
+ { alpha2: "BZ", alpha3: "BLZ", name: "Belize" },
22462
+ { alpha2: "CA", alpha3: "CAN", name: "Canada" },
22463
+ { alpha2: "CC", alpha3: "CCK", name: "Cocos (Keeling) Islands" },
22464
+ { alpha2: "CD", alpha3: "COD", name: "Congo, Democratic Republic of the" },
22465
+ { alpha2: "CF", alpha3: "CAF", name: "Central African Republic" },
22466
+ { alpha2: "CG", alpha3: "COG", name: "Congo" },
22467
+ { alpha2: "CH", alpha3: "CHE", name: "Switzerland" },
22468
+ { alpha2: "CI", alpha3: "CIV", name: "C\xF4te d'Ivoire" },
22469
+ { alpha2: "CK", alpha3: "COK", name: "Cook Islands" },
22470
+ { alpha2: "CL", alpha3: "CHL", name: "Chile" },
22471
+ { alpha2: "CM", alpha3: "CMR", name: "Cameroon" },
22472
+ { alpha2: "CN", alpha3: "CHN", name: "China" },
22473
+ { alpha2: "CO", alpha3: "COL", name: "Colombia" },
22474
+ { alpha2: "CR", alpha3: "CRI", name: "Costa Rica" },
22475
+ { alpha2: "CU", alpha3: "CUB", name: "Cuba" },
22476
+ { alpha2: "CV", alpha3: "CPV", name: "Cabo Verde" },
22477
+ { alpha2: "CW", alpha3: "CUW", name: "Cura\xE7ao" },
22478
+ { alpha2: "CX", alpha3: "CXR", name: "Christmas Island" },
22479
+ { alpha2: "CY", alpha3: "CYP", name: "Cyprus" },
22480
+ { alpha2: "CZ", alpha3: "CZE", name: "Czechia" },
22481
+ { alpha2: "DE", alpha3: "DEU", name: "Germany" },
22482
+ { alpha2: "DJ", alpha3: "DJI", name: "Djibouti" },
22483
+ { alpha2: "DK", alpha3: "DNK", name: "Denmark" },
22484
+ { alpha2: "DM", alpha3: "DMA", name: "Dominica" },
22485
+ { alpha2: "DO", alpha3: "DOM", name: "Dominican Republic" },
22486
+ { alpha2: "DZ", alpha3: "DZA", name: "Algeria" },
22487
+ { alpha2: "EC", alpha3: "ECU", name: "Ecuador" },
22488
+ { alpha2: "EE", alpha3: "EST", name: "Estonia" },
22489
+ { alpha2: "EG", alpha3: "EGY", name: "Egypt" },
22490
+ { alpha2: "EH", alpha3: "ESH", name: "Western Sahara" },
22491
+ { alpha2: "ER", alpha3: "ERI", name: "Eritrea" },
22492
+ { alpha2: "ES", alpha3: "ESP", name: "Spain" },
22493
+ { alpha2: "ET", alpha3: "ETH", name: "Ethiopia" },
22494
+ { alpha2: "FI", alpha3: "FIN", name: "Finland" },
22495
+ { alpha2: "FJ", alpha3: "FJI", name: "Fiji" },
22496
+ { alpha2: "FK", alpha3: "FLK", name: "Falkland Islands (Malvinas)" },
22497
+ { alpha2: "FM", alpha3: "FSM", name: "Micronesia (Federated States of)" },
22498
+ { alpha2: "FO", alpha3: "FRO", name: "Faroe Islands" },
22499
+ { alpha2: "FR", alpha3: "FRA", name: "France" },
22500
+ { alpha2: "GA", alpha3: "GAB", name: "Gabon" },
22501
+ { alpha2: "GB", alpha3: "GBR", name: "United Kingdom of Great Britain and Northern Ireland" },
22502
+ { alpha2: "GD", alpha3: "GRD", name: "Grenada" },
22503
+ { alpha2: "GE", alpha3: "GEO", name: "Georgia" },
22504
+ { alpha2: "GF", alpha3: "GUF", name: "French Guiana" },
22505
+ { alpha2: "GG", alpha3: "GGY", name: "Guernsey" },
22506
+ { alpha2: "GH", alpha3: "GHA", name: "Ghana" },
22507
+ { alpha2: "GI", alpha3: "GIB", name: "Gibraltar" },
22508
+ { alpha2: "GL", alpha3: "GRL", name: "Greenland" },
22509
+ { alpha2: "GM", alpha3: "GMB", name: "Gambia" },
22510
+ { alpha2: "GN", alpha3: "GIN", name: "Guinea" },
22511
+ { alpha2: "GP", alpha3: "GLP", name: "Guadeloupe" },
22512
+ { alpha2: "GQ", alpha3: "GNQ", name: "Equatorial Guinea" },
22513
+ { alpha2: "GR", alpha3: "GRC", name: "Greece" },
22514
+ { alpha2: "GS", alpha3: "SGS", name: "South Georgia and the South Sandwich Islands" },
22515
+ { alpha2: "GT", alpha3: "GTM", name: "Guatemala" },
22516
+ { alpha2: "GU", alpha3: "GUM", name: "Guam" },
22517
+ { alpha2: "GW", alpha3: "GNB", name: "Guinea-Bissau" },
22518
+ { alpha2: "GY", alpha3: "GUY", name: "Guyana" },
22519
+ { alpha2: "HK", alpha3: "HKG", name: "Hong Kong" },
22520
+ { alpha2: "HM", alpha3: "HMD", name: "Heard Island and McDonald Islands" },
22521
+ { alpha2: "HN", alpha3: "HND", name: "Honduras" },
22522
+ { alpha2: "HR", alpha3: "HRV", name: "Croatia" },
22523
+ { alpha2: "HT", alpha3: "HTI", name: "Haiti" },
22524
+ { alpha2: "HU", alpha3: "HUN", name: "Hungary" },
22525
+ { alpha2: "ID", alpha3: "IDN", name: "Indonesia" },
22526
+ { alpha2: "IE", alpha3: "IRL", name: "Ireland" },
22527
+ { alpha2: "IL", alpha3: "ISR", name: "Israel" },
22528
+ { alpha2: "IM", alpha3: "IMN", name: "Isle of Man" },
22529
+ { alpha2: "IN", alpha3: "IND", name: "India" },
22530
+ { alpha2: "IO", alpha3: "IOT", name: "British Indian Ocean Territory" },
22531
+ { alpha2: "IQ", alpha3: "IRQ", name: "Iraq" },
22532
+ { alpha2: "IR", alpha3: "IRN", name: "Iran (Islamic Republic of)" },
22533
+ { alpha2: "IS", alpha3: "ISL", name: "Iceland" },
22534
+ { alpha2: "IT", alpha3: "ITA", name: "Italy" },
22535
+ { alpha2: "JE", alpha3: "JEY", name: "Jersey" },
22536
+ { alpha2: "JM", alpha3: "JAM", name: "Jamaica" },
22537
+ { alpha2: "JO", alpha3: "JOR", name: "Jordan" },
22538
+ { alpha2: "JP", alpha3: "JPN", name: "Japan" },
22539
+ { alpha2: "KE", alpha3: "KEN", name: "Kenya" },
22540
+ { alpha2: "KG", alpha3: "KGZ", name: "Kyrgyzstan" },
22541
+ { alpha2: "KH", alpha3: "KHM", name: "Cambodia" },
22542
+ { alpha2: "KI", alpha3: "KIR", name: "Kiribati" },
22543
+ { alpha2: "KM", alpha3: "COM", name: "Comoros" },
22544
+ { alpha2: "KN", alpha3: "KNA", name: "Saint Kitts and Nevis" },
22545
+ { alpha2: "KP", alpha3: "PRK", name: "Korea (Democratic People's Republic of)" },
22546
+ { alpha2: "KR", alpha3: "KOR", name: "Korea, Republic of" },
22547
+ { alpha2: "KW", alpha3: "KWT", name: "Kuwait" },
22548
+ { alpha2: "KY", alpha3: "CYM", name: "Cayman Islands" },
22549
+ { alpha2: "KZ", alpha3: "KAZ", name: "Kazakhstan" },
22550
+ { alpha2: "LA", alpha3: "LAO", name: "Lao People's Democratic Republic" },
22551
+ { alpha2: "LB", alpha3: "LBN", name: "Lebanon" },
22552
+ { alpha2: "LC", alpha3: "LCA", name: "Saint Lucia" },
22553
+ { alpha2: "LI", alpha3: "LIE", name: "Liechtenstein" },
22554
+ { alpha2: "LK", alpha3: "LKA", name: "Sri Lanka" },
22555
+ { alpha2: "LR", alpha3: "LBR", name: "Liberia" },
22556
+ { alpha2: "LS", alpha3: "LSO", name: "Lesotho" },
22557
+ { alpha2: "LT", alpha3: "LTU", name: "Lithuania" },
22558
+ { alpha2: "LU", alpha3: "LUX", name: "Luxembourg" },
22559
+ { alpha2: "LV", alpha3: "LVA", name: "Latvia" },
22560
+ { alpha2: "LY", alpha3: "LBY", name: "Libya" },
22561
+ { alpha2: "MA", alpha3: "MAR", name: "Morocco" },
22562
+ { alpha2: "MC", alpha3: "MCO", name: "Monaco" },
22563
+ { alpha2: "MD", alpha3: "MDA", name: "Moldova, Republic of" },
22564
+ { alpha2: "ME", alpha3: "MNE", name: "Montenegro" },
22565
+ { alpha2: "MF", alpha3: "MAF", name: "Saint Martin (French part)" },
22566
+ { alpha2: "MG", alpha3: "MDG", name: "Madagascar" },
22567
+ { alpha2: "MH", alpha3: "MHL", name: "Marshall Islands" },
22568
+ { alpha2: "MK", alpha3: "MKD", name: "North Macedonia" },
22569
+ { alpha2: "ML", alpha3: "MLI", name: "Mali" },
22570
+ { alpha2: "MM", alpha3: "MMR", name: "Myanmar" },
22571
+ { alpha2: "MN", alpha3: "MNG", name: "Mongolia" },
22572
+ { alpha2: "MO", alpha3: "MAC", name: "Macao" },
22573
+ { alpha2: "MP", alpha3: "MNP", name: "Northern Mariana Islands" },
22574
+ { alpha2: "MQ", alpha3: "MTQ", name: "Martinique" },
22575
+ { alpha2: "MR", alpha3: "MRT", name: "Mauritania" },
22576
+ { alpha2: "MS", alpha3: "MSR", name: "Montserrat" },
22577
+ { alpha2: "MT", alpha3: "MLT", name: "Malta" },
22578
+ { alpha2: "MU", alpha3: "MUS", name: "Mauritius" },
22579
+ { alpha2: "MV", alpha3: "MDV", name: "Maldives" },
22580
+ { alpha2: "MW", alpha3: "MWI", name: "Malawi" },
22581
+ { alpha2: "MX", alpha3: "MEX", name: "Mexico" },
22582
+ { alpha2: "MY", alpha3: "MYS", name: "Malaysia" },
22583
+ { alpha2: "MZ", alpha3: "MOZ", name: "Mozambique" },
22584
+ { alpha2: "NA", alpha3: "NAM", name: "Namibia" },
22585
+ { alpha2: "NC", alpha3: "NCL", name: "New Caledonia" },
22586
+ { alpha2: "NE", alpha3: "NER", name: "Niger" },
22587
+ { alpha2: "NF", alpha3: "NFK", name: "Norfolk Island" },
22588
+ { alpha2: "NG", alpha3: "NGA", name: "Nigeria" },
22589
+ { alpha2: "NI", alpha3: "NIC", name: "Nicaragua" },
22590
+ { alpha2: "NL", alpha3: "NLD", name: "Netherlands" },
22591
+ { alpha2: "NO", alpha3: "NOR", name: "Norway" },
22592
+ { alpha2: "NP", alpha3: "NPL", name: "Nepal" },
22593
+ { alpha2: "NR", alpha3: "NRU", name: "Nauru" },
22594
+ { alpha2: "NU", alpha3: "NIU", name: "Niue" },
22595
+ { alpha2: "NZ", alpha3: "NZL", name: "New Zealand" },
22596
+ { alpha2: "OM", alpha3: "OMN", name: "Oman" },
22597
+ { alpha2: "PA", alpha3: "PAN", name: "Panama" },
22598
+ { alpha2: "PE", alpha3: "PER", name: "Peru" },
22599
+ { alpha2: "PF", alpha3: "PYF", name: "French Polynesia" },
22600
+ { alpha2: "PG", alpha3: "PNG", name: "Papua New Guinea" },
22601
+ { alpha2: "PH", alpha3: "PHL", name: "Philippines" },
22602
+ { alpha2: "PK", alpha3: "PAK", name: "Pakistan" },
22603
+ { alpha2: "PL", alpha3: "POL", name: "Poland" },
22604
+ { alpha2: "PM", alpha3: "SPM", name: "Saint Pierre and Miquelon" },
22605
+ { alpha2: "PN", alpha3: "PCN", name: "Pitcairn" },
22606
+ { alpha2: "PR", alpha3: "PRI", name: "Puerto Rico" },
22607
+ { alpha2: "PS", alpha3: "PSE", name: "Palestine, State of" },
22608
+ { alpha2: "PT", alpha3: "PRT", name: "Portugal" },
22609
+ { alpha2: "PW", alpha3: "PLW", name: "Palau" },
22610
+ { alpha2: "PY", alpha3: "PRY", name: "Paraguay" },
22611
+ { alpha2: "QA", alpha3: "QAT", name: "Qatar" },
22612
+ { alpha2: "RE", alpha3: "REU", name: "R\xE9union" },
22613
+ { alpha2: "RO", alpha3: "ROU", name: "Romania" },
22614
+ { alpha2: "RS", alpha3: "SRB", name: "Serbia" },
22615
+ { alpha2: "RU", alpha3: "RUS", name: "Russian Federation" },
22616
+ { alpha2: "RW", alpha3: "RWA", name: "Rwanda" },
22617
+ { alpha2: "SA", alpha3: "SAU", name: "Saudi Arabia" },
22618
+ { alpha2: "SB", alpha3: "SLB", name: "Solomon Islands" },
22619
+ { alpha2: "SC", alpha3: "SYC", name: "Seychelles" },
22620
+ { alpha2: "SD", alpha3: "SDN", name: "Sudan" },
22621
+ { alpha2: "SE", alpha3: "SWE", name: "Sweden" },
22622
+ { alpha2: "SG", alpha3: "SGP", name: "Singapore" },
22623
+ { alpha2: "SH", alpha3: "SHN", name: "Saint Helena, Ascension and Tristan da Cunha" },
22624
+ { alpha2: "SI", alpha3: "SVN", name: "Slovenia" },
22625
+ { alpha2: "SJ", alpha3: "SJM", name: "Svalbard and Jan Mayen" },
22626
+ { alpha2: "SK", alpha3: "SVK", name: "Slovakia" },
22627
+ { alpha2: "SL", alpha3: "SLE", name: "Sierra Leone" },
22628
+ { alpha2: "SM", alpha3: "SMR", name: "San Marino" },
22629
+ { alpha2: "SN", alpha3: "SEN", name: "Senegal" },
22630
+ { alpha2: "SO", alpha3: "SOM", name: "Somalia" },
22631
+ { alpha2: "SR", alpha3: "SUR", name: "Suriname" },
22632
+ { alpha2: "SS", alpha3: "SSD", name: "South Sudan" },
22633
+ { alpha2: "ST", alpha3: "STP", name: "Sao Tome and Principe" },
22634
+ { alpha2: "SV", alpha3: "SLV", name: "El Salvador" },
22635
+ { alpha2: "SX", alpha3: "SXM", name: "Sint Maarten (Dutch part)" },
22636
+ { alpha2: "SY", alpha3: "SYR", name: "Syrian Arab Republic" },
22637
+ { alpha2: "SZ", alpha3: "SWZ", name: "Eswatini" },
22638
+ { alpha2: "TC", alpha3: "TCA", name: "Turks and Caicos Islands" },
22639
+ { alpha2: "TD", alpha3: "TCD", name: "Chad" },
22640
+ { alpha2: "TF", alpha3: "ATF", name: "French Southern Territories" },
22641
+ { alpha2: "TG", alpha3: "TGO", name: "Togo" },
22642
+ { alpha2: "TH", alpha3: "THA", name: "Thailand" },
22643
+ { alpha2: "TJ", alpha3: "TJK", name: "Tajikistan" },
22644
+ { alpha2: "TK", alpha3: "TKL", name: "Tokelau" },
22645
+ { alpha2: "TL", alpha3: "TLS", name: "Timor-Leste" },
22646
+ { alpha2: "TM", alpha3: "TKM", name: "Turkmenistan" },
22647
+ { alpha2: "TN", alpha3: "TUN", name: "Tunisia" },
22648
+ { alpha2: "TO", alpha3: "TON", name: "Tonga" },
22649
+ { alpha2: "TR", alpha3: "TUR", name: "T\xFCrkiye" },
22650
+ { alpha2: "TT", alpha3: "TTO", name: "Trinidad and Tobago" },
22651
+ { alpha2: "TV", alpha3: "TUV", name: "Tuvalu" },
22652
+ { alpha2: "TW", alpha3: "TWN", name: "Taiwan, Province of China" },
22653
+ { alpha2: "TZ", alpha3: "TZA", name: "Tanzania, United Republic of" },
22654
+ { alpha2: "UA", alpha3: "UKR", name: "Ukraine" },
22655
+ { alpha2: "UG", alpha3: "UGA", name: "Uganda" },
22656
+ { alpha2: "UM", alpha3: "UMI", name: "United States Minor Outlying Islands" },
22657
+ { alpha2: "US", alpha3: "USA", name: "United States of America" },
22658
+ { alpha2: "UY", alpha3: "URY", name: "Uruguay" },
22659
+ { alpha2: "UZ", alpha3: "UZB", name: "Uzbekistan" },
22660
+ { alpha2: "VA", alpha3: "VAT", name: "Holy See" },
22661
+ { alpha2: "VC", alpha3: "VCT", name: "Saint Vincent and the Grenadines" },
22662
+ { alpha2: "VE", alpha3: "VEN", name: "Venezuela (Bolivarian Republic of)" },
22663
+ { alpha2: "VG", alpha3: "VGB", name: "Virgin Islands (British)" },
22664
+ { alpha2: "VI", alpha3: "VIR", name: "Virgin Islands (U.S.)" },
22665
+ { alpha2: "VN", alpha3: "VNM", name: "Viet Nam" },
22666
+ { alpha2: "VU", alpha3: "VUT", name: "Vanuatu" },
22667
+ { alpha2: "WF", alpha3: "WLF", name: "Wallis and Futuna" },
22668
+ { alpha2: "WS", alpha3: "WSM", name: "Samoa" },
22669
+ { alpha2: "YE", alpha3: "YEM", name: "Yemen" },
22670
+ { alpha2: "YT", alpha3: "MYT", name: "Mayotte" },
22671
+ { alpha2: "ZA", alpha3: "ZAF", name: "South Africa" },
22672
+ { alpha2: "ZM", alpha3: "ZMB", name: "Zambia" },
22673
+ { alpha2: "ZW", alpha3: "ZWE", name: "Zimbabwe" }
22674
+ ];
22675
+ var COMMON_ALIASES = {
22676
+ usa: "US",
22677
+ america: "US",
22678
+ unitedstates: "US",
22679
+ uk: "GB",
22680
+ unitedkingdom: "GB",
22681
+ britain: "GB",
22682
+ greatbritain: "GB",
22683
+ southkorea: "KR",
22684
+ northkorea: "KP",
22685
+ russia: "RU",
22686
+ vietnam: "VN",
22687
+ laos: "LA",
22688
+ syria: "SY",
22689
+ iran: "IR",
22690
+ ivorycoast: "CI",
22691
+ czechrepublic: "CZ",
22692
+ swaziland: "SZ",
22693
+ burma: "MM",
22694
+ capeverde: "CV",
22695
+ easttimor: "TL",
22696
+ taiwan: "TW",
22697
+ macedonia: "MK",
22698
+ vatican: "VA",
22699
+ vaticancity: "VA",
22700
+ holland: "NL",
22701
+ moldova: "MD",
22702
+ bolivia: "BO",
22703
+ tanzania: "TZ",
22704
+ venezuela: "VE",
22705
+ palestine: "PS",
22706
+ brunei: "BN"
22707
+ };
22708
+ function normalize(s) {
22709
+ return s.toLowerCase().trim().replace(/[^a-z0-9]/g, "");
22710
+ }
22711
+ function isValidCountry(input) {
22712
+ const trimmed = input.trim();
22713
+ if (/^[A-Za-z]{2}$/.test(trimmed)) {
22714
+ return COUNTRIES.some((c) => c.alpha2 === trimmed.toUpperCase());
22715
+ }
22716
+ if (/^[A-Za-z]{3}$/.test(trimmed)) {
22717
+ return COUNTRIES.some((c) => c.alpha3 === trimmed.toUpperCase());
22718
+ }
22719
+ const normalized = normalize(trimmed);
22720
+ if (COMMON_ALIASES[normalized]) return true;
22721
+ return COUNTRIES.some((c) => normalize(c.name) === normalized);
22166
22722
  }
22167
22723
 
22168
22724
  // src/template-aliases.ts
22725
+ function validateDob(value) {
22726
+ if (typeof value !== "string") return "dob must be a string in YYYY-MM-DD format";
22727
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return `dob must match the format YYYY-MM-DD, got "${value}"`;
22728
+ const [year, month, day] = value.split("-").map(Number);
22729
+ const date3 = new Date(Date.UTC(year, month - 1, day));
22730
+ const isRealCalendarDate = date3.getUTCFullYear() === year && date3.getUTCMonth() === month - 1 && date3.getUTCDate() === day;
22731
+ if (!isRealCalendarDate) return `dob is not a valid calendar date: "${value}"`;
22732
+ return void 0;
22733
+ }
22734
+ function validateCountryOfOrigin(value) {
22735
+ if (typeof value !== "string" || !value.trim()) return "countryOfOrigin must be a non-empty string";
22736
+ if (!isValidCountry(value)) return `countryOfOrigin must be an ISO 3166 country code or name, got "${value}"`;
22737
+ return void 0;
22738
+ }
22169
22739
  var TEMPLATE_ALIASES = [
22170
22740
  {
22171
22741
  match: "birthcert",
22172
- testnet: "did:zid:d6b783559acf6ba0f7ef6e1365bdaf0774d622d8d22728ca6323677f49ee94f8",
22173
- mainnet: "did:zid:032cb99be3577beccfc6252783c49c83673af38f8456d73462043654d7764e83"
22742
+ testnet: "did:zid:3c0fb79adff08e14e06dcd6e3243205010dd65f533434a3d96c55575d1d3d959",
22743
+ mainnet: "did:zid:19091d19049abb8869b4b8e2f4a887bd1d1d86e5f5ebd0c8297000255f67765b",
22744
+ deriveAttributes: { id: "agentUsername" },
22745
+ validateAttributes: { dob: validateDob, countryOfOrigin: validateCountryOfOrigin }
22174
22746
  }
22175
22747
  ];
22176
- function normalize(s) {
22748
+ function normalize2(s) {
22177
22749
  return s.toLowerCase().replace(/[^a-z0-9]/g, "");
22178
22750
  }
22751
+ function findEntry(templateId, network) {
22752
+ if (/^did:zid:/.test(templateId)) {
22753
+ const key = network.includes("testnet") ? "testnet" : "mainnet";
22754
+ return TEMPLATE_ALIASES.find((e) => e[key] === templateId);
22755
+ }
22756
+ const normalized = normalize2(templateId);
22757
+ return TEMPLATE_ALIASES.find((e) => normalized.includes(e.match));
22758
+ }
22179
22759
  function resolveTemplateAlias(input, network) {
22180
22760
  if (/^did:zid:/.test(input)) return void 0;
22181
- const normalized = normalize(input);
22182
- const entry = TEMPLATE_ALIASES.find((e) => normalized.includes(e.match));
22761
+ const entry = findEntry(input, network);
22183
22762
  if (!entry) return void 0;
22184
22763
  return network.includes("testnet") ? entry.testnet : entry.mainnet;
22185
22764
  }
22765
+ function deriveTemplateAttributes(templateId, network, attributes) {
22766
+ const entry = findEntry(templateId, network);
22767
+ if (!entry?.deriveAttributes) return attributes;
22768
+ const result = { ...attributes };
22769
+ for (const [target, source] of Object.entries(entry.deriveAttributes)) {
22770
+ if ((result[target] === void 0 || result[target] === null || result[target] === "") && result[source] !== void 0) {
22771
+ result[target] = result[source];
22772
+ }
22773
+ }
22774
+ return result;
22775
+ }
22776
+ function derivedAttributeKeys(templateId, network) {
22777
+ const entry = findEntry(templateId, network);
22778
+ return entry?.deriveAttributes ? Object.keys(entry.deriveAttributes) : [];
22779
+ }
22780
+ function validateTemplateAttributes(templateId, network, attributes) {
22781
+ const entry = findEntry(templateId, network);
22782
+ if (!entry?.validateAttributes) return [];
22783
+ const errors = [];
22784
+ for (const [key, validate] of Object.entries(entry.validateAttributes)) {
22785
+ const value = attributes[key];
22786
+ if (value === void 0 || value === null || value === "") continue;
22787
+ const error2 = validate(value);
22788
+ if (error2) errors.push(error2);
22789
+ }
22790
+ return errors;
22791
+ }
22186
22792
 
22187
22793
  // src/mcp-tools.ts
22188
22794
  async function loadValidCachedCredentials(cache) {
@@ -22194,13 +22800,15 @@ function createTools(deps) {
22194
22800
  return {
22195
22801
  async wallet_status(input = {}) {
22196
22802
  const balances = deps.getBalances ? await deps.getBalances() : void 0;
22803
+ const tokenBalance = input.token && deps.queryTokenBalance ? await deps.queryTokenBalance(input.token) : void 0;
22197
22804
  const credentials = input.heldCredentials ?? (await loadValidCachedCredentials(deps.cache)).map((entry) => entry.vc);
22198
22805
  return {
22199
22806
  holderDid: deps.config.holderDid,
22200
22807
  zetrixAddress: deps.config.zetrixAddress,
22201
22808
  network: deps.config.network,
22202
22809
  credentials,
22203
- ...balances !== void 0 ? { balances } : {}
22810
+ ...balances !== void 0 ? { balances } : {},
22811
+ ...tokenBalance !== void 0 ? { tokenBalance } : {}
22204
22812
  };
22205
22813
  },
22206
22814
  async prove_identity(input) {
@@ -22226,16 +22834,67 @@ function createTools(deps) {
22226
22834
  pay_and_fetch(input) {
22227
22835
  return payAndFetch(deps.payer, input);
22228
22836
  },
22229
- subscribe_and_issue(input) {
22837
+ async subscribe_and_issue(input) {
22230
22838
  const resolved = resolveTemplateAlias(input.templateId, deps.config.network);
22231
- return subscribeAndIssue(deps.subscribeDeps, resolved ? { ...input, templateId: resolved } : input);
22839
+ const templateId = resolved ?? input.templateId;
22840
+ const attributes = deriveTemplateAttributes(templateId, deps.config.network, input.attributes ?? {});
22841
+ const errors = validateTemplateAttributes(templateId, deps.config.network, attributes);
22842
+ if (errors.length > 0) {
22843
+ return { issued: false, reason: errors.join("; ") };
22844
+ }
22845
+ const result = await subscribeAndIssue(deps.subscribeDeps, { ...input, templateId, attributes });
22846
+ if (!result.schema) return result;
22847
+ const hidden = /* @__PURE__ */ new Set(["agentDid", ...derivedAttributeKeys(templateId, deps.config.network)]);
22848
+ return {
22849
+ ...result,
22850
+ schema: {
22851
+ required: result.schema.required.filter((k) => !hidden.has(k)),
22852
+ optional: result.schema.optional.filter((k) => !hidden.has(k))
22853
+ }
22854
+ };
22855
+ },
22856
+ /**
22857
+ * Free schema lookup. Previously the only way to ask "what does this template need?" was
22858
+ * subscribe_and_issue({ dryRun: true }) — a tool whose name reads as "this charges money", so
22859
+ * an agent reasoning about required fields had no obvious reason to reach for it. Reported
22860
+ * live: an agent discovered a newly-required attribute only by failing an issuance first.
22861
+ */
22862
+ async get_template_schema(input) {
22863
+ const resolved = resolveTemplateAlias(input.templateId, deps.config.network);
22864
+ const templateId = resolved ?? input.templateId;
22865
+ if (!/^did:zid:/.test(templateId)) {
22866
+ return {
22867
+ templateId,
22868
+ error: `templateId must be a did:zid:... credential-definition id or a known template name, got "${input.templateId}"`
22869
+ };
22870
+ }
22871
+ const fields = deps.subscribeDeps.resolveTemplateFields ? await deps.subscribeDeps.resolveTemplateFields(templateId) : null;
22872
+ if (!fields) {
22873
+ return {
22874
+ templateId,
22875
+ error: `template schema could not be read from chain for ${templateId} (unknown/inactive template, or node unavailable)`
22876
+ };
22877
+ }
22878
+ const hidden = /* @__PURE__ */ new Set(["agentDid", ...derivedAttributeKeys(templateId, deps.config.network)]);
22879
+ return {
22880
+ templateId,
22881
+ schema: {
22882
+ required: fields.required.filter((k) => !hidden.has(k)),
22883
+ optional: fields.allKeys.filter((k) => !fields.required.includes(k) && !hidden.has(k))
22884
+ }
22885
+ };
22886
+ },
22887
+ query_contract(input) {
22888
+ return deps.queryContract(input);
22232
22889
  },
22233
22890
  create_holder_account(input) {
22234
22891
  return createHolderAccount(
22235
22892
  {
22236
22893
  create: deps.createAccount,
22237
22894
  getExistingAccount: () => Promise.resolve(deps.config.zetrixAddress ? { zetrixAddress: deps.config.zetrixAddress, holderDid: deps.config.holderDid } : null),
22238
- saveAccount: deps.saveAccount
22895
+ saveAccount: deps.saveAccount,
22896
+ checkActivationStatus: deps.checkActivationStatus,
22897
+ sleep: deps.sleep
22239
22898
  },
22240
22899
  input
22241
22900
  );
@@ -22246,8 +22905,16 @@ function createTools(deps) {
22246
22905
  // src/orchestrator/resolve-holder.ts
22247
22906
  async function resolveHolder(deps, input) {
22248
22907
  if (!input.zetrixAddress) {
22249
- const { zetrixAddress, publicKeyHex } = await deps.createAccount(input.hsmPassword);
22250
- return { zetrixAddress, holderDid: deriveHolderDid(publicKeyHex), created: true, didMismatch: false };
22908
+ const { zetrixAddress, publicKeyHex, activated } = await deps.createAccount(input.hsmPassword);
22909
+ let finalActivated = activated;
22910
+ if (!finalActivated) {
22911
+ try {
22912
+ finalActivated = await waitForActivation(deps.checkActivationStatus, zetrixAddress, deps.sleep);
22913
+ } catch {
22914
+ finalActivated = false;
22915
+ }
22916
+ }
22917
+ return { zetrixAddress, holderDid: deriveHolderDid(publicKeyHex), created: true, didMismatch: false, activated: finalActivated };
22251
22918
  }
22252
22919
  const { publicKey } = await deps.signMessage(input.zetrixAddress, input.zetrixAddress, input.hsmPassword);
22253
22920
  const derivedDid = deriveHolderDid(publicKey);
@@ -22289,7 +22956,8 @@ function buildToolList() {
22289
22956
  inputSchema: {
22290
22957
  type: "object",
22291
22958
  properties: {
22292
- heldCredentials: { type: "array", items: { type: "object" }, description: "VCs the client holds. Omit to report whatever the wallet has cached locally from prior subscribe_and_issue calls instead." }
22959
+ heldCredentials: { type: "array", items: { type: "object" }, description: "VCs the client holds. Omit to report whatever the wallet has cached locally from prior subscribe_and_issue calls instead." },
22960
+ token: { type: "string", description: `Optional token symbol (e.g. "ZTX", "JMYR") to check its balance for the active network, alongside the usual status fields. Returns { balance, decimals } where balance is in the asset's raw base units \u2014 divide by 10^decimals for the human amount (e.g. balance "473999900" with decimals 6 is 473.9999 JMYR). A failed lookup reports { error: "query_failed" } rather than a zero balance.` }
22293
22961
  }
22294
22962
  }
22295
22963
  },
@@ -22328,9 +22996,36 @@ function buildToolList() {
22328
22996
  required: ["url"]
22329
22997
  }
22330
22998
  },
22999
+ {
23000
+ name: "get_template_schema",
23001
+ description: `Read a VC template's declared attribute schema from chain \u2014 FREE, no payment, no signing, no MBI issuance. Call this BEFORE subscribe_and_issue to find out which attributes a template requires, rather than discovering a missing one by attempting an issuance and being rejected. Accepts a did:zid:... credential-definition id or a known template name (e.g. "AI Birthcert"). Returns { templateId, schema: { required, optional } }; attributes the wallet fills in itself (agentDid, alias-derived keys) are omitted since you never supply them. A template that cannot be read reports { error } rather than an empty schema, so "needs nothing" is never confused with "could not look it up".`,
23002
+ inputSchema: {
23003
+ type: "object",
23004
+ properties: {
23005
+ templateId: {
23006
+ type: "string",
23007
+ description: 'The MBI credential-definition id (did:zid:...) or a known template name, e.g. "AI Birthcert".'
23008
+ }
23009
+ },
23010
+ required: ["templateId"]
23011
+ }
23012
+ },
23013
+ {
23014
+ name: "query_contract",
23015
+ description: 'Read-only query against a Zetrix contract or account \u2014 call an arbitrary contract method (e.g. "balanceOf", "contractInfo") and return its raw result. No signing, no state change.',
23016
+ inputSchema: {
23017
+ type: "object",
23018
+ properties: {
23019
+ contractAddress: { type: "string", description: "Zetrix contract address to query." },
23020
+ method: { type: "string", description: 'Contract method name, e.g. "balanceOf", "contractInfo".' },
23021
+ params: { type: "object", description: 'Method parameters, e.g. { "address": "ZTX..." } for balanceOf.' }
23022
+ },
23023
+ required: ["contractAddress", "method"]
23024
+ }
23025
+ },
22331
23026
  {
22332
23027
  name: "subscribe_and_issue",
22333
- description: "Obtain a VC from MBI: build the signed payload, pay x402, and return the issued credential. If a still-valid credential for this templateId is already cached locally, it is returned directly with no payment (fromCache: true) \u2014 pass forceReissue:true to pay and issue fresh regardless. Payment is asset-agnostic \u2014 MBI's 402 challenge may quote the native ZETRIX token or a ZTP20 token (e.g. JMYR); pass dryRun:true first to see the quoted asset/amount for free before committing to pay.",
23028
+ description: "Obtain a VC from MBI: build the signed payload, pay x402, and return the issued credential. If a still-valid credential for this templateId is already cached locally, it is returned directly with no payment (fromCache: true) \u2014 pass forceReissue:true to pay and issue fresh regardless. Payment is asset-agnostic \u2014 MBI's 402 challenge may quote the native ZETRIX token or a ZTP20 token (e.g. JMYR); pass dryRun:true first to see the quoted asset/amount for free before committing to pay. What a call actually cost is reported precisely: paidAsset/amountPaid are set ONLY when this call paid, a cache hit reports the earlier charge under originalPayment instead (never as amountPaid, so summing spend cannot double-count), and any failure after the payment has settled on chain reports paymentAttempted: { asset, amount, paymentId }. Two such failures exist and mean different things: MBI 4006 is a definitive facilitator rejection, while 4012 (HTTP 502) means the outcome is INDETERMINATE \u2014 the payment may well have landed. On 4012 the wallet automatically polls MBI's recovery endpoint and reports recovery: { status, txHash?, vcId?, polls }, where status is ISSUED (the credential exists after all \u2014 fetch it by vcId, since recovery returns no VC body), FAILED, or REQUIRED/SETTLED (still unresolved) / UNKNOWN (recovery itself unreachable). NEVER retry a payment after either failure: the funds may already be gone, and a retry charges the full amount again \u2014 look the paymentId up instead. Every response except a cache hit also includes { schema: { required, optional } } \u2014 the template's full declared attribute schema read from chain \u2014 so you see the complete field list, not just what went wrong; a cache hit skips the chain lookup and omits it.",
22334
23029
  inputSchema: {
22335
23030
  type: "object",
22336
23031
  properties: {
@@ -22345,7 +23040,7 @@ function buildToolList() {
22345
23040
  expirationDate: { type: "string" },
22346
23041
  dryRun: {
22347
23042
  type: "boolean",
22348
- description: "Stop after MBI's free phase-1 quote and return { quote: { asset, maxAmountRequired, payTo, requiredAttributes? } } without paying or issuing \u2014 use this to check the payment requirement AND the template's required attributes (read from chain) before spending funds."
23043
+ description: "Stop after MBI's free phase-1 quote and return { quote: { asset, maxAmountRequired, payTo }, schema: { required, optional } } without paying or issuing \u2014 use this to check the payment requirement AND the template's full attribute schema (read from chain) before spending funds. Still validates required attributes locally first \u2014 a missing one blocks before any MBI call."
22349
23044
  },
22350
23045
  forceReissue: {
22351
23046
  type: "boolean",
@@ -22388,10 +23083,13 @@ async function main() {
22388
23083
  const config2 = loadConfig(env);
22389
23084
  const hsmPassword = config2.hsmPassword;
22390
23085
  const be = new WalletBeClient(config2.walletBeUrl);
22391
- const { zetrixAddress, holderDid, created, didMismatch } = await resolveHolder(
23086
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
23087
+ const { zetrixAddress, holderDid, created, didMismatch, activated } = await resolveHolder(
22392
23088
  {
22393
23089
  createAccount: (password) => be.createAccount(password),
22394
- signMessage: (message, address, password) => be.signMessage(message, address, password)
23090
+ signMessage: (message, address, password) => be.signMessage(message, address, password),
23091
+ checkActivationStatus: (address) => be.checkActivationStatus(address),
23092
+ sleep
22395
23093
  },
22396
23094
  { zetrixAddress: config2.zetrixAddress, holderDid: config2.holderDid, hsmPassword }
22397
23095
  );
@@ -22410,6 +23108,12 @@ async function main() {
22410
23108
  if (didMismatch) {
22411
23109
  process.stderr.write(
22412
23110
  `agentic-wallet-mcp: configured HOLDER_DID=${config2.holderDid} does not match the account's actual public key \u2014 using the derived HOLDER_DID=${holderDid} instead. Update your MCP config.
23111
+ `
23112
+ );
23113
+ }
23114
+ if (created && !activated) {
23115
+ process.stderr.write(
23116
+ `agentic-wallet-mcp: the newly created HSM account (ZETRIX_ADDRESS=${zetrixAddress}) has not completed on-chain activation yet \u2014 balance/on-chain calls for this address may fail until it does.
22413
23117
  `
22414
23118
  );
22415
23119
  }
@@ -22420,6 +23124,20 @@ async function main() {
22420
23124
  const sdk = new import_zetrix_sdk_nodejs.default({ host: config2.nodeHost, port: config2.nodePort });
22421
23125
  const contractQuery = (a) => sdk.contract.call(a);
22422
23126
  const resolveSymbol = (asset) => resolveAssetSymbol(asset, contractQuery);
23127
+ const fetchNativeBalance = async (address) => {
23128
+ const res = await sdk.account.getInfo(address);
23129
+ if (res.errorCode !== 0) throw new Error(`getInfo failed with errorCode ${res.errorCode}`);
23130
+ const balance = res.result?.balance;
23131
+ if (typeof balance !== "string") throw new Error("getInfo returned no balance");
23132
+ return balance;
23133
+ };
23134
+ const tokenBalanceDeps = {
23135
+ address: zetrixAddress,
23136
+ fetchNativeBalance,
23137
+ resolveTokenAddress: (symbol) => resolveTokenAddress(symbol, config2.network) ?? null,
23138
+ query: contractQuery
23139
+ };
23140
+ const queryTokenBalance2 = (token) => queryTokenBalance(tokenBalanceDeps, token);
22423
23141
  const nodeBaseUrl = `https://${config2.nodeHost}${config2.nodePort ? `:${config2.nodePort}` : ""}`;
22424
23142
  const nodeMetaQuery = (url) => fetch(url, { headers: { Accept: "application/json" } }).then((r) => r.json());
22425
23143
  const resolveTemplateFields = (templateId) => fetchTemplateFields(templateId, config2.templateRegistryAddress, nodeBaseUrl, nodeMetaQuery);
@@ -22427,7 +23145,10 @@ async function main() {
22427
23145
  const vcCache = createFsVcCache((0, import_node_path3.join)((0, import_node_os.homedir)(), ".agentic-wallet-mcp", "vc-cache", cacheScope));
22428
23146
  const pay = (accept) => {
22429
23147
  assertWithinPaymentCap(accept, config2.maxPaymentAmount);
22430
- return import_x402_zetrix_client.PaymentEngine.pay(asPayRequest(accept), walletCfg, node, {}, walletBeSignerFn);
23148
+ return payWithReadinessCheck(
23149
+ String(accept.asset ?? ""),
23150
+ () => import_x402_zetrix_client2.PaymentEngine.pay(asPayRequest(accept), walletCfg, node, {}, walletBeSignerFn)
23151
+ );
22431
23152
  };
22432
23153
  const payer = async (req) => {
22433
23154
  const init = { method: req.method ?? "GET", headers: req.headers, body: req.body };
@@ -22438,7 +23159,15 @@ async function main() {
22438
23159
  const parsed = await res.json();
22439
23160
  const accept = parsed.accepts?.[0];
22440
23161
  if (!accept) throw new Error("pay_and_fetch: 402 had no accepts[]");
22441
- const xPayment = await pay(accept);
23162
+ let xPayment;
23163
+ try {
23164
+ xPayment = await pay(accept);
23165
+ } catch (err) {
23166
+ if (err instanceof PaymentReadinessError) {
23167
+ return { status: 402, body: "", paymentMade: false, amountPaid: "", amountPaidHuman: "", asset: "", insufficientFunds: err.shortfall };
23168
+ }
23169
+ throw err;
23170
+ }
22442
23171
  const retry = await fetch(req.url, { ...init, headers: { ...req.headers ?? {}, "x-payment": xPayment } });
22443
23172
  const asset = await resolveSymbol(String(accept.asset ?? ""));
22444
23173
  return {
@@ -22468,8 +23197,12 @@ async function main() {
22468
23197
  makeWallet,
22469
23198
  payer,
22470
23199
  subscribeDeps: { mbi, sign: subscribeSign, pay, resolveSymbol, holderDid, resolveTemplateFields, cache: vcCache },
23200
+ queryContract: (input) => queryContract(input, contractQuery),
23201
+ queryTokenBalance: queryTokenBalance2,
22471
23202
  createAccount: (password, label, purpose) => be.createAccount(password, label, purpose),
22472
23203
  saveAccount: (account) => accountStore.set({ ...account, createdAt: (/* @__PURE__ */ new Date()).toISOString() }),
23204
+ checkActivationStatus: (address) => be.checkActivationStatus(address),
23205
+ sleep,
22473
23206
  cache: vcCache
22474
23207
  };
22475
23208
  const tools = createTools(deps);