agentic-wallet-mcp 0.4.0 → 0.5.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/README.md +35 -14
- package/dist/server-bundle.cjs +715 -57
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,8 +23,8 @@ VC issuance → identity proof → pay-per-use).
|
|
|
23
23
|
| `wallet_status` | Report holder DID/address/network + held VCs (client-supplied, or the local cache) | `{ heldCredentials? }` | `{ holderDid, zetrixAddress, network, credentials }` |
|
|
24
24
|
| `prove_identity` | Answer an x401 `PROOF-REQUEST` → return the `PROOF-RESPONSE` header to replay | `{ proofRequest, vc?, revealAttribute?, issuerKeys? }` | `{ proofResponseHeader, verified, presentationId }` |
|
|
25
25
|
| `pay_and_fetch` | Fetch a URL, auto-pay with x402 (self-pay via Wallet BE) on `402` | `{ url, method?, headers?, body? }` | `{ status, body, paymentMade, amountPaid, amountPaidHuman, asset }` |
|
|
26
|
-
| `subscribe_and_issue` | Reuse a cached VC if still valid, else pay x402 → MBI issues → return the VC | `{ templateId, attributes, expirationDate?, dryRun?, forceReissue? }` | `{ issued, vcId, vc, txHash, fromCache? }` |
|
|
27
|
-
| `create_holder_account` | Onboarding:
|
|
26
|
+
| `subscribe_and_issue` | Reuse a cached VC if still valid, else pay x402 → MBI issues → return the VC | `{ templateId, attributes, expirationDate?, dryRun?, forceReissue? }` | `{ issued, vcId, vc, txHash, fromCache?, schema? }` |
|
|
27
|
+
| `create_holder_account` | Onboarding: mint an HSM account (the MCP already auto-creates one at startup if `ZETRIX_ADDRESS` is omitted — see Environment below). Always checks for an existing account first — if one is active for this session, returns `{ alreadyExists: true, existing }` without creating anything; pass `confirmNew: true` (after asking the user) to mint a new one anyway | `{ password, label?, purpose?, confirmNew? }` | `{ created, alreadyExists, existing?, zetrixAddress?, holderDid?, publicKeyHex?, message }` |
|
|
28
28
|
|
|
29
29
|
> **VCs are cached locally**, keyed by `templateId`, under `~/.agentic-wallet-mcp/vc-cache/`
|
|
30
30
|
> (scoped per network + holder — different identities or networks never share a cache).
|
|
@@ -38,10 +38,25 @@ VC issuance → identity proof → pay-per-use).
|
|
|
38
38
|
> back to the `expirationDate` requested at issuance; a VC with neither is cached indefinitely.
|
|
39
39
|
> All Ed25519 signing still goes through Wallet BE HSM; no plaintext private keys.
|
|
40
40
|
|
|
41
|
+
> `subscribe_and_issue` also returns `schema: { required, optional }` — the template's full
|
|
42
|
+
> declared attribute list, read from chain — on every outcome (issued, dry-run quote, or a
|
|
43
|
+
> missing-attribute error), so you always see the complete field list rather than only what went
|
|
44
|
+
> wrong. Attributes the wallet auto-fills for you (e.g. `agentDid`, or a template-declared derived
|
|
45
|
+
> key like the `AI Birthcert` template's `id` ← `agentUsername`) are omitted from `schema` since
|
|
46
|
+
> you never need to supply them. Some templates also declare format validators for optional
|
|
47
|
+
> attributes (e.g. `AI Birthcert`'s `dob` must be `YYYY-MM-DD`, `countryOfOrigin` must be a valid
|
|
48
|
+
> ISO 3166 code or name) — an invalid value is rejected locally before any payment or MBI call.
|
|
49
|
+
|
|
41
50
|
> `create_holder_account` mints a **brand-new** keypair — Wallet BE's `/account/create` has no
|
|
42
|
-
> way to provision a pre-chosen address. It
|
|
43
|
-
>
|
|
44
|
-
>
|
|
51
|
+
> way to provision a pre-chosen address. It always checks first whether an account is already
|
|
52
|
+
> active for this session; if so, it returns `{ alreadyExists: true, existing }` and creates
|
|
53
|
+
> nothing — ask the user whether to keep the existing account or replace it, then call again
|
|
54
|
+
> with `confirmNew: true` only if they want a new one. A freshly minted account (address, DID,
|
|
55
|
+
> **and** password) is saved to this MCP's own local account store
|
|
56
|
+
> (`~/.agentic-wallet-mcp/account.json`, owner-only) and reused automatically on the next
|
|
57
|
+
> restart — no manual config edit needed. An explicit `ZETRIX_ADDRESS`/`HSM_PASSWORD` still set
|
|
58
|
+
> in your MCP config always overrides the saved account (see Environment below); the tool never
|
|
59
|
+
> writes the MCP host's own config file or restarts the server for you.
|
|
45
60
|
|
|
46
61
|
> `revealAttribute` on `prove_identity` is optional and usually should stay that way. Omitted,
|
|
47
62
|
> it's derived automatically from the challenge's DCQL `credential_requirements` — each claim path
|
|
@@ -100,9 +115,11 @@ startup, in one of two ways:
|
|
|
100
115
|
|
|
101
116
|
1. **First-time user — only `HSM_PASSWORD` set.** The MCP creates a brand-new HSM account on
|
|
102
117
|
Wallet BE (`POST /wallet/hsm/account/create`) and derives the DID from the returned public
|
|
103
|
-
key. It logs the new `ZETRIX_ADDRESS` (and `HOLDER_DID`) to stderr on startup
|
|
104
|
-
|
|
105
|
-
|
|
118
|
+
key. It logs the new `ZETRIX_ADDRESS` (and `HOLDER_DID`) to stderr on startup, and saves the
|
|
119
|
+
address, DID, and password to a local account store
|
|
120
|
+
(`~/.agentic-wallet-mcp/account.json`, owner-only) — it's reused automatically next run, no
|
|
121
|
+
config edit required. An explicit `ZETRIX_ADDRESS`/`HSM_PASSWORD` set later in your MCP
|
|
122
|
+
config still overrides the saved account.
|
|
106
123
|
2. **Existing user — `ZETRIX_ADDRESS` + `HSM_PASSWORD` set, `HOLDER_DID` optional.** The MCP
|
|
107
124
|
always self-signs the address via the existing `POST /wallet/hsm/sign-message` call and
|
|
108
125
|
derives the DID from the `publicKey` the response carries — no separate lookup endpoint
|
|
@@ -171,8 +188,9 @@ the `<...>` placeholders (don't commit a filled copy; `mcp.local.json` is gitign
|
|
|
171
188
|
```
|
|
172
189
|
|
|
173
190
|
> First run, no account yet? Omit `ZETRIX_ADDRESS` (and `HOLDER_DID`) entirely — the MCP creates
|
|
174
|
-
> one for you at startup
|
|
175
|
-
> "Onboarding" under
|
|
191
|
+
> one for you at startup, logs it to stderr, and saves it (address, DID, password) to
|
|
192
|
+
> `~/.agentic-wallet-mcp/account.json` for automatic reuse next run. See "Onboarding" under
|
|
193
|
+
> Environment above.
|
|
176
194
|
|
|
177
195
|
> Working on this repo locally instead of the published package? Point `command`/`args` at the
|
|
178
196
|
> local build directly: `"command": "node"`, `"args": ["/absolute/path/to/zetrix-agentic-wallet/dist/server-bundle.cjs"]`.
|
|
@@ -190,7 +208,7 @@ the `<...>` placeholders (don't commit a filled copy; `mcp.local.json` is gitign
|
|
|
190
208
|
- *"I got a 401 with this PROOF-REQUEST header — prove my identity and give me the PROOF-RESPONSE to replay."* → `prove_identity`
|
|
191
209
|
- *"Fetch `https://api.example/data` and pay automatically if it asks."* → `pay_and_fetch`
|
|
192
210
|
- *"Apply for the agent-identity credential with these attributes and pay for it."* → `subscribe_and_issue`
|
|
193
|
-
- *"My wallet_status call is failing — I don't have a holder account yet. Set one up."* → `create_holder_account` (asks you for a password, then
|
|
211
|
+
- *"My wallet_status call is failing — I don't have a holder account yet. Set one up."* → `create_holder_account` (asks you for a password; if an account already exists it reports that instead of creating — confirm with the user, then re-call with `confirmNew: true` to replace it)
|
|
194
212
|
|
|
195
213
|
For the full ordered script (onboarding → check → issue → prove → pay), see [`docs/USAGE_FLOW.md`](docs/USAGE_FLOW.md).
|
|
196
214
|
|
|
@@ -200,9 +218,12 @@ Full narrative version with example prompts: [`docs/USAGE_FLOW.md`](docs/USAGE_F
|
|
|
200
218
|
|
|
201
219
|
**Step 0 — onboarding (once).** Only if `ZETRIX_ADDRESS` isn't set yet: the MCP creates an HSM
|
|
202
220
|
account automatically at startup from `HSM_PASSWORD` alone (see "Onboarding" under Environment
|
|
203
|
-
above)
|
|
204
|
-
|
|
205
|
-
|
|
221
|
+
above) and saves it locally for automatic reuse. Alternatively, call `create_holder_account
|
|
222
|
+
{ password }` manually — it always checks for an existing account first and reports it instead
|
|
223
|
+
of creating (pass `confirmNew: true`, after asking the user, to replace it anyway). Either way,
|
|
224
|
+
the account is saved to `~/.agentic-wallet-mcp/account.json` and picked up automatically on the
|
|
225
|
+
next restart; no manual config edit needed unless your MCP config also sets `ZETRIX_ADDRESS`/
|
|
226
|
+
`HSM_PASSWORD` via env, in which case those still take precedence and should be updated too.
|
|
206
227
|
|
|
207
228
|
**Phase 1 — `wallet_status` — pre-check.** Pass any VCs the caller already holds via
|
|
208
229
|
`heldCredentials`; the response tells you whether the agent-identity credential you need is
|
package/dist/server-bundle.cjs
CHANGED
|
@@ -2233,8 +2233,8 @@ var require_resolve = __commonJS({
|
|
|
2233
2233
|
}
|
|
2234
2234
|
return count;
|
|
2235
2235
|
}
|
|
2236
|
-
function getFullPath(resolver, id = "",
|
|
2237
|
-
if (
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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 =
|
|
8573
|
+
includePath = normalize3(includePath);
|
|
8574
8574
|
if (isAbsolute(includePath))
|
|
8575
8575
|
return includePath;
|
|
8576
8576
|
if (!alreadyNormalized)
|
|
8577
|
-
originPath =
|
|
8578
|
-
return (originPath = originPath.replace(/(?:\/|^)[^/]+$/, "")).length ?
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
12311
|
+
throw new InsufficientBalanceError2(`Insufficient ZTX for gas: required ${feeLimit}, available ${ztxBal}`, feeLimit, ztxBal, "ZTX");
|
|
12312
12312
|
}
|
|
12313
12313
|
}
|
|
12314
12314
|
},
|
|
@@ -12542,12 +12542,12 @@ __export(index_exports, {
|
|
|
12542
12542
|
module.exports = __toCommonJS(index_exports);
|
|
12543
12543
|
var import_node_crypto2 = require("node:crypto");
|
|
12544
12544
|
var import_node_os = require("node:os");
|
|
12545
|
-
var
|
|
12545
|
+
var import_node_path3 = require("node:path");
|
|
12546
12546
|
|
|
12547
12547
|
// package.json
|
|
12548
12548
|
var package_default = {
|
|
12549
12549
|
name: "agentic-wallet-mcp",
|
|
12550
|
-
version: "0.
|
|
12550
|
+
version: "0.5.0",
|
|
12551
12551
|
description: "Agent-facing MCP wallet for Zetrix \u2014 orchestrates x401 identity proof, x402 payment, and MBI VC issuance",
|
|
12552
12552
|
keywords: ["mcp", "model-context-protocol", "zetrix", "wallet", "x401", "x402", "blockchain"],
|
|
12553
12553
|
license: "MIT",
|
|
@@ -21159,7 +21159,7 @@ var StdioServerTransport = class {
|
|
|
21159
21159
|
};
|
|
21160
21160
|
|
|
21161
21161
|
// src/index.ts
|
|
21162
|
-
var
|
|
21162
|
+
var import_x402_zetrix_client2 = __toESM(require_dist2(), 1);
|
|
21163
21163
|
|
|
21164
21164
|
// node_modules/x401-zetrix-client/dist/config.js
|
|
21165
21165
|
var ZETRIX_OID4VP_URLS = {
|
|
@@ -21547,6 +21547,14 @@ function deriveMbiBaseUrl(network) {
|
|
|
21547
21547
|
function deriveTemplateRegistryAddress(network) {
|
|
21548
21548
|
return network.includes("testnet") ? "ZTX3JszqPgRUx743SAp7q7zURfjvkWuH2FMEz" : "ZTX3GqJM1U6ifMPonwD4fGvrgoTKJua7b2cKX";
|
|
21549
21549
|
}
|
|
21550
|
+
var TOKEN_REGISTRY = {
|
|
21551
|
+
JMYR: { testnet: "ZTX3WeinXtt28YMyr4vUZ14ddTgEMGeuc1e6b", mainnet: "ZTX3NCkXBqbyJWjZZxciQez945Lu6tGAcjNJr" }
|
|
21552
|
+
};
|
|
21553
|
+
function resolveTokenAddress(symbol, network) {
|
|
21554
|
+
const entry = TOKEN_REGISTRY[symbol.toUpperCase()];
|
|
21555
|
+
if (!entry) return void 0;
|
|
21556
|
+
return network.includes("testnet") ? entry.testnet : entry.mainnet;
|
|
21557
|
+
}
|
|
21550
21558
|
function loadConfig(env) {
|
|
21551
21559
|
const req = (key, hint) => {
|
|
21552
21560
|
const v = env[key];
|
|
@@ -21609,6 +21617,32 @@ async function resolveAssetSymbol(asset, query) {
|
|
|
21609
21617
|
return info?.symbol ?? asset;
|
|
21610
21618
|
}
|
|
21611
21619
|
|
|
21620
|
+
// src/clients/contract-query-client.ts
|
|
21621
|
+
async function queryContract(input, query) {
|
|
21622
|
+
let response;
|
|
21623
|
+
try {
|
|
21624
|
+
response = await query({
|
|
21625
|
+
contractAddress: input.contractAddress,
|
|
21626
|
+
input: JSON.stringify({ method: input.method, params: input.params ?? {} }),
|
|
21627
|
+
optType: 2
|
|
21628
|
+
});
|
|
21629
|
+
} catch (e) {
|
|
21630
|
+
return { ok: false, error: `query_contract: RPC call failed \u2014 ${e.message}` };
|
|
21631
|
+
}
|
|
21632
|
+
if (response.errorCode !== 0) {
|
|
21633
|
+
return { ok: false, error: `query_contract: contract call failed with errorCode ${response.errorCode}` };
|
|
21634
|
+
}
|
|
21635
|
+
const raw = response.result?.query_rets?.[0]?.result?.value;
|
|
21636
|
+
if (raw === void 0) {
|
|
21637
|
+
return { ok: false, error: "query_contract: no result value returned" };
|
|
21638
|
+
}
|
|
21639
|
+
try {
|
|
21640
|
+
return { ok: true, result: JSON.parse(raw) };
|
|
21641
|
+
} catch {
|
|
21642
|
+
return { ok: true, result: raw };
|
|
21643
|
+
}
|
|
21644
|
+
}
|
|
21645
|
+
|
|
21612
21646
|
// src/clients/template-info-client.ts
|
|
21613
21647
|
async function fetchTemplateFields(templateId, registryAddress, nodeBaseUrl, query) {
|
|
21614
21648
|
const key = `template__${templateId}`;
|
|
@@ -21667,6 +21701,10 @@ var WalletBeClient = class {
|
|
|
21667
21701
|
createAccount(password, label, purpose) {
|
|
21668
21702
|
return this.post("/wallet/hsm/account/create", { password, label, purpose });
|
|
21669
21703
|
}
|
|
21704
|
+
/** GET /wallet/hsm/account/activate/status — ground-truth on-chain activation check (no local state). */
|
|
21705
|
+
checkActivationStatus(address) {
|
|
21706
|
+
return this.get("/wallet/hsm/account/activate/status", { address });
|
|
21707
|
+
}
|
|
21670
21708
|
async post(path, body) {
|
|
21671
21709
|
const url = `${this.baseUrl}${path}`;
|
|
21672
21710
|
let res;
|
|
@@ -21694,6 +21732,25 @@ var WalletBeClient = class {
|
|
|
21694
21732
|
}
|
|
21695
21733
|
return env.data;
|
|
21696
21734
|
}
|
|
21735
|
+
async get(path, query) {
|
|
21736
|
+
const url = `${this.baseUrl}${path}?${new URLSearchParams(query).toString()}`;
|
|
21737
|
+
let res;
|
|
21738
|
+
try {
|
|
21739
|
+
res = await fetch(url, { headers: { Accept: "application/json" } });
|
|
21740
|
+
} catch (e) {
|
|
21741
|
+
throw new WalletBeError(`Wallet BE ${path} request failed`, void 0, e);
|
|
21742
|
+
}
|
|
21743
|
+
if (!res.ok) {
|
|
21744
|
+
const text = await res.text().catch(() => "");
|
|
21745
|
+
throw new WalletBeError(`Wallet BE ${path} HTTP ${res.status}: ${text}`);
|
|
21746
|
+
}
|
|
21747
|
+
const env = await res.json();
|
|
21748
|
+
if (env.errorCode !== 0) {
|
|
21749
|
+
const detail = env.data?.errorList?.length ? ` \u2014 ${env.data.errorList.join("; ")}` : "";
|
|
21750
|
+
throw new WalletBeError(`Wallet BE ${path} errorCode ${env.errorCode}: ${env.message ?? "error"}${detail}`, env.errorCode);
|
|
21751
|
+
}
|
|
21752
|
+
return env.data;
|
|
21753
|
+
}
|
|
21697
21754
|
};
|
|
21698
21755
|
|
|
21699
21756
|
// src/signer.ts
|
|
@@ -21809,14 +21866,22 @@ var MbiError = class extends Error {
|
|
|
21809
21866
|
this.httpStatus = httpStatus;
|
|
21810
21867
|
}
|
|
21811
21868
|
};
|
|
21812
|
-
var MbiClient = class {
|
|
21869
|
+
var MbiClient = class _MbiClient {
|
|
21813
21870
|
baseUrl;
|
|
21814
21871
|
constructor(baseUrl) {
|
|
21815
21872
|
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
21816
21873
|
}
|
|
21817
|
-
/**
|
|
21874
|
+
/**
|
|
21875
|
+
* Phase 1 — POST /v1/vc/pay/apply without X-PAYMENT; expects the 402 challenge.
|
|
21876
|
+
* A free template short-circuits this: MBI issues the VC synchronously and returns
|
|
21877
|
+
* 200 instead, with no phase-2 settle to follow — surfaced via the `issued` field.
|
|
21878
|
+
*/
|
|
21818
21879
|
async applyChallenge(body) {
|
|
21819
21880
|
const res = await this.fetch("POST", "/v1/vc/pay/apply", body);
|
|
21881
|
+
if (res.status === 200) {
|
|
21882
|
+
const issued = await this.unwrap(res);
|
|
21883
|
+
return { x402Version: 1, accepts: [], issued };
|
|
21884
|
+
}
|
|
21820
21885
|
if (res.status !== 402) {
|
|
21821
21886
|
throw await this.error(res, "apply (phase 1) expected 402");
|
|
21822
21887
|
}
|
|
@@ -21864,12 +21929,14 @@ var MbiClient = class {
|
|
|
21864
21929
|
const body = await res.json();
|
|
21865
21930
|
return body.data;
|
|
21866
21931
|
}
|
|
21932
|
+
static ERROR_BODY_MAX_LEN = 500;
|
|
21867
21933
|
async error(res, context) {
|
|
21868
21934
|
const text = await res.text().catch(() => "");
|
|
21869
21935
|
let msg = text;
|
|
21870
21936
|
try {
|
|
21871
21937
|
const j = JSON.parse(text);
|
|
21872
|
-
|
|
21938
|
+
const truncated = text.length > _MbiClient.ERROR_BODY_MAX_LEN ? `${text.slice(0, _MbiClient.ERROR_BODY_MAX_LEN)}\u2026 (truncated, ${text.length} bytes total)` : text;
|
|
21939
|
+
msg = `${j.message ?? j.error ?? text} | full body: ${truncated}`;
|
|
21873
21940
|
} catch {
|
|
21874
21941
|
}
|
|
21875
21942
|
return new MbiError(`MBI ${context} \u2014 HTTP ${res.status}: ${msg}`, res.status);
|
|
@@ -22025,6 +22092,36 @@ function extractValidUntil(vc, fallback) {
|
|
|
22025
22092
|
return fallback;
|
|
22026
22093
|
}
|
|
22027
22094
|
|
|
22095
|
+
// src/payment-readiness.ts
|
|
22096
|
+
var import_x402_zetrix_client = __toESM(require_dist2(), 1);
|
|
22097
|
+
var PaymentReadinessError = class extends Error {
|
|
22098
|
+
shortfall;
|
|
22099
|
+
constructor(message, shortfall) {
|
|
22100
|
+
super(message);
|
|
22101
|
+
this.name = "PaymentReadinessError";
|
|
22102
|
+
this.shortfall = shortfall;
|
|
22103
|
+
}
|
|
22104
|
+
};
|
|
22105
|
+
function toPaymentReadinessError(err, requestedAsset) {
|
|
22106
|
+
if (!(err instanceof import_x402_zetrix_client.InsufficientBalanceError)) return null;
|
|
22107
|
+
const reason = err.asset === "ZTX" && requestedAsset !== "ZTX" ? "gas" : "resource_payment";
|
|
22108
|
+
return new PaymentReadinessError(err.message, {
|
|
22109
|
+
asset: err.asset,
|
|
22110
|
+
required: err.required,
|
|
22111
|
+
available: err.available,
|
|
22112
|
+
reason
|
|
22113
|
+
});
|
|
22114
|
+
}
|
|
22115
|
+
async function payWithReadinessCheck(requestedAsset, rawPay) {
|
|
22116
|
+
try {
|
|
22117
|
+
return await rawPay();
|
|
22118
|
+
} catch (err) {
|
|
22119
|
+
const readinessError = toPaymentReadinessError(err, requestedAsset);
|
|
22120
|
+
if (readinessError) throw readinessError;
|
|
22121
|
+
throw err;
|
|
22122
|
+
}
|
|
22123
|
+
}
|
|
22124
|
+
|
|
22028
22125
|
// src/orchestrator/subscribe.ts
|
|
22029
22126
|
async function subscribeAndIssue(deps, opts) {
|
|
22030
22127
|
if (!/^did:zid:/.test(opts.templateId)) {
|
|
@@ -22048,13 +22145,15 @@ async function subscribeAndIssue(deps, opts) {
|
|
|
22048
22145
|
const { agentDid, ...rest } = opts.attributes ?? {};
|
|
22049
22146
|
const shouldAutoFillAgentDid = !agentDid && fields !== null && fields.allKeys.includes("agentDid");
|
|
22050
22147
|
const attributes = shouldAutoFillAgentDid ? { agentDid: deps.holderDid, ...rest } : opts.attributes ?? {};
|
|
22051
|
-
|
|
22148
|
+
const schema = fields ? { required: fields.required, optional: fields.allKeys.filter((k) => !fields.required.includes(k)) } : void 0;
|
|
22149
|
+
if (fields) {
|
|
22052
22150
|
const attrs = attributes;
|
|
22053
22151
|
const missing = fields.required.filter((k) => attrs[k] === void 0 || attrs[k] === null || attrs[k] === "");
|
|
22054
22152
|
if (missing.length > 0) {
|
|
22055
22153
|
return {
|
|
22056
22154
|
issued: false,
|
|
22057
|
-
reason: `template requires attribute(s) not supplied: ${missing.join(", ")} \u2014 no payment made
|
|
22155
|
+
reason: `template requires attribute(s) not supplied: ${missing.join(", ")} \u2014 no payment made`,
|
|
22156
|
+
...schema ? { schema } : {}
|
|
22058
22157
|
};
|
|
22059
22158
|
}
|
|
22060
22159
|
}
|
|
@@ -22063,9 +22162,42 @@ async function subscribeAndIssue(deps, opts) {
|
|
|
22063
22162
|
const { signBlob: signData, publicKey } = await deps.sign(blob);
|
|
22064
22163
|
const body = { data, signData, publicKey };
|
|
22065
22164
|
if (opts.expirationDate) body.expirationDate = opts.expirationDate;
|
|
22066
|
-
|
|
22165
|
+
let challenge;
|
|
22166
|
+
try {
|
|
22167
|
+
challenge = await deps.mbi.applyChallenge(body);
|
|
22168
|
+
} catch (err) {
|
|
22169
|
+
if (err instanceof MbiError) {
|
|
22170
|
+
return { issued: false, reason: err.message, httpStatus: err.httpStatus, ...schema ? { schema } : {} };
|
|
22171
|
+
}
|
|
22172
|
+
throw err;
|
|
22173
|
+
}
|
|
22174
|
+
if (challenge.issued) {
|
|
22175
|
+
const issued2 = challenge.issued;
|
|
22176
|
+
if (deps.cache) {
|
|
22177
|
+
await deps.cache.set(opts.templateId, {
|
|
22178
|
+
templateId: opts.templateId,
|
|
22179
|
+
vc: issued2.verifiableCredential,
|
|
22180
|
+
vcId: issued2.vcId,
|
|
22181
|
+
txHash: issued2.txHash,
|
|
22182
|
+
paidAsset: "none",
|
|
22183
|
+
amountPaid: "0",
|
|
22184
|
+
issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22185
|
+
validUntil: extractValidUntil(issued2.verifiableCredential, opts.expirationDate)
|
|
22186
|
+
});
|
|
22187
|
+
}
|
|
22188
|
+
return {
|
|
22189
|
+
issued: true,
|
|
22190
|
+
vcId: issued2.vcId,
|
|
22191
|
+
vc: issued2.verifiableCredential,
|
|
22192
|
+
txHash: issued2.txHash,
|
|
22193
|
+
paidAsset: "none",
|
|
22194
|
+
amountPaid: "0",
|
|
22195
|
+
...schema ? { schema } : {},
|
|
22196
|
+
...opts.dryRun ? { reason: "this template requires no payment \u2014 MBI issues synchronously at phase 1, so dryRun could not prevent this issuance" } : {}
|
|
22197
|
+
};
|
|
22198
|
+
}
|
|
22067
22199
|
const accept = challenge.accepts[0];
|
|
22068
|
-
if (!accept) return { issued: false, reason: "MBI 402 returned no payment options" };
|
|
22200
|
+
if (!accept) return { issued: false, reason: "MBI 402 returned no payment options", ...schema ? { schema } : {} };
|
|
22069
22201
|
if (opts.dryRun) {
|
|
22070
22202
|
const quotedRaw = String(accept.asset ?? "");
|
|
22071
22203
|
const quotedAsset = deps.resolveSymbol ? await deps.resolveSymbol(quotedRaw) : quotedRaw;
|
|
@@ -22075,13 +22207,29 @@ async function subscribeAndIssue(deps, opts) {
|
|
|
22075
22207
|
quote: {
|
|
22076
22208
|
asset: quotedAsset,
|
|
22077
22209
|
maxAmountRequired: accept.maxAmountRequired,
|
|
22078
|
-
payTo: accept.payTo
|
|
22079
|
-
|
|
22080
|
-
}
|
|
22210
|
+
payTo: accept.payTo
|
|
22211
|
+
},
|
|
22212
|
+
...schema ? { schema } : {}
|
|
22081
22213
|
};
|
|
22082
22214
|
}
|
|
22083
|
-
|
|
22084
|
-
|
|
22215
|
+
let xPayment;
|
|
22216
|
+
try {
|
|
22217
|
+
xPayment = await deps.pay(accept);
|
|
22218
|
+
} catch (err) {
|
|
22219
|
+
if (err instanceof PaymentReadinessError) {
|
|
22220
|
+
return { issued: false, reason: `insufficient funds: ${err.message}`, insufficientFunds: err.shortfall };
|
|
22221
|
+
}
|
|
22222
|
+
throw err;
|
|
22223
|
+
}
|
|
22224
|
+
let issued;
|
|
22225
|
+
try {
|
|
22226
|
+
issued = await deps.mbi.applySettle({ ...body, paymentId: challenge.paymentId }, xPayment);
|
|
22227
|
+
} catch (err) {
|
|
22228
|
+
if (err instanceof MbiError) {
|
|
22229
|
+
return { issued: false, reason: err.message, httpStatus: err.httpStatus, ...schema ? { schema } : {} };
|
|
22230
|
+
}
|
|
22231
|
+
throw err;
|
|
22232
|
+
}
|
|
22085
22233
|
const rawAsset = String(accept.asset ?? "");
|
|
22086
22234
|
const paidAsset = deps.resolveSymbol ? await deps.resolveSymbol(rawAsset) : rawAsset;
|
|
22087
22235
|
const amountPaid = String(accept.maxAmountRequired ?? "");
|
|
@@ -22103,10 +22251,23 @@ async function subscribeAndIssue(deps, opts) {
|
|
|
22103
22251
|
vc: issued.verifiableCredential,
|
|
22104
22252
|
txHash: issued.txHash,
|
|
22105
22253
|
paidAsset,
|
|
22106
|
-
amountPaid
|
|
22254
|
+
amountPaid,
|
|
22255
|
+
...schema ? { schema } : {}
|
|
22107
22256
|
};
|
|
22108
22257
|
}
|
|
22109
22258
|
|
|
22259
|
+
// src/orchestrator/wait-for-activation.ts
|
|
22260
|
+
async function waitForActivation(check, address, sleep, opts = {}) {
|
|
22261
|
+
const attempts = opts.attempts ?? 3;
|
|
22262
|
+
const delayMs = opts.delayMs ?? 3e3;
|
|
22263
|
+
for (let i = 0; i < attempts; i++) {
|
|
22264
|
+
await sleep(delayMs);
|
|
22265
|
+
const { activated } = await check(address);
|
|
22266
|
+
if (activated) return true;
|
|
22267
|
+
}
|
|
22268
|
+
return false;
|
|
22269
|
+
}
|
|
22270
|
+
|
|
22110
22271
|
// src/orchestrator/onboard.ts
|
|
22111
22272
|
function deriveHolderDid(publicKeyHex) {
|
|
22112
22273
|
const hex = publicKeyHex.trim();
|
|
@@ -22114,31 +22275,393 @@ function deriveHolderDid(publicKeyHex) {
|
|
|
22114
22275
|
if (hex.length === 76 && hex.slice(0, 4).toLowerCase() === "b001") return `did:zid:${hex.slice(4, 68)}`;
|
|
22115
22276
|
throw new Error(`onboard: unrecognized public key hex format (length ${hex.length})`);
|
|
22116
22277
|
}
|
|
22117
|
-
async function createHolderAccount(
|
|
22118
|
-
const
|
|
22278
|
+
async function createHolderAccount(deps, input) {
|
|
22279
|
+
const existing = await deps.getExistingAccount();
|
|
22280
|
+
if (existing && !input.confirmNew) {
|
|
22281
|
+
return {
|
|
22282
|
+
created: false,
|
|
22283
|
+
alreadyExists: true,
|
|
22284
|
+
existing,
|
|
22285
|
+
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.`
|
|
22286
|
+
};
|
|
22287
|
+
}
|
|
22288
|
+
const { zetrixAddress, publicKeyHex, activated } = await deps.create(input.password, input.label, input.purpose);
|
|
22119
22289
|
const holderDid = deriveHolderDid(publicKeyHex);
|
|
22120
|
-
|
|
22121
|
-
|
|
22290
|
+
await deps.saveAccount({ zetrixAddress, holderDid, hsmPassword: input.password, label: input.label, purpose: input.purpose });
|
|
22291
|
+
const finalActivated = activated || await waitForActivation(deps.checkActivationStatus, zetrixAddress, deps.sleep);
|
|
22292
|
+
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.`);
|
|
22293
|
+
return { created: true, alreadyExists: Boolean(existing), zetrixAddress, holderDid, publicKeyHex, activated: finalActivated, message };
|
|
22294
|
+
}
|
|
22295
|
+
|
|
22296
|
+
// src/iso3166.ts
|
|
22297
|
+
var COUNTRIES = [
|
|
22298
|
+
{ alpha2: "AD", alpha3: "AND", name: "Andorra" },
|
|
22299
|
+
{ alpha2: "AE", alpha3: "ARE", name: "United Arab Emirates" },
|
|
22300
|
+
{ alpha2: "AF", alpha3: "AFG", name: "Afghanistan" },
|
|
22301
|
+
{ alpha2: "AG", alpha3: "ATG", name: "Antigua and Barbuda" },
|
|
22302
|
+
{ alpha2: "AI", alpha3: "AIA", name: "Anguilla" },
|
|
22303
|
+
{ alpha2: "AL", alpha3: "ALB", name: "Albania" },
|
|
22304
|
+
{ alpha2: "AM", alpha3: "ARM", name: "Armenia" },
|
|
22305
|
+
{ alpha2: "AO", alpha3: "AGO", name: "Angola" },
|
|
22306
|
+
{ alpha2: "AQ", alpha3: "ATA", name: "Antarctica" },
|
|
22307
|
+
{ alpha2: "AR", alpha3: "ARG", name: "Argentina" },
|
|
22308
|
+
{ alpha2: "AS", alpha3: "ASM", name: "American Samoa" },
|
|
22309
|
+
{ alpha2: "AT", alpha3: "AUT", name: "Austria" },
|
|
22310
|
+
{ alpha2: "AU", alpha3: "AUS", name: "Australia" },
|
|
22311
|
+
{ alpha2: "AW", alpha3: "ABW", name: "Aruba" },
|
|
22312
|
+
{ alpha2: "AX", alpha3: "ALA", name: "\xC5land Islands" },
|
|
22313
|
+
{ alpha2: "AZ", alpha3: "AZE", name: "Azerbaijan" },
|
|
22314
|
+
{ alpha2: "BA", alpha3: "BIH", name: "Bosnia and Herzegovina" },
|
|
22315
|
+
{ alpha2: "BB", alpha3: "BRB", name: "Barbados" },
|
|
22316
|
+
{ alpha2: "BD", alpha3: "BGD", name: "Bangladesh" },
|
|
22317
|
+
{ alpha2: "BE", alpha3: "BEL", name: "Belgium" },
|
|
22318
|
+
{ alpha2: "BF", alpha3: "BFA", name: "Burkina Faso" },
|
|
22319
|
+
{ alpha2: "BG", alpha3: "BGR", name: "Bulgaria" },
|
|
22320
|
+
{ alpha2: "BH", alpha3: "BHR", name: "Bahrain" },
|
|
22321
|
+
{ alpha2: "BI", alpha3: "BDI", name: "Burundi" },
|
|
22322
|
+
{ alpha2: "BJ", alpha3: "BEN", name: "Benin" },
|
|
22323
|
+
{ alpha2: "BL", alpha3: "BLM", name: "Saint Barth\xE9lemy" },
|
|
22324
|
+
{ alpha2: "BM", alpha3: "BMU", name: "Bermuda" },
|
|
22325
|
+
{ alpha2: "BN", alpha3: "BRN", name: "Brunei Darussalam" },
|
|
22326
|
+
{ alpha2: "BO", alpha3: "BOL", name: "Bolivia (Plurinational State of)" },
|
|
22327
|
+
{ alpha2: "BQ", alpha3: "BES", name: "Bonaire, Sint Eustatius and Saba" },
|
|
22328
|
+
{ alpha2: "BR", alpha3: "BRA", name: "Brazil" },
|
|
22329
|
+
{ alpha2: "BS", alpha3: "BHS", name: "Bahamas" },
|
|
22330
|
+
{ alpha2: "BT", alpha3: "BTN", name: "Bhutan" },
|
|
22331
|
+
{ alpha2: "BV", alpha3: "BVT", name: "Bouvet Island" },
|
|
22332
|
+
{ alpha2: "BW", alpha3: "BWA", name: "Botswana" },
|
|
22333
|
+
{ alpha2: "BY", alpha3: "BLR", name: "Belarus" },
|
|
22334
|
+
{ alpha2: "BZ", alpha3: "BLZ", name: "Belize" },
|
|
22335
|
+
{ alpha2: "CA", alpha3: "CAN", name: "Canada" },
|
|
22336
|
+
{ alpha2: "CC", alpha3: "CCK", name: "Cocos (Keeling) Islands" },
|
|
22337
|
+
{ alpha2: "CD", alpha3: "COD", name: "Congo, Democratic Republic of the" },
|
|
22338
|
+
{ alpha2: "CF", alpha3: "CAF", name: "Central African Republic" },
|
|
22339
|
+
{ alpha2: "CG", alpha3: "COG", name: "Congo" },
|
|
22340
|
+
{ alpha2: "CH", alpha3: "CHE", name: "Switzerland" },
|
|
22341
|
+
{ alpha2: "CI", alpha3: "CIV", name: "C\xF4te d'Ivoire" },
|
|
22342
|
+
{ alpha2: "CK", alpha3: "COK", name: "Cook Islands" },
|
|
22343
|
+
{ alpha2: "CL", alpha3: "CHL", name: "Chile" },
|
|
22344
|
+
{ alpha2: "CM", alpha3: "CMR", name: "Cameroon" },
|
|
22345
|
+
{ alpha2: "CN", alpha3: "CHN", name: "China" },
|
|
22346
|
+
{ alpha2: "CO", alpha3: "COL", name: "Colombia" },
|
|
22347
|
+
{ alpha2: "CR", alpha3: "CRI", name: "Costa Rica" },
|
|
22348
|
+
{ alpha2: "CU", alpha3: "CUB", name: "Cuba" },
|
|
22349
|
+
{ alpha2: "CV", alpha3: "CPV", name: "Cabo Verde" },
|
|
22350
|
+
{ alpha2: "CW", alpha3: "CUW", name: "Cura\xE7ao" },
|
|
22351
|
+
{ alpha2: "CX", alpha3: "CXR", name: "Christmas Island" },
|
|
22352
|
+
{ alpha2: "CY", alpha3: "CYP", name: "Cyprus" },
|
|
22353
|
+
{ alpha2: "CZ", alpha3: "CZE", name: "Czechia" },
|
|
22354
|
+
{ alpha2: "DE", alpha3: "DEU", name: "Germany" },
|
|
22355
|
+
{ alpha2: "DJ", alpha3: "DJI", name: "Djibouti" },
|
|
22356
|
+
{ alpha2: "DK", alpha3: "DNK", name: "Denmark" },
|
|
22357
|
+
{ alpha2: "DM", alpha3: "DMA", name: "Dominica" },
|
|
22358
|
+
{ alpha2: "DO", alpha3: "DOM", name: "Dominican Republic" },
|
|
22359
|
+
{ alpha2: "DZ", alpha3: "DZA", name: "Algeria" },
|
|
22360
|
+
{ alpha2: "EC", alpha3: "ECU", name: "Ecuador" },
|
|
22361
|
+
{ alpha2: "EE", alpha3: "EST", name: "Estonia" },
|
|
22362
|
+
{ alpha2: "EG", alpha3: "EGY", name: "Egypt" },
|
|
22363
|
+
{ alpha2: "EH", alpha3: "ESH", name: "Western Sahara" },
|
|
22364
|
+
{ alpha2: "ER", alpha3: "ERI", name: "Eritrea" },
|
|
22365
|
+
{ alpha2: "ES", alpha3: "ESP", name: "Spain" },
|
|
22366
|
+
{ alpha2: "ET", alpha3: "ETH", name: "Ethiopia" },
|
|
22367
|
+
{ alpha2: "FI", alpha3: "FIN", name: "Finland" },
|
|
22368
|
+
{ alpha2: "FJ", alpha3: "FJI", name: "Fiji" },
|
|
22369
|
+
{ alpha2: "FK", alpha3: "FLK", name: "Falkland Islands (Malvinas)" },
|
|
22370
|
+
{ alpha2: "FM", alpha3: "FSM", name: "Micronesia (Federated States of)" },
|
|
22371
|
+
{ alpha2: "FO", alpha3: "FRO", name: "Faroe Islands" },
|
|
22372
|
+
{ alpha2: "FR", alpha3: "FRA", name: "France" },
|
|
22373
|
+
{ alpha2: "GA", alpha3: "GAB", name: "Gabon" },
|
|
22374
|
+
{ alpha2: "GB", alpha3: "GBR", name: "United Kingdom of Great Britain and Northern Ireland" },
|
|
22375
|
+
{ alpha2: "GD", alpha3: "GRD", name: "Grenada" },
|
|
22376
|
+
{ alpha2: "GE", alpha3: "GEO", name: "Georgia" },
|
|
22377
|
+
{ alpha2: "GF", alpha3: "GUF", name: "French Guiana" },
|
|
22378
|
+
{ alpha2: "GG", alpha3: "GGY", name: "Guernsey" },
|
|
22379
|
+
{ alpha2: "GH", alpha3: "GHA", name: "Ghana" },
|
|
22380
|
+
{ alpha2: "GI", alpha3: "GIB", name: "Gibraltar" },
|
|
22381
|
+
{ alpha2: "GL", alpha3: "GRL", name: "Greenland" },
|
|
22382
|
+
{ alpha2: "GM", alpha3: "GMB", name: "Gambia" },
|
|
22383
|
+
{ alpha2: "GN", alpha3: "GIN", name: "Guinea" },
|
|
22384
|
+
{ alpha2: "GP", alpha3: "GLP", name: "Guadeloupe" },
|
|
22385
|
+
{ alpha2: "GQ", alpha3: "GNQ", name: "Equatorial Guinea" },
|
|
22386
|
+
{ alpha2: "GR", alpha3: "GRC", name: "Greece" },
|
|
22387
|
+
{ alpha2: "GS", alpha3: "SGS", name: "South Georgia and the South Sandwich Islands" },
|
|
22388
|
+
{ alpha2: "GT", alpha3: "GTM", name: "Guatemala" },
|
|
22389
|
+
{ alpha2: "GU", alpha3: "GUM", name: "Guam" },
|
|
22390
|
+
{ alpha2: "GW", alpha3: "GNB", name: "Guinea-Bissau" },
|
|
22391
|
+
{ alpha2: "GY", alpha3: "GUY", name: "Guyana" },
|
|
22392
|
+
{ alpha2: "HK", alpha3: "HKG", name: "Hong Kong" },
|
|
22393
|
+
{ alpha2: "HM", alpha3: "HMD", name: "Heard Island and McDonald Islands" },
|
|
22394
|
+
{ alpha2: "HN", alpha3: "HND", name: "Honduras" },
|
|
22395
|
+
{ alpha2: "HR", alpha3: "HRV", name: "Croatia" },
|
|
22396
|
+
{ alpha2: "HT", alpha3: "HTI", name: "Haiti" },
|
|
22397
|
+
{ alpha2: "HU", alpha3: "HUN", name: "Hungary" },
|
|
22398
|
+
{ alpha2: "ID", alpha3: "IDN", name: "Indonesia" },
|
|
22399
|
+
{ alpha2: "IE", alpha3: "IRL", name: "Ireland" },
|
|
22400
|
+
{ alpha2: "IL", alpha3: "ISR", name: "Israel" },
|
|
22401
|
+
{ alpha2: "IM", alpha3: "IMN", name: "Isle of Man" },
|
|
22402
|
+
{ alpha2: "IN", alpha3: "IND", name: "India" },
|
|
22403
|
+
{ alpha2: "IO", alpha3: "IOT", name: "British Indian Ocean Territory" },
|
|
22404
|
+
{ alpha2: "IQ", alpha3: "IRQ", name: "Iraq" },
|
|
22405
|
+
{ alpha2: "IR", alpha3: "IRN", name: "Iran (Islamic Republic of)" },
|
|
22406
|
+
{ alpha2: "IS", alpha3: "ISL", name: "Iceland" },
|
|
22407
|
+
{ alpha2: "IT", alpha3: "ITA", name: "Italy" },
|
|
22408
|
+
{ alpha2: "JE", alpha3: "JEY", name: "Jersey" },
|
|
22409
|
+
{ alpha2: "JM", alpha3: "JAM", name: "Jamaica" },
|
|
22410
|
+
{ alpha2: "JO", alpha3: "JOR", name: "Jordan" },
|
|
22411
|
+
{ alpha2: "JP", alpha3: "JPN", name: "Japan" },
|
|
22412
|
+
{ alpha2: "KE", alpha3: "KEN", name: "Kenya" },
|
|
22413
|
+
{ alpha2: "KG", alpha3: "KGZ", name: "Kyrgyzstan" },
|
|
22414
|
+
{ alpha2: "KH", alpha3: "KHM", name: "Cambodia" },
|
|
22415
|
+
{ alpha2: "KI", alpha3: "KIR", name: "Kiribati" },
|
|
22416
|
+
{ alpha2: "KM", alpha3: "COM", name: "Comoros" },
|
|
22417
|
+
{ alpha2: "KN", alpha3: "KNA", name: "Saint Kitts and Nevis" },
|
|
22418
|
+
{ alpha2: "KP", alpha3: "PRK", name: "Korea (Democratic People's Republic of)" },
|
|
22419
|
+
{ alpha2: "KR", alpha3: "KOR", name: "Korea, Republic of" },
|
|
22420
|
+
{ alpha2: "KW", alpha3: "KWT", name: "Kuwait" },
|
|
22421
|
+
{ alpha2: "KY", alpha3: "CYM", name: "Cayman Islands" },
|
|
22422
|
+
{ alpha2: "KZ", alpha3: "KAZ", name: "Kazakhstan" },
|
|
22423
|
+
{ alpha2: "LA", alpha3: "LAO", name: "Lao People's Democratic Republic" },
|
|
22424
|
+
{ alpha2: "LB", alpha3: "LBN", name: "Lebanon" },
|
|
22425
|
+
{ alpha2: "LC", alpha3: "LCA", name: "Saint Lucia" },
|
|
22426
|
+
{ alpha2: "LI", alpha3: "LIE", name: "Liechtenstein" },
|
|
22427
|
+
{ alpha2: "LK", alpha3: "LKA", name: "Sri Lanka" },
|
|
22428
|
+
{ alpha2: "LR", alpha3: "LBR", name: "Liberia" },
|
|
22429
|
+
{ alpha2: "LS", alpha3: "LSO", name: "Lesotho" },
|
|
22430
|
+
{ alpha2: "LT", alpha3: "LTU", name: "Lithuania" },
|
|
22431
|
+
{ alpha2: "LU", alpha3: "LUX", name: "Luxembourg" },
|
|
22432
|
+
{ alpha2: "LV", alpha3: "LVA", name: "Latvia" },
|
|
22433
|
+
{ alpha2: "LY", alpha3: "LBY", name: "Libya" },
|
|
22434
|
+
{ alpha2: "MA", alpha3: "MAR", name: "Morocco" },
|
|
22435
|
+
{ alpha2: "MC", alpha3: "MCO", name: "Monaco" },
|
|
22436
|
+
{ alpha2: "MD", alpha3: "MDA", name: "Moldova, Republic of" },
|
|
22437
|
+
{ alpha2: "ME", alpha3: "MNE", name: "Montenegro" },
|
|
22438
|
+
{ alpha2: "MF", alpha3: "MAF", name: "Saint Martin (French part)" },
|
|
22439
|
+
{ alpha2: "MG", alpha3: "MDG", name: "Madagascar" },
|
|
22440
|
+
{ alpha2: "MH", alpha3: "MHL", name: "Marshall Islands" },
|
|
22441
|
+
{ alpha2: "MK", alpha3: "MKD", name: "North Macedonia" },
|
|
22442
|
+
{ alpha2: "ML", alpha3: "MLI", name: "Mali" },
|
|
22443
|
+
{ alpha2: "MM", alpha3: "MMR", name: "Myanmar" },
|
|
22444
|
+
{ alpha2: "MN", alpha3: "MNG", name: "Mongolia" },
|
|
22445
|
+
{ alpha2: "MO", alpha3: "MAC", name: "Macao" },
|
|
22446
|
+
{ alpha2: "MP", alpha3: "MNP", name: "Northern Mariana Islands" },
|
|
22447
|
+
{ alpha2: "MQ", alpha3: "MTQ", name: "Martinique" },
|
|
22448
|
+
{ alpha2: "MR", alpha3: "MRT", name: "Mauritania" },
|
|
22449
|
+
{ alpha2: "MS", alpha3: "MSR", name: "Montserrat" },
|
|
22450
|
+
{ alpha2: "MT", alpha3: "MLT", name: "Malta" },
|
|
22451
|
+
{ alpha2: "MU", alpha3: "MUS", name: "Mauritius" },
|
|
22452
|
+
{ alpha2: "MV", alpha3: "MDV", name: "Maldives" },
|
|
22453
|
+
{ alpha2: "MW", alpha3: "MWI", name: "Malawi" },
|
|
22454
|
+
{ alpha2: "MX", alpha3: "MEX", name: "Mexico" },
|
|
22455
|
+
{ alpha2: "MY", alpha3: "MYS", name: "Malaysia" },
|
|
22456
|
+
{ alpha2: "MZ", alpha3: "MOZ", name: "Mozambique" },
|
|
22457
|
+
{ alpha2: "NA", alpha3: "NAM", name: "Namibia" },
|
|
22458
|
+
{ alpha2: "NC", alpha3: "NCL", name: "New Caledonia" },
|
|
22459
|
+
{ alpha2: "NE", alpha3: "NER", name: "Niger" },
|
|
22460
|
+
{ alpha2: "NF", alpha3: "NFK", name: "Norfolk Island" },
|
|
22461
|
+
{ alpha2: "NG", alpha3: "NGA", name: "Nigeria" },
|
|
22462
|
+
{ alpha2: "NI", alpha3: "NIC", name: "Nicaragua" },
|
|
22463
|
+
{ alpha2: "NL", alpha3: "NLD", name: "Netherlands" },
|
|
22464
|
+
{ alpha2: "NO", alpha3: "NOR", name: "Norway" },
|
|
22465
|
+
{ alpha2: "NP", alpha3: "NPL", name: "Nepal" },
|
|
22466
|
+
{ alpha2: "NR", alpha3: "NRU", name: "Nauru" },
|
|
22467
|
+
{ alpha2: "NU", alpha3: "NIU", name: "Niue" },
|
|
22468
|
+
{ alpha2: "NZ", alpha3: "NZL", name: "New Zealand" },
|
|
22469
|
+
{ alpha2: "OM", alpha3: "OMN", name: "Oman" },
|
|
22470
|
+
{ alpha2: "PA", alpha3: "PAN", name: "Panama" },
|
|
22471
|
+
{ alpha2: "PE", alpha3: "PER", name: "Peru" },
|
|
22472
|
+
{ alpha2: "PF", alpha3: "PYF", name: "French Polynesia" },
|
|
22473
|
+
{ alpha2: "PG", alpha3: "PNG", name: "Papua New Guinea" },
|
|
22474
|
+
{ alpha2: "PH", alpha3: "PHL", name: "Philippines" },
|
|
22475
|
+
{ alpha2: "PK", alpha3: "PAK", name: "Pakistan" },
|
|
22476
|
+
{ alpha2: "PL", alpha3: "POL", name: "Poland" },
|
|
22477
|
+
{ alpha2: "PM", alpha3: "SPM", name: "Saint Pierre and Miquelon" },
|
|
22478
|
+
{ alpha2: "PN", alpha3: "PCN", name: "Pitcairn" },
|
|
22479
|
+
{ alpha2: "PR", alpha3: "PRI", name: "Puerto Rico" },
|
|
22480
|
+
{ alpha2: "PS", alpha3: "PSE", name: "Palestine, State of" },
|
|
22481
|
+
{ alpha2: "PT", alpha3: "PRT", name: "Portugal" },
|
|
22482
|
+
{ alpha2: "PW", alpha3: "PLW", name: "Palau" },
|
|
22483
|
+
{ alpha2: "PY", alpha3: "PRY", name: "Paraguay" },
|
|
22484
|
+
{ alpha2: "QA", alpha3: "QAT", name: "Qatar" },
|
|
22485
|
+
{ alpha2: "RE", alpha3: "REU", name: "R\xE9union" },
|
|
22486
|
+
{ alpha2: "RO", alpha3: "ROU", name: "Romania" },
|
|
22487
|
+
{ alpha2: "RS", alpha3: "SRB", name: "Serbia" },
|
|
22488
|
+
{ alpha2: "RU", alpha3: "RUS", name: "Russian Federation" },
|
|
22489
|
+
{ alpha2: "RW", alpha3: "RWA", name: "Rwanda" },
|
|
22490
|
+
{ alpha2: "SA", alpha3: "SAU", name: "Saudi Arabia" },
|
|
22491
|
+
{ alpha2: "SB", alpha3: "SLB", name: "Solomon Islands" },
|
|
22492
|
+
{ alpha2: "SC", alpha3: "SYC", name: "Seychelles" },
|
|
22493
|
+
{ alpha2: "SD", alpha3: "SDN", name: "Sudan" },
|
|
22494
|
+
{ alpha2: "SE", alpha3: "SWE", name: "Sweden" },
|
|
22495
|
+
{ alpha2: "SG", alpha3: "SGP", name: "Singapore" },
|
|
22496
|
+
{ alpha2: "SH", alpha3: "SHN", name: "Saint Helena, Ascension and Tristan da Cunha" },
|
|
22497
|
+
{ alpha2: "SI", alpha3: "SVN", name: "Slovenia" },
|
|
22498
|
+
{ alpha2: "SJ", alpha3: "SJM", name: "Svalbard and Jan Mayen" },
|
|
22499
|
+
{ alpha2: "SK", alpha3: "SVK", name: "Slovakia" },
|
|
22500
|
+
{ alpha2: "SL", alpha3: "SLE", name: "Sierra Leone" },
|
|
22501
|
+
{ alpha2: "SM", alpha3: "SMR", name: "San Marino" },
|
|
22502
|
+
{ alpha2: "SN", alpha3: "SEN", name: "Senegal" },
|
|
22503
|
+
{ alpha2: "SO", alpha3: "SOM", name: "Somalia" },
|
|
22504
|
+
{ alpha2: "SR", alpha3: "SUR", name: "Suriname" },
|
|
22505
|
+
{ alpha2: "SS", alpha3: "SSD", name: "South Sudan" },
|
|
22506
|
+
{ alpha2: "ST", alpha3: "STP", name: "Sao Tome and Principe" },
|
|
22507
|
+
{ alpha2: "SV", alpha3: "SLV", name: "El Salvador" },
|
|
22508
|
+
{ alpha2: "SX", alpha3: "SXM", name: "Sint Maarten (Dutch part)" },
|
|
22509
|
+
{ alpha2: "SY", alpha3: "SYR", name: "Syrian Arab Republic" },
|
|
22510
|
+
{ alpha2: "SZ", alpha3: "SWZ", name: "Eswatini" },
|
|
22511
|
+
{ alpha2: "TC", alpha3: "TCA", name: "Turks and Caicos Islands" },
|
|
22512
|
+
{ alpha2: "TD", alpha3: "TCD", name: "Chad" },
|
|
22513
|
+
{ alpha2: "TF", alpha3: "ATF", name: "French Southern Territories" },
|
|
22514
|
+
{ alpha2: "TG", alpha3: "TGO", name: "Togo" },
|
|
22515
|
+
{ alpha2: "TH", alpha3: "THA", name: "Thailand" },
|
|
22516
|
+
{ alpha2: "TJ", alpha3: "TJK", name: "Tajikistan" },
|
|
22517
|
+
{ alpha2: "TK", alpha3: "TKL", name: "Tokelau" },
|
|
22518
|
+
{ alpha2: "TL", alpha3: "TLS", name: "Timor-Leste" },
|
|
22519
|
+
{ alpha2: "TM", alpha3: "TKM", name: "Turkmenistan" },
|
|
22520
|
+
{ alpha2: "TN", alpha3: "TUN", name: "Tunisia" },
|
|
22521
|
+
{ alpha2: "TO", alpha3: "TON", name: "Tonga" },
|
|
22522
|
+
{ alpha2: "TR", alpha3: "TUR", name: "T\xFCrkiye" },
|
|
22523
|
+
{ alpha2: "TT", alpha3: "TTO", name: "Trinidad and Tobago" },
|
|
22524
|
+
{ alpha2: "TV", alpha3: "TUV", name: "Tuvalu" },
|
|
22525
|
+
{ alpha2: "TW", alpha3: "TWN", name: "Taiwan, Province of China" },
|
|
22526
|
+
{ alpha2: "TZ", alpha3: "TZA", name: "Tanzania, United Republic of" },
|
|
22527
|
+
{ alpha2: "UA", alpha3: "UKR", name: "Ukraine" },
|
|
22528
|
+
{ alpha2: "UG", alpha3: "UGA", name: "Uganda" },
|
|
22529
|
+
{ alpha2: "UM", alpha3: "UMI", name: "United States Minor Outlying Islands" },
|
|
22530
|
+
{ alpha2: "US", alpha3: "USA", name: "United States of America" },
|
|
22531
|
+
{ alpha2: "UY", alpha3: "URY", name: "Uruguay" },
|
|
22532
|
+
{ alpha2: "UZ", alpha3: "UZB", name: "Uzbekistan" },
|
|
22533
|
+
{ alpha2: "VA", alpha3: "VAT", name: "Holy See" },
|
|
22534
|
+
{ alpha2: "VC", alpha3: "VCT", name: "Saint Vincent and the Grenadines" },
|
|
22535
|
+
{ alpha2: "VE", alpha3: "VEN", name: "Venezuela (Bolivarian Republic of)" },
|
|
22536
|
+
{ alpha2: "VG", alpha3: "VGB", name: "Virgin Islands (British)" },
|
|
22537
|
+
{ alpha2: "VI", alpha3: "VIR", name: "Virgin Islands (U.S.)" },
|
|
22538
|
+
{ alpha2: "VN", alpha3: "VNM", name: "Viet Nam" },
|
|
22539
|
+
{ alpha2: "VU", alpha3: "VUT", name: "Vanuatu" },
|
|
22540
|
+
{ alpha2: "WF", alpha3: "WLF", name: "Wallis and Futuna" },
|
|
22541
|
+
{ alpha2: "WS", alpha3: "WSM", name: "Samoa" },
|
|
22542
|
+
{ alpha2: "YE", alpha3: "YEM", name: "Yemen" },
|
|
22543
|
+
{ alpha2: "YT", alpha3: "MYT", name: "Mayotte" },
|
|
22544
|
+
{ alpha2: "ZA", alpha3: "ZAF", name: "South Africa" },
|
|
22545
|
+
{ alpha2: "ZM", alpha3: "ZMB", name: "Zambia" },
|
|
22546
|
+
{ alpha2: "ZW", alpha3: "ZWE", name: "Zimbabwe" }
|
|
22547
|
+
];
|
|
22548
|
+
var COMMON_ALIASES = {
|
|
22549
|
+
usa: "US",
|
|
22550
|
+
america: "US",
|
|
22551
|
+
unitedstates: "US",
|
|
22552
|
+
uk: "GB",
|
|
22553
|
+
unitedkingdom: "GB",
|
|
22554
|
+
britain: "GB",
|
|
22555
|
+
greatbritain: "GB",
|
|
22556
|
+
southkorea: "KR",
|
|
22557
|
+
northkorea: "KP",
|
|
22558
|
+
russia: "RU",
|
|
22559
|
+
vietnam: "VN",
|
|
22560
|
+
laos: "LA",
|
|
22561
|
+
syria: "SY",
|
|
22562
|
+
iran: "IR",
|
|
22563
|
+
ivorycoast: "CI",
|
|
22564
|
+
czechrepublic: "CZ",
|
|
22565
|
+
swaziland: "SZ",
|
|
22566
|
+
burma: "MM",
|
|
22567
|
+
capeverde: "CV",
|
|
22568
|
+
easttimor: "TL",
|
|
22569
|
+
taiwan: "TW",
|
|
22570
|
+
macedonia: "MK",
|
|
22571
|
+
vatican: "VA",
|
|
22572
|
+
vaticancity: "VA",
|
|
22573
|
+
holland: "NL",
|
|
22574
|
+
moldova: "MD",
|
|
22575
|
+
bolivia: "BO",
|
|
22576
|
+
tanzania: "TZ",
|
|
22577
|
+
venezuela: "VE",
|
|
22578
|
+
palestine: "PS",
|
|
22579
|
+
brunei: "BN"
|
|
22580
|
+
};
|
|
22581
|
+
function normalize(s) {
|
|
22582
|
+
return s.toLowerCase().trim().replace(/[^a-z0-9]/g, "");
|
|
22583
|
+
}
|
|
22584
|
+
function isValidCountry(input) {
|
|
22585
|
+
const trimmed = input.trim();
|
|
22586
|
+
if (/^[A-Za-z]{2}$/.test(trimmed)) {
|
|
22587
|
+
return COUNTRIES.some((c) => c.alpha2 === trimmed.toUpperCase());
|
|
22588
|
+
}
|
|
22589
|
+
if (/^[A-Za-z]{3}$/.test(trimmed)) {
|
|
22590
|
+
return COUNTRIES.some((c) => c.alpha3 === trimmed.toUpperCase());
|
|
22591
|
+
}
|
|
22592
|
+
const normalized = normalize(trimmed);
|
|
22593
|
+
if (COMMON_ALIASES[normalized]) return true;
|
|
22594
|
+
return COUNTRIES.some((c) => normalize(c.name) === normalized);
|
|
22122
22595
|
}
|
|
22123
22596
|
|
|
22124
22597
|
// src/template-aliases.ts
|
|
22598
|
+
function validateDob(value) {
|
|
22599
|
+
if (typeof value !== "string") return "dob must be a string in YYYY-MM-DD format";
|
|
22600
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return `dob must match the format YYYY-MM-DD, got "${value}"`;
|
|
22601
|
+
const [year, month, day] = value.split("-").map(Number);
|
|
22602
|
+
const date3 = new Date(Date.UTC(year, month - 1, day));
|
|
22603
|
+
const isRealCalendarDate = date3.getUTCFullYear() === year && date3.getUTCMonth() === month - 1 && date3.getUTCDate() === day;
|
|
22604
|
+
if (!isRealCalendarDate) return `dob is not a valid calendar date: "${value}"`;
|
|
22605
|
+
return void 0;
|
|
22606
|
+
}
|
|
22607
|
+
function validateCountryOfOrigin(value) {
|
|
22608
|
+
if (typeof value !== "string" || !value.trim()) return "countryOfOrigin must be a non-empty string";
|
|
22609
|
+
if (!isValidCountry(value)) return `countryOfOrigin must be an ISO 3166 country code or name, got "${value}"`;
|
|
22610
|
+
return void 0;
|
|
22611
|
+
}
|
|
22125
22612
|
var TEMPLATE_ALIASES = [
|
|
22126
22613
|
{
|
|
22127
22614
|
match: "birthcert",
|
|
22128
|
-
testnet: "did:zid:
|
|
22129
|
-
mainnet: "did:zid:
|
|
22615
|
+
testnet: "did:zid:3c0fb79adff08e14e06dcd6e3243205010dd65f533434a3d96c55575d1d3d959",
|
|
22616
|
+
mainnet: "did:zid:19091d19049abb8869b4b8e2f4a887bd1d1d86e5f5ebd0c8297000255f67765b",
|
|
22617
|
+
deriveAttributes: { id: "agentUsername" },
|
|
22618
|
+
validateAttributes: { dob: validateDob, countryOfOrigin: validateCountryOfOrigin }
|
|
22130
22619
|
}
|
|
22131
22620
|
];
|
|
22132
|
-
function
|
|
22621
|
+
function normalize2(s) {
|
|
22133
22622
|
return s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
22134
22623
|
}
|
|
22624
|
+
function findEntry(templateId, network) {
|
|
22625
|
+
if (/^did:zid:/.test(templateId)) {
|
|
22626
|
+
const key = network.includes("testnet") ? "testnet" : "mainnet";
|
|
22627
|
+
return TEMPLATE_ALIASES.find((e) => e[key] === templateId);
|
|
22628
|
+
}
|
|
22629
|
+
const normalized = normalize2(templateId);
|
|
22630
|
+
return TEMPLATE_ALIASES.find((e) => normalized.includes(e.match));
|
|
22631
|
+
}
|
|
22135
22632
|
function resolveTemplateAlias(input, network) {
|
|
22136
22633
|
if (/^did:zid:/.test(input)) return void 0;
|
|
22137
|
-
const
|
|
22138
|
-
const entry = TEMPLATE_ALIASES.find((e) => normalized.includes(e.match));
|
|
22634
|
+
const entry = findEntry(input, network);
|
|
22139
22635
|
if (!entry) return void 0;
|
|
22140
22636
|
return network.includes("testnet") ? entry.testnet : entry.mainnet;
|
|
22141
22637
|
}
|
|
22638
|
+
function deriveTemplateAttributes(templateId, network, attributes) {
|
|
22639
|
+
const entry = findEntry(templateId, network);
|
|
22640
|
+
if (!entry?.deriveAttributes) return attributes;
|
|
22641
|
+
const result = { ...attributes };
|
|
22642
|
+
for (const [target, source] of Object.entries(entry.deriveAttributes)) {
|
|
22643
|
+
if ((result[target] === void 0 || result[target] === null || result[target] === "") && result[source] !== void 0) {
|
|
22644
|
+
result[target] = result[source];
|
|
22645
|
+
}
|
|
22646
|
+
}
|
|
22647
|
+
return result;
|
|
22648
|
+
}
|
|
22649
|
+
function derivedAttributeKeys(templateId, network) {
|
|
22650
|
+
const entry = findEntry(templateId, network);
|
|
22651
|
+
return entry?.deriveAttributes ? Object.keys(entry.deriveAttributes) : [];
|
|
22652
|
+
}
|
|
22653
|
+
function validateTemplateAttributes(templateId, network, attributes) {
|
|
22654
|
+
const entry = findEntry(templateId, network);
|
|
22655
|
+
if (!entry?.validateAttributes) return [];
|
|
22656
|
+
const errors = [];
|
|
22657
|
+
for (const [key, validate] of Object.entries(entry.validateAttributes)) {
|
|
22658
|
+
const value = attributes[key];
|
|
22659
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
22660
|
+
const error2 = validate(value);
|
|
22661
|
+
if (error2) errors.push(error2);
|
|
22662
|
+
}
|
|
22663
|
+
return errors;
|
|
22664
|
+
}
|
|
22142
22665
|
|
|
22143
22666
|
// src/mcp-tools.ts
|
|
22144
22667
|
async function loadValidCachedCredentials(cache) {
|
|
@@ -22150,13 +22673,15 @@ function createTools(deps) {
|
|
|
22150
22673
|
return {
|
|
22151
22674
|
async wallet_status(input = {}) {
|
|
22152
22675
|
const balances = deps.getBalances ? await deps.getBalances() : void 0;
|
|
22676
|
+
const tokenBalance = input.token && deps.queryTokenBalance ? await deps.queryTokenBalance(input.token) : void 0;
|
|
22153
22677
|
const credentials = input.heldCredentials ?? (await loadValidCachedCredentials(deps.cache)).map((entry) => entry.vc);
|
|
22154
22678
|
return {
|
|
22155
22679
|
holderDid: deps.config.holderDid,
|
|
22156
22680
|
zetrixAddress: deps.config.zetrixAddress,
|
|
22157
22681
|
network: deps.config.network,
|
|
22158
22682
|
credentials,
|
|
22159
|
-
...balances !== void 0 ? { balances } : {}
|
|
22683
|
+
...balances !== void 0 ? { balances } : {},
|
|
22684
|
+
...tokenBalance !== void 0 ? { tokenBalance } : {}
|
|
22160
22685
|
};
|
|
22161
22686
|
},
|
|
22162
22687
|
async prove_identity(input) {
|
|
@@ -22182,12 +22707,39 @@ function createTools(deps) {
|
|
|
22182
22707
|
pay_and_fetch(input) {
|
|
22183
22708
|
return payAndFetch(deps.payer, input);
|
|
22184
22709
|
},
|
|
22185
|
-
subscribe_and_issue(input) {
|
|
22710
|
+
async subscribe_and_issue(input) {
|
|
22186
22711
|
const resolved = resolveTemplateAlias(input.templateId, deps.config.network);
|
|
22187
|
-
|
|
22712
|
+
const templateId = resolved ?? input.templateId;
|
|
22713
|
+
const attributes = deriveTemplateAttributes(templateId, deps.config.network, input.attributes ?? {});
|
|
22714
|
+
const errors = validateTemplateAttributes(templateId, deps.config.network, attributes);
|
|
22715
|
+
if (errors.length > 0) {
|
|
22716
|
+
return { issued: false, reason: errors.join("; ") };
|
|
22717
|
+
}
|
|
22718
|
+
const result = await subscribeAndIssue(deps.subscribeDeps, { ...input, templateId, attributes });
|
|
22719
|
+
if (!result.schema) return result;
|
|
22720
|
+
const hidden = /* @__PURE__ */ new Set(["agentDid", ...derivedAttributeKeys(templateId, deps.config.network)]);
|
|
22721
|
+
return {
|
|
22722
|
+
...result,
|
|
22723
|
+
schema: {
|
|
22724
|
+
required: result.schema.required.filter((k) => !hidden.has(k)),
|
|
22725
|
+
optional: result.schema.optional.filter((k) => !hidden.has(k))
|
|
22726
|
+
}
|
|
22727
|
+
};
|
|
22728
|
+
},
|
|
22729
|
+
query_contract(input) {
|
|
22730
|
+
return deps.queryContract(input);
|
|
22188
22731
|
},
|
|
22189
22732
|
create_holder_account(input) {
|
|
22190
|
-
return createHolderAccount(
|
|
22733
|
+
return createHolderAccount(
|
|
22734
|
+
{
|
|
22735
|
+
create: deps.createAccount,
|
|
22736
|
+
getExistingAccount: () => Promise.resolve(deps.config.zetrixAddress ? { zetrixAddress: deps.config.zetrixAddress, holderDid: deps.config.holderDid } : null),
|
|
22737
|
+
saveAccount: deps.saveAccount,
|
|
22738
|
+
checkActivationStatus: deps.checkActivationStatus,
|
|
22739
|
+
sleep: deps.sleep
|
|
22740
|
+
},
|
|
22741
|
+
input
|
|
22742
|
+
);
|
|
22191
22743
|
}
|
|
22192
22744
|
};
|
|
22193
22745
|
}
|
|
@@ -22195,8 +22747,16 @@ function createTools(deps) {
|
|
|
22195
22747
|
// src/orchestrator/resolve-holder.ts
|
|
22196
22748
|
async function resolveHolder(deps, input) {
|
|
22197
22749
|
if (!input.zetrixAddress) {
|
|
22198
|
-
const { zetrixAddress, publicKeyHex } = await deps.createAccount(input.hsmPassword);
|
|
22199
|
-
|
|
22750
|
+
const { zetrixAddress, publicKeyHex, activated } = await deps.createAccount(input.hsmPassword);
|
|
22751
|
+
let finalActivated = activated;
|
|
22752
|
+
if (!finalActivated) {
|
|
22753
|
+
try {
|
|
22754
|
+
finalActivated = await waitForActivation(deps.checkActivationStatus, zetrixAddress, deps.sleep);
|
|
22755
|
+
} catch {
|
|
22756
|
+
finalActivated = false;
|
|
22757
|
+
}
|
|
22758
|
+
}
|
|
22759
|
+
return { zetrixAddress, holderDid: deriveHolderDid(publicKeyHex), created: true, didMismatch: false, activated: finalActivated };
|
|
22200
22760
|
}
|
|
22201
22761
|
const { publicKey } = await deps.signMessage(input.zetrixAddress, input.zetrixAddress, input.hsmPassword);
|
|
22202
22762
|
const derivedDid = deriveHolderDid(publicKey);
|
|
@@ -22204,6 +22764,30 @@ async function resolveHolder(deps, input) {
|
|
|
22204
22764
|
return { zetrixAddress: input.zetrixAddress, holderDid: derivedDid, created: false, didMismatch };
|
|
22205
22765
|
}
|
|
22206
22766
|
|
|
22767
|
+
// src/clients/account-store.ts
|
|
22768
|
+
var import_promises2 = require("node:fs/promises");
|
|
22769
|
+
var import_node_path2 = require("node:path");
|
|
22770
|
+
function isStoredAccountShape(value) {
|
|
22771
|
+
return typeof value === "object" && value !== null && typeof value.zetrixAddress === "string" && typeof value.holderDid === "string" && typeof value.hsmPassword === "string";
|
|
22772
|
+
}
|
|
22773
|
+
function createFsAccountStore(filePath) {
|
|
22774
|
+
return {
|
|
22775
|
+
async get() {
|
|
22776
|
+
try {
|
|
22777
|
+
const raw = await (0, import_promises2.readFile)(filePath, "utf8");
|
|
22778
|
+
const parsed = JSON.parse(raw);
|
|
22779
|
+
return isStoredAccountShape(parsed) ? parsed : null;
|
|
22780
|
+
} catch {
|
|
22781
|
+
return null;
|
|
22782
|
+
}
|
|
22783
|
+
},
|
|
22784
|
+
async set(account) {
|
|
22785
|
+
await (0, import_promises2.mkdir)((0, import_node_path2.dirname)(filePath), { recursive: true, mode: 448 });
|
|
22786
|
+
await (0, import_promises2.writeFile)(filePath, JSON.stringify(account, null, 2), { encoding: "utf8", mode: 384 });
|
|
22787
|
+
}
|
|
22788
|
+
};
|
|
22789
|
+
}
|
|
22790
|
+
|
|
22207
22791
|
// src/index.ts
|
|
22208
22792
|
var packageVersion = package_default.version;
|
|
22209
22793
|
function buildToolList() {
|
|
@@ -22214,7 +22798,8 @@ function buildToolList() {
|
|
|
22214
22798
|
inputSchema: {
|
|
22215
22799
|
type: "object",
|
|
22216
22800
|
properties: {
|
|
22217
|
-
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." }
|
|
22801
|
+
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." },
|
|
22802
|
+
token: { type: "string", description: 'Optional token symbol (e.g. "ZTX", "JMYR") to check its balance for the active network, alongside the usual status fields.' }
|
|
22218
22803
|
}
|
|
22219
22804
|
}
|
|
22220
22805
|
},
|
|
@@ -22253,9 +22838,22 @@ function buildToolList() {
|
|
|
22253
22838
|
required: ["url"]
|
|
22254
22839
|
}
|
|
22255
22840
|
},
|
|
22841
|
+
{
|
|
22842
|
+
name: "query_contract",
|
|
22843
|
+
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.',
|
|
22844
|
+
inputSchema: {
|
|
22845
|
+
type: "object",
|
|
22846
|
+
properties: {
|
|
22847
|
+
contractAddress: { type: "string", description: "Zetrix contract address to query." },
|
|
22848
|
+
method: { type: "string", description: 'Contract method name, e.g. "balanceOf", "contractInfo".' },
|
|
22849
|
+
params: { type: "object", description: 'Method parameters, e.g. { "address": "ZTX..." } for balanceOf.' }
|
|
22850
|
+
},
|
|
22851
|
+
required: ["contractAddress", "method"]
|
|
22852
|
+
}
|
|
22853
|
+
},
|
|
22256
22854
|
{
|
|
22257
22855
|
name: "subscribe_and_issue",
|
|
22258
|
-
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.",
|
|
22856
|
+
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. Every response (success, dry-run, or a missing-attribute error) also includes { schema: { required, optional } } \u2014 the template's full declared attribute schema read from chain \u2014 so you always see the complete field list, not just what went wrong.",
|
|
22259
22857
|
inputSchema: {
|
|
22260
22858
|
type: "object",
|
|
22261
22859
|
properties: {
|
|
@@ -22270,7 +22868,7 @@ function buildToolList() {
|
|
|
22270
22868
|
expirationDate: { type: "string" },
|
|
22271
22869
|
dryRun: {
|
|
22272
22870
|
type: "boolean",
|
|
22273
|
-
description: "Stop after MBI's free phase-1 quote and return { quote: { asset, maxAmountRequired, payTo,
|
|
22871
|
+
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."
|
|
22274
22872
|
},
|
|
22275
22873
|
forceReissue: {
|
|
22276
22874
|
type: "boolean",
|
|
@@ -22282,13 +22880,17 @@ function buildToolList() {
|
|
|
22282
22880
|
},
|
|
22283
22881
|
{
|
|
22284
22882
|
name: "create_holder_account",
|
|
22285
|
-
description: "Create a new holder HSM account on Wallet BE (onboarding
|
|
22883
|
+
description: "Create a new holder HSM account on Wallet BE (onboarding). Ask the user for a password first \u2014 never invent one. ALWAYS check first: if an account already exists for this session, this returns { alreadyExists: true, existing: {...} } WITHOUT creating anything \u2014 ask the user whether to keep using the existing account or create a new one, then call again with confirmNew:true only if they choose new. A freshly created account is saved to this MCP's local account store and reused automatically on the next restart; an explicit ZETRIX_ADDRESS in the MCP config still overrides it.",
|
|
22286
22884
|
inputSchema: {
|
|
22287
22885
|
type: "object",
|
|
22288
22886
|
properties: {
|
|
22289
22887
|
password: { type: "string", description: "HSM password to protect the new account. Must come from the user." },
|
|
22290
22888
|
label: { type: "string" },
|
|
22291
|
-
purpose: { type: "string" }
|
|
22889
|
+
purpose: { type: "string" },
|
|
22890
|
+
confirmNew: {
|
|
22891
|
+
type: "boolean",
|
|
22892
|
+
description: "Set true to mint a new account even though one already exists for this session \u2014 only after the user has confirmed they want a new one."
|
|
22893
|
+
}
|
|
22292
22894
|
},
|
|
22293
22895
|
required: ["password"]
|
|
22294
22896
|
}
|
|
@@ -22300,25 +22902,46 @@ function asPayRequest(accept) {
|
|
|
22300
22902
|
return { ...accept, extra: { gasModel: "client", ...extra } };
|
|
22301
22903
|
}
|
|
22302
22904
|
async function main() {
|
|
22303
|
-
const
|
|
22905
|
+
const accountStore = createFsAccountStore((0, import_node_path3.join)((0, import_node_os.homedir)(), ".agentic-wallet-mcp", "account.json"));
|
|
22906
|
+
const storedAccount = process.env.ZETRIX_ADDRESS ? null : await accountStore.get();
|
|
22907
|
+
const env = { ...process.env };
|
|
22908
|
+
if (!env.ZETRIX_ADDRESS && storedAccount) env.ZETRIX_ADDRESS = storedAccount.zetrixAddress;
|
|
22909
|
+
if (!env.HOLDER_DID && storedAccount) env.HOLDER_DID = storedAccount.holderDid;
|
|
22910
|
+
if (!env.HSM_PASSWORD && storedAccount) env.HSM_PASSWORD = storedAccount.hsmPassword;
|
|
22911
|
+
const config2 = loadConfig(env);
|
|
22304
22912
|
const hsmPassword = config2.hsmPassword;
|
|
22305
22913
|
const be = new WalletBeClient(config2.walletBeUrl);
|
|
22306
|
-
const
|
|
22914
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
22915
|
+
const { zetrixAddress, holderDid, created, didMismatch, activated } = await resolveHolder(
|
|
22307
22916
|
{
|
|
22308
22917
|
createAccount: (password) => be.createAccount(password),
|
|
22309
|
-
signMessage: (message, address, password) => be.signMessage(message, address, password)
|
|
22918
|
+
signMessage: (message, address, password) => be.signMessage(message, address, password),
|
|
22919
|
+
checkActivationStatus: (address) => be.checkActivationStatus(address),
|
|
22920
|
+
sleep
|
|
22310
22921
|
},
|
|
22311
22922
|
{ zetrixAddress: config2.zetrixAddress, holderDid: config2.holderDid, hsmPassword }
|
|
22312
22923
|
);
|
|
22313
22924
|
if (created) {
|
|
22925
|
+
await accountStore.set({ zetrixAddress, holderDid, hsmPassword, createdAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
22926
|
+
process.stderr.write(
|
|
22927
|
+
`agentic-wallet-mcp: no ZETRIX_ADDRESS was set \u2014 created a new HSM account and saved it (address, DID, and password) to ~/.agentic-wallet-mcp/account.json; it will be reused automatically next run. ZETRIX_ADDRESS=${zetrixAddress} (HOLDER_DID=${holderDid} is optional; it re-derives automatically).
|
|
22928
|
+
`
|
|
22929
|
+
);
|
|
22930
|
+
} else if (storedAccount && config2.zetrixAddress === storedAccount.zetrixAddress) {
|
|
22314
22931
|
process.stderr.write(
|
|
22315
|
-
`agentic-wallet-mcp:
|
|
22932
|
+
`agentic-wallet-mcp: using the holder account saved in ~/.agentic-wallet-mcp/account.json (ZETRIX_ADDRESS=${zetrixAddress}) \u2014 no ZETRIX_ADDRESS/HSM_PASSWORD was set in the MCP config.
|
|
22316
22933
|
`
|
|
22317
22934
|
);
|
|
22318
22935
|
}
|
|
22319
22936
|
if (didMismatch) {
|
|
22320
22937
|
process.stderr.write(
|
|
22321
22938
|
`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.
|
|
22939
|
+
`
|
|
22940
|
+
);
|
|
22941
|
+
}
|
|
22942
|
+
if (created && !activated) {
|
|
22943
|
+
process.stderr.write(
|
|
22944
|
+
`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.
|
|
22322
22945
|
`
|
|
22323
22946
|
);
|
|
22324
22947
|
}
|
|
@@ -22329,14 +22952,36 @@ async function main() {
|
|
|
22329
22952
|
const sdk = new import_zetrix_sdk_nodejs.default({ host: config2.nodeHost, port: config2.nodePort });
|
|
22330
22953
|
const contractQuery = (a) => sdk.contract.call(a);
|
|
22331
22954
|
const resolveSymbol = (asset) => resolveAssetSymbol(asset, contractQuery);
|
|
22955
|
+
const queryTokenBalance = async (token) => {
|
|
22956
|
+
const symbol = token.toUpperCase();
|
|
22957
|
+
if (symbol === "ZTX") {
|
|
22958
|
+
try {
|
|
22959
|
+
const { balance } = await import_x402_zetrix_client2.PaymentEngine.fetchAccountInfo(zetrixAddress, node);
|
|
22960
|
+
return { token: symbol, balance };
|
|
22961
|
+
} catch {
|
|
22962
|
+
return { token: symbol, error: "query_failed" };
|
|
22963
|
+
}
|
|
22964
|
+
}
|
|
22965
|
+
const contractAddress = resolveTokenAddress(symbol, config2.network);
|
|
22966
|
+
if (!contractAddress) return { token: symbol, error: "unknown_token" };
|
|
22967
|
+
try {
|
|
22968
|
+
const { balance } = await import_x402_zetrix_client2.PaymentEngine.fetchZTP20Balance(contractAddress, zetrixAddress, node);
|
|
22969
|
+
return { token: symbol, balance };
|
|
22970
|
+
} catch {
|
|
22971
|
+
return { token: symbol, error: "query_failed" };
|
|
22972
|
+
}
|
|
22973
|
+
};
|
|
22332
22974
|
const nodeBaseUrl = `https://${config2.nodeHost}${config2.nodePort ? `:${config2.nodePort}` : ""}`;
|
|
22333
22975
|
const nodeMetaQuery = (url) => fetch(url, { headers: { Accept: "application/json" } }).then((r) => r.json());
|
|
22334
22976
|
const resolveTemplateFields = (templateId) => fetchTemplateFields(templateId, config2.templateRegistryAddress, nodeBaseUrl, nodeMetaQuery);
|
|
22335
22977
|
const cacheScope = (0, import_node_crypto2.createHash)("sha256").update(`${config2.network}:${zetrixAddress}`).digest("hex");
|
|
22336
|
-
const vcCache = createFsVcCache((0,
|
|
22978
|
+
const vcCache = createFsVcCache((0, import_node_path3.join)((0, import_node_os.homedir)(), ".agentic-wallet-mcp", "vc-cache", cacheScope));
|
|
22337
22979
|
const pay = (accept) => {
|
|
22338
22980
|
assertWithinPaymentCap(accept, config2.maxPaymentAmount);
|
|
22339
|
-
return
|
|
22981
|
+
return payWithReadinessCheck(
|
|
22982
|
+
String(accept.asset ?? ""),
|
|
22983
|
+
() => import_x402_zetrix_client2.PaymentEngine.pay(asPayRequest(accept), walletCfg, node, {}, walletBeSignerFn)
|
|
22984
|
+
);
|
|
22340
22985
|
};
|
|
22341
22986
|
const payer = async (req) => {
|
|
22342
22987
|
const init = { method: req.method ?? "GET", headers: req.headers, body: req.body };
|
|
@@ -22347,7 +22992,15 @@ async function main() {
|
|
|
22347
22992
|
const parsed = await res.json();
|
|
22348
22993
|
const accept = parsed.accepts?.[0];
|
|
22349
22994
|
if (!accept) throw new Error("pay_and_fetch: 402 had no accepts[]");
|
|
22350
|
-
|
|
22995
|
+
let xPayment;
|
|
22996
|
+
try {
|
|
22997
|
+
xPayment = await pay(accept);
|
|
22998
|
+
} catch (err) {
|
|
22999
|
+
if (err instanceof PaymentReadinessError) {
|
|
23000
|
+
return { status: 402, body: "", paymentMade: false, amountPaid: "", amountPaidHuman: "", asset: "", insufficientFunds: err.shortfall };
|
|
23001
|
+
}
|
|
23002
|
+
throw err;
|
|
23003
|
+
}
|
|
22351
23004
|
const retry = await fetch(req.url, { ...init, headers: { ...req.headers ?? {}, "x-payment": xPayment } });
|
|
22352
23005
|
const asset = await resolveSymbol(String(accept.asset ?? ""));
|
|
22353
23006
|
return {
|
|
@@ -22377,7 +23030,12 @@ async function main() {
|
|
|
22377
23030
|
makeWallet,
|
|
22378
23031
|
payer,
|
|
22379
23032
|
subscribeDeps: { mbi, sign: subscribeSign, pay, resolveSymbol, holderDid, resolveTemplateFields, cache: vcCache },
|
|
23033
|
+
queryContract: (input) => queryContract(input, contractQuery),
|
|
23034
|
+
queryTokenBalance,
|
|
22380
23035
|
createAccount: (password, label, purpose) => be.createAccount(password, label, purpose),
|
|
23036
|
+
saveAccount: (account) => accountStore.set({ ...account, createdAt: (/* @__PURE__ */ new Date()).toISOString() }),
|
|
23037
|
+
checkActivationStatus: (address) => be.checkActivationStatus(address),
|
|
23038
|
+
sleep,
|
|
22381
23039
|
cache: vcCache
|
|
22382
23040
|
};
|
|
22383
23041
|
const tools = createTools(deps);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-wallet-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Agent-facing MCP wallet for Zetrix — orchestrates x401 identity proof, x402 payment, and MBI VC issuance",
|
|
5
5
|
"keywords": ["mcp", "model-context-protocol", "zetrix", "wallet", "x401", "x402", "blockchain"],
|
|
6
6
|
"license": "MIT",
|