agentic-wallet-mcp 0.8.0 → 0.8.1

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/CHANGELOG.md CHANGED
@@ -8,6 +8,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  > Entries for 0.5.0 and earlier were reconstructed from commit history when this file was
9
9
  > introduced in 0.6.0, so they summarise each release rather than being exhaustive.
10
10
 
11
+ ## [0.8.1] — 19 August 2026
12
+
13
+ ### Fixed
14
+
15
+ - **A payment blocked by the spending cap, or rejected for insufficient balance, now names the
16
+ real token and shows a human-readable amount** — e.g. `10000 (0.01 JMYR)` instead of a bare raw
17
+ integer that got mislabeled as ZTX once relayed. Affects `pay_and_fetch`, `subscribe_and_issue`,
18
+ and `request_ai_birthcert_verification`, since all three share the same payment step.
19
+ - **A wallet holding a ZTP20 token (e.g. JMYR) but zero ZTX no longer fails with a raw, opaque
20
+ error when trying to pay.** The wallet now checks its own ZTX gas balance before attempting the
21
+ payment and reports a clear "send some ZTX first" message — working around a bug in
22
+ `x402-zetrix-client` where its own gas check runs after an on-chain call that can itself fail
23
+ unhelpfully on a zero-gas account.
24
+
25
+ ### Changed
26
+
27
+ - The known-but-never-confirmed mainnet SSIVC host is now a named, exported constant
28
+ (`UNVERIFIED_MAINNET_SSIVC_BASE_URL`) instead of only living in a comment — still not wired in by
29
+ default (`SSIVC_BASE_URL` remains the way to enable it on mainnet), but easier to flip on once
30
+ confirmed reachable.
31
+
11
32
  ## [0.8.0] — 17 August 2026
12
33
 
13
34
  ### Added
@@ -12548,7 +12548,7 @@ var import_node_path6 = require("node:path");
12548
12548
  // package.json
12549
12549
  var package_default = {
12550
12550
  name: "agentic-wallet-mcp",
12551
- version: "0.8.0",
12551
+ version: "0.8.1",
12552
12552
  description: "Agent-facing MCP wallet for Zetrix \u2014 orchestrates x401 identity proof, x402 payment, and MBI VC issuance",
12553
12553
  keywords: [
12554
12554
  "mcp",
@@ -21496,9 +21496,18 @@ var import_node_path = require("node:path");
21496
21496
 
21497
21497
  // src/payment-guard.ts
21498
21498
  var PaymentCapError = class extends Error {
21499
- constructor(message) {
21499
+ /**
21500
+ * Set only when the failure is "requested amount exceeds the configured cap" — carries the raw
21501
+ * asset identifier and raw base-unit amounts so a caller with symbol/decimals resolution (e.g.
21502
+ * index.ts's `pay`) can rebuild a human-readable message without re-deriving these numbers.
21503
+ * Undefined for configuration-shaped failures (missing cap entry, malformed input), which have
21504
+ * no amount to render more legibly.
21505
+ */
21506
+ detail;
21507
+ constructor(message, detail) {
21500
21508
  super(message);
21501
21509
  this.name = "PaymentCapError";
21510
+ this.detail = detail;
21502
21511
  }
21503
21512
  };
21504
21513
  function isNonNegativeIntegerString(v) {
@@ -21538,7 +21547,10 @@ function assertWithinPaymentCap(accept, caps) {
21538
21547
  const required2 = BigInt(requiredRaw);
21539
21548
  const cap = BigInt(capRaw);
21540
21549
  if (required2 > cap) {
21541
- throw new PaymentCapError(`payment blocked: requested ${required2} ${asset || "(unknown asset)"} exceeds configured MAX_PAYMENT_AMOUNT ${cap}`);
21550
+ throw new PaymentCapError(
21551
+ `payment blocked: requested ${required2} ${asset || "(unknown asset)"} exceeds configured MAX_PAYMENT_AMOUNT ${cap}`,
21552
+ { asset, requiredRaw: required2.toString(), capRaw: cap.toString() }
21553
+ );
21542
21554
  }
21543
21555
  }
21544
21556
 
@@ -21645,6 +21657,23 @@ async function resolveAssetSymbol(asset, query) {
21645
21657
  const info = await fetchTokenInfo(asset, query);
21646
21658
  return info?.symbol ?? asset;
21647
21659
  }
21660
+ var ZTX_DECIMALS = 6;
21661
+ async function resolveAssetInfo(asset, query) {
21662
+ if (asset === "ZTX") return { symbol: "ZTX", decimals: ZTX_DECIMALS };
21663
+ if (asset === "") return { symbol: "", decimals: 0 };
21664
+ const info = await fetchTokenInfo(asset, query);
21665
+ return info ?? { symbol: asset, decimals: 0 };
21666
+ }
21667
+ function formatHumanAmount(raw, decimals) {
21668
+ if (!/^\d+$/.test(raw) || decimals <= 0) return raw;
21669
+ const base = 10n ** BigInt(decimals);
21670
+ const value = BigInt(raw);
21671
+ const wholePart = value / base;
21672
+ const fracPart = value % base;
21673
+ if (fracPart === 0n) return wholePart.toString();
21674
+ const fracStr = fracPart.toString().padStart(decimals, "0").replace(/0+$/, "");
21675
+ return `${wholePart}.${fracStr}`;
21676
+ }
21648
21677
 
21649
21678
  // src/clients/contract-query-client.ts
21650
21679
  async function queryContract(input, query) {
@@ -21673,7 +21702,7 @@ async function queryContract(input, query) {
21673
21702
  }
21674
21703
 
21675
21704
  // src/clients/token-balance-client.ts
21676
- var ZTX_DECIMALS = 6;
21705
+ var ZTX_DECIMALS2 = 6;
21677
21706
  function parseNativeBalance(res) {
21678
21707
  if (res?.errorCode !== 0) throw new Error(`getInfo failed with errorCode ${res?.errorCode}`);
21679
21708
  if (res.result === void 0 || res.result === null) throw new Error("getInfo returned no result");
@@ -21707,7 +21736,7 @@ async function queryTokenBalance(deps, token) {
21707
21736
  const symbol = token.toUpperCase();
21708
21737
  if (symbol === "ZTX") {
21709
21738
  try {
21710
- return { token: symbol, balance: await deps.fetchNativeBalance(deps.address), decimals: ZTX_DECIMALS };
21739
+ return { token: symbol, balance: await deps.fetchNativeBalance(deps.address), decimals: ZTX_DECIMALS2 };
21711
21740
  } catch {
21712
21741
  return { token: symbol, error: "query_failed" };
21713
21742
  }
@@ -23705,13 +23734,53 @@ async function main() {
23705
23734
  const vcCache = createFsVcCache((0, import_node_path6.join)(config2.stateDir, "vc-cache", cacheScope));
23706
23735
  const mbi = new MbiClient(config2.mbiBaseUrl);
23707
23736
  const messageSigner = (message) => be.signMessage(message, zetrixAddress, hsmPassword);
23708
- const pay = (accept) => {
23709
- assertWithinPaymentCap(accept, config2.maxPaymentAmount);
23710
- return payWithReadinessCheck(
23711
- String(accept.asset ?? ""),
23712
- () => import_x402_zetrix_client2.PaymentEngine.pay(asPayRequest(accept), walletCfg, node, {}, walletBeSignerFn),
23713
- activated
23714
- );
23737
+ const formatAssetAmount = async (asset, raw) => {
23738
+ const { symbol, decimals } = await resolveAssetInfo(asset, contractQuery);
23739
+ const label = symbol || "(unknown asset)";
23740
+ const human = formatHumanAmount(raw, decimals);
23741
+ return human === raw ? `${raw} ${label}` : `${raw} (${human} ${label})`;
23742
+ };
23743
+ const pay = async (accept) => {
23744
+ const rawAsset = String(accept.asset ?? "");
23745
+ try {
23746
+ assertWithinPaymentCap(accept, config2.maxPaymentAmount);
23747
+ } catch (err) {
23748
+ if (err instanceof PaymentCapError && err.detail) {
23749
+ const { asset, requiredRaw, capRaw } = err.detail;
23750
+ throw new PaymentCapError(
23751
+ `payment blocked: requested ${await formatAssetAmount(asset, requiredRaw)} exceeds configured MAX_PAYMENT_AMOUNT ${await formatAssetAmount(asset, capRaw)}`,
23752
+ err.detail
23753
+ );
23754
+ }
23755
+ throw err;
23756
+ }
23757
+ if (rawAsset !== "" && rawAsset !== "ZTX") {
23758
+ const gasBalance = await fetchNativeBalance(zetrixAddress).catch(() => null);
23759
+ if (gasBalance === "0") {
23760
+ const { symbol } = await resolveAssetInfo(rawAsset, contractQuery);
23761
+ throw new PaymentReadinessError(
23762
+ `this wallet has 0 ZTX to pay network gas \u2014 the ${symbol || rawAsset} balance is separate from gas, and every transaction costs a small amount of ZTX regardless of which token is being paid. Send some ZTX to ${zetrixAddress} first, then retry.`,
23763
+ { asset: "ZTX", required: "unknown", available: "0", reason: "gas" }
23764
+ );
23765
+ }
23766
+ }
23767
+ try {
23768
+ return await payWithReadinessCheck(
23769
+ rawAsset,
23770
+ () => import_x402_zetrix_client2.PaymentEngine.pay(asPayRequest(accept), walletCfg, node, {}, walletBeSignerFn),
23771
+ activated
23772
+ );
23773
+ } catch (err) {
23774
+ if (err instanceof PaymentReadinessError && err.shortfall.reason !== "not_activated") {
23775
+ const { asset, required: required2, available, reason } = err.shortfall;
23776
+ const label = reason === "gas" ? "ZTX for gas" : (await resolveAssetInfo(asset, contractQuery)).symbol || asset;
23777
+ throw new PaymentReadinessError(
23778
+ `insufficient ${label} \u2014 required ${await formatAssetAmount(asset, required2)}, available ${await formatAssetAmount(asset, available)}`,
23779
+ err.shortfall
23780
+ );
23781
+ }
23782
+ throw err;
23783
+ }
23715
23784
  };
23716
23785
  const verifyAiBirthcert = config2.ssivcBaseUrl ? (() => {
23717
23786
  const ssivcSessionStore = createFsSsivcSessionStore((0, import_node_path6.join)(config2.stateDir, "ssivc-session.json"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-wallet-mcp",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Agent-facing MCP wallet for Zetrix — orchestrates x401 identity proof, x402 payment, and MBI VC issuance",
5
5
  "keywords": [
6
6
  "mcp",