agentic-wallet-mcp 0.6.0 → 0.7.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/CHANGELOG.md +65 -0
- package/README.md +51 -25
- package/dist/server-bundle.cjs +190 -43
- package/package.json +55 -55
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,71 @@ 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.7.0] — 4 August 2026
|
|
12
|
+
|
|
13
|
+
**Read the breaking change before upgrading if you make x402 payments.** This release lets the wallet
|
|
14
|
+
provision itself, so it can start with no configuration at all — which is what makes an OpenClaw plugin
|
|
15
|
+
install possible, and also why the payment cap now has to default to zero.
|
|
16
|
+
|
|
17
|
+
### Changed (breaking)
|
|
18
|
+
|
|
19
|
+
- **`MAX_PAYMENT_AMOUNT` now defaults to `{"*":"0"}` instead of being unset.** An unconfigured wallet
|
|
20
|
+
refuses every x402 payment rather than allowing any amount. Previously an unset cap disabled the
|
|
21
|
+
ceiling entirely, which was defensible only while every install required hand-written environment
|
|
22
|
+
variables; a wallet that can now start with no configuration must not be able to auto-pay a hostile
|
|
23
|
+
challenge. To keep paying, set the cap explicitly, e.g.
|
|
24
|
+
`MAX_PAYMENT_AMOUNT={"ZTX":"1000000000","*":"0"}`.
|
|
25
|
+
|
|
26
|
+
- **`create_holder_account` no longer takes a `password` parameter.** The wallet uses the password
|
|
27
|
+
already active for the session, so a model can neither be asked for one nor supply one. A
|
|
28
|
+
`password` still passed by an old caller is **silently ignored** rather than rejected, because the
|
|
29
|
+
tool schema permits extra properties. Consequence: a newly minted account now inherits the session
|
|
30
|
+
password instead of taking a different one.
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- **The wallet provisions itself.** `HSM_PASSWORD` and `ZETRIX_NETWORK` are both optional now.
|
|
35
|
+
With neither set, the wallet defaults to testnet, generates a random HSM password, creates a
|
|
36
|
+
holder account, and stores address, DID and password in its own state directory — so it starts
|
|
37
|
+
with no configuration at all. An explicit env var still wins over every other source, so an
|
|
38
|
+
existing `.mcp.json` behaves exactly as before.
|
|
39
|
+
- **`npx agentic-wallet-mcp export-credentials`** prints the address, DID and HSM password for
|
|
40
|
+
backup. A generated password is the only thing that can authorize signing for the account, so
|
|
41
|
+
losing the state directory means losing the account. Interactive terminals only, and deliberately
|
|
42
|
+
not an MCP tool, so an agent can never read it.
|
|
43
|
+
- **`--config <path>`** reads settings from a JSON file instead of the environment, for hosts that
|
|
44
|
+
cannot set env vars. It carries no secret: a `hsmPassword` key is a hard error, and so is any
|
|
45
|
+
unknown key, so a typo cannot silently start the wallet on the wrong network.
|
|
46
|
+
- **`ZETRIX_WALLET_STATE_DIR`** moves `account.json` and the VC cache out of the home directory.
|
|
47
|
+
The default is unchanged.
|
|
48
|
+
- `pay_and_fetch` and `subscribe_and_issue` now report a `not_activated` shortfall when the holder
|
|
49
|
+
address is not yet on chain, instead of reporting it as a low balance — the remedy is to send it
|
|
50
|
+
gas, not to top up a token.
|
|
51
|
+
|
|
52
|
+
### Fixed
|
|
53
|
+
|
|
54
|
+
- **A zero ZTX balance no longer reports `query_failed`.** The node omits the `balance` field
|
|
55
|
+
entirely when it is zero, and `wallet_status({ token: "ZTX" })` treated its absence as a failed
|
|
56
|
+
read — so every account holding no ZTX looked like a broken lookup. A successful RPC that omits a
|
|
57
|
+
zero-valued field is now read as `0`. A non-zero `errorCode`, or a response with no `result` at
|
|
58
|
+
all, still fails loudly rather than reporting a fabricated zero.
|
|
59
|
+
|
|
60
|
+
## [0.6.1] — 2026-07-29
|
|
61
|
+
|
|
62
|
+
**Upgrade if you use testnet.** The testnet Wallet BE and MBI endpoints have moved to the Zetrix
|
|
63
|
+
sandbox hosts. No API, tool or config surface changed — this is an endpoint migration only.
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- **Testnet default endpoints moved to the Zetrix sandbox hosts.** `ZETRIX_NETWORK=zetrix:testnet`
|
|
68
|
+
now derives `https://wallet-api-sandbox.zetrix.com/server` (was
|
|
69
|
+
`https://wallet-api.myegdev.com/server`) and `https://mbi-vc-sandbox.zetrix.com` (was
|
|
70
|
+
`https://mbi-vc.myegdev.com`). Anyone on testnet relying on the built-in defaults should upgrade
|
|
71
|
+
to follow the platform; mainnet defaults are unchanged, and an explicit `WALLET_BE_URL` /
|
|
72
|
+
`MBI_BASE_URL` still wins over the network default, so anyone pinning those is unaffected.
|
|
73
|
+
Note the Wallet BE base keeps its `/server` path suffix — verified against the new host, which
|
|
74
|
+
returns nginx `404` without it.
|
|
75
|
+
|
|
11
76
|
## [0.6.0] — 2026-07-28
|
|
12
77
|
|
|
13
78
|
This release is about the wallet never misstating what a call cost. Four reporting defects were
|
package/README.md
CHANGED
|
@@ -165,8 +165,8 @@ Node ≥ 18 required (built-in `fetch`).
|
|
|
165
165
|
|
|
166
166
|
| Variable | Required | Description |
|
|
167
167
|
|---|---|---|
|
|
168
|
-
| `ZETRIX_NETWORK` |
|
|
169
|
-
| `HSM_PASSWORD` |
|
|
168
|
+
| `ZETRIX_NETWORK` | no | `zetrix:testnet` or `zetrix:mainnet` — also selects the default `WALLET_BE_URL`/`MBI_BASE_URL`/`OID4VP_BASE_URL`/`ZID_RESOLVER_BASE_URL` below. **Defaults to `zetrix:testnet`**; mainnet is always a deliberate choice |
|
|
169
|
+
| `HSM_PASSWORD` | no* | HSM password. Omit it and the wallet generates one on first run and stores it in its own state directory — see "Onboarding" below |
|
|
170
170
|
| `ZETRIX_ADDRESS` | no | Holder Zetrix address (the HSM account). Omit on first run — see "Onboarding" below |
|
|
171
171
|
| `HOLDER_DID` | no | Holder DID. Omit and the MCP derives it automatically — see "Onboarding" below |
|
|
172
172
|
| `WALLET_BE_URL` | no | Wallet BE base URL override (HSM `/wallet/hsm/sign-blob`) — auto-derived from `ZETRIX_NETWORK` when not set |
|
|
@@ -174,22 +174,25 @@ Node ≥ 18 required (built-in `fetch`).
|
|
|
174
174
|
| `OID4VP_BASE_URL` | no | OID4VP verifier base URL override — auto-derived from `ZETRIX_NETWORK` by the x401 SDK when not set |
|
|
175
175
|
| `ZETRIX_NODE_HOST` / `ZETRIX_NODE_PORT` | no | RPC node override (auto-derived from network) |
|
|
176
176
|
| `ZID_RESOLVER_BASE_URL` | no | ZID resolver override (auto-derived from network: sandbox for testnet, prod for mainnet) |
|
|
177
|
-
| `MAX_PAYMENT_AMOUNT` | no** | Per-asset x402 auto-pay cap — JSON `{ "<asset>": "<maxRawUnits>", "*": "<fallback>" }`. `pay_and_fetch`/`subscribe_and_issue` are asset-agnostic: the resource server's 402 challenge may quote the native ZETRIX token (asset code `ZTX`) **or** a ZTP20 token (e.g. `JMYR`) — cap whichever assets you expect, e.g. `{"ZTX":"1000000000","JMYR":"5000000","*":"0"}`.
|
|
177
|
+
| `MAX_PAYMENT_AMOUNT` | no** | Per-asset x402 auto-pay cap — JSON `{ "<asset>": "<maxRawUnits>", "*": "<fallback>" }`. `pay_and_fetch`/`subscribe_and_issue` are asset-agnostic: the resource server's 402 challenge may quote the native ZETRIX token (asset code `ZTX`) **or** a ZTP20 token (e.g. `JMYR`) — cap whichever assets you expect, e.g. `{"ZTX":"1000000000","JMYR":"5000000","*":"0"}`. **Defaults to `{"*":"0"}` — every payment is refused until you set this.** |
|
|
178
|
+
| `ZETRIX_WALLET_STATE_DIR` | no | Where the wallet keeps `account.json` and its VC cache. Defaults to `~/.agentic-wallet-mcp` |
|
|
178
179
|
|
|
179
|
-
\* sensitive — never logged.
|
|
180
|
+
\* sensitive — never logged, never returned in a tool result, and never a tool parameter.
|
|
180
181
|
|
|
181
182
|
### Onboarding: two ways to set up your holder identity
|
|
182
183
|
|
|
183
184
|
`ZETRIX_ADDRESS` and `HOLDER_DID` are both optional — the MCP resolves your holder identity at
|
|
184
185
|
startup, in one of two ways:
|
|
185
186
|
|
|
186
|
-
1. **First-time user — only `HSM_PASSWORD` set.** The MCP creates a
|
|
187
|
-
Wallet BE (`POST /wallet/hsm/account/create`) and derives the DID from
|
|
188
|
-
key.
|
|
189
|
-
|
|
190
|
-
(`~/.agentic-wallet-mcp/account.json`, owner-only) —
|
|
191
|
-
config edit required. An explicit
|
|
192
|
-
config still overrides the saved account.
|
|
187
|
+
1. **First-time user — nothing set at all, or only `HSM_PASSWORD` set.** The MCP creates a
|
|
188
|
+
brand-new HSM account on Wallet BE (`POST /wallet/hsm/account/create`) and derives the DID from
|
|
189
|
+
the returned public key. If you did not set `HSM_PASSWORD`, it generates one for you first. It
|
|
190
|
+
logs the new `ZETRIX_ADDRESS` (and `HOLDER_DID`) to stderr on startup, and saves the address,
|
|
191
|
+
DID, and password to a local account store (`~/.agentic-wallet-mcp/account.json`, owner-only) —
|
|
192
|
+
it's reused automatically next run, no config edit required. An explicit
|
|
193
|
+
`ZETRIX_ADDRESS`/`HSM_PASSWORD` set later in your MCP config still overrides the saved account.
|
|
194
|
+
|
|
195
|
+
**If the wallet generated your password, back it up — see below.**
|
|
193
196
|
2. **Existing user — `ZETRIX_ADDRESS` + `HSM_PASSWORD` set, `HOLDER_DID` optional.** The MCP
|
|
194
197
|
always self-signs the address via the existing `POST /wallet/hsm/sign-message` call and
|
|
195
198
|
derives the DID from the `publicKey` the response carries — no separate lookup endpoint
|
|
@@ -201,13 +204,28 @@ startup, in one of two ways:
|
|
|
201
204
|
Either way, `wallet_status` always reports the resolved `zetrixAddress`/`holderDid` for the
|
|
202
205
|
running session, so you can confirm what the MCP resolved to at any time.
|
|
203
206
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
207
|
+
### Backing up a self-provisioned wallet
|
|
208
|
+
|
|
209
|
+
If you never set `HSM_PASSWORD`, the wallet generated one and stored it in
|
|
210
|
+
`~/.agentic-wallet-mcp/account.json`. You never have to type it — but it is the only thing that
|
|
211
|
+
can authorize signing for your account. Wallet BE holds the key and will not use it without this
|
|
212
|
+
password, so if you lose the file the account cannot be recovered and any funds in it are gone.
|
|
213
|
+
Back it up with:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
npx agentic-wallet-mcp export-credentials
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
This only runs in an interactive terminal, so it can't be piped into a file or a log, and it is
|
|
220
|
+
deliberately **not** available as an MCP tool — an AI agent can never read your password.
|
|
221
|
+
|
|
222
|
+
\*\* **You must set this before the wallet will pay for anything.** It defaults to `{"*":"0"}`,
|
|
223
|
+
which refuses every payment. `pay_and_fetch` and `subscribe_and_issue` auto-pay whatever
|
|
224
|
+
`maxAmountRequired` a remote server's 402 challenge demands, so without a ceiling a prompt-injected
|
|
225
|
+
or misled agent calling either tool against a hostile endpoint would pay whatever that endpoint
|
|
226
|
+
asks for, bounded only by the account balance. `MAX_PAYMENT_AMOUNT` is a hard, code-enforced cap
|
|
227
|
+
that holds regardless of what the calling agent decides. It is also an allowlist: an asset with no
|
|
228
|
+
entry and no `"*"` fallback is **denied**, not passed through uncapped.
|
|
211
229
|
|
|
212
230
|
You don't need to look up or fill in `WALLET_BE_URL`/`MBI_BASE_URL`/`OID4VP_BASE_URL`/
|
|
213
231
|
`ZID_RESOLVER_BASE_URL` yourself — just pick `zetrix:testnet` or `zetrix:mainnet` for
|
|
@@ -216,11 +234,11 @@ network:
|
|
|
216
234
|
|
|
217
235
|
| Network | `WALLET_BE_URL` default | `MBI_BASE_URL` default | `OID4VP_BASE_URL` default | `ZID_RESOLVER_BASE_URL` default |
|
|
218
236
|
|---|---|---|---|---|
|
|
219
|
-
| `zetrix:testnet` | `https://wallet-api.
|
|
237
|
+
| `zetrix:testnet` | `https://wallet-api-sandbox.zetrix.com/server` | `https://mbi-vc-sandbox.zetrix.com` | `https://zid-oid4vp-sandbox.zetrix.com/api` | `https://zid-resolver-sandbox.zetrix.com` |
|
|
220
238
|
| `zetrix:mainnet` | `https://wallet-api.zetrix.com/server` | `https://mbi-vc.zetrix.com` | `https://zid-oid4vp.zetrix.com/api` | `https://zid-resolver.zetrix.com` |
|
|
221
239
|
|
|
222
240
|
Only set any of the four explicitly if you run your own instance of that service instead of the
|
|
223
|
-
default one — an explicit value always wins over the network default. The
|
|
241
|
+
default one — an explicit value always wins over the network default. The `*-sandbox.zetrix.com`
|
|
224
242
|
(testnet) hosts are public endpoints — no VPN needed; if you're on a corporate VPN and one of
|
|
225
243
|
them times out, disconnecting it is more likely to fix that than connecting it. See "Network
|
|
226
244
|
reachability & troubleshooting" below.
|
|
@@ -323,17 +341,25 @@ Independent of Phases 2/3 — no VC or identity proof involved, just a fresh pay
|
|
|
323
341
|
|
|
324
342
|
## Network reachability & troubleshooting
|
|
325
343
|
|
|
326
|
-
The
|
|
327
|
-
|
|
328
|
-
|
|
344
|
+
The `*-sandbox.zetrix.com` testnet endpoints are public — no VPN needed to reach them. Reachability
|
|
345
|
+
differs per host, and this is the first thing to check when something that worked before suddenly
|
|
346
|
+
times out or 403s, before assuming a code regression.
|
|
347
|
+
|
|
348
|
+
- **`wallet-api-sandbox.zetrix.com` / `mbi-vc-sandbox.zetrix.com`** — direct origin IPs
|
|
349
|
+
(`124.243.148.237` / `111.119.237.222` as of 2026-07-29), **not** CDN-fronted, unlike the ZID
|
|
350
|
+
hosts and the mainnet endpoints. Being off-CDN is why the **corporate VPN blocks them
|
|
351
|
+
specifically** while `test-node.zetrix.com` and the mainnet hosts keep working over the same
|
|
352
|
+
VPN — so a failure here looks like a total outage while everything else looks healthy.
|
|
353
|
+
Disconnect the VPN. Verified reachable over a plain internet path on 2026-07-29.
|
|
354
|
+
- **ZID resolver** sits behind a CDN edge with a managed challenge — see its row below.
|
|
329
355
|
|
|
330
356
|
| Symptom | Cause | Fix / status |
|
|
331
357
|
|---|---|---|
|
|
332
|
-
| `Wallet BE /wallet/hsm/sign-blob request failed <- fetch failed <- UND_ERR_CONNECT_TIMEOUT` (or same for `mbi-vc.
|
|
358
|
+
| `Wallet BE /wallet/hsm/sign-blob request failed <- fetch failed <- UND_ERR_CONNECT_TIMEOUT` (or same for `mbi-vc-sandbox.zetrix.com`) | Corporate VPN routing away from the public internet — it blocks these two direct-origin hosts specifically (observed 2026-07-29), or a transient network issue | **Disconnect the VPN and retry.** Verify reachability directly: `curl -sS -o /dev/null -w '%{http_code}\n' "https://wallet-api-sandbox.zetrix.com/server/wallet/hsm/account/activate/status?address=<yourAddress>"` — `200` (with an `errorCode: 0` body) is healthy; `000` means the TCP connect never completed, so it's a network path problem, not a code problem |
|
|
333
359
|
| `VP derivation failed <- ZID resolver HTTP 403 ... cf-mitigated: challenge` | ZID resolver (`zid-resolver-sandbox.zetrix.com`) sitting behind a Cloudflare **managed challenge** that blocks plain server-to-server `fetch` | Server-to-server access to the resolver must be allowlisted so it returns `200` directly. If the challenge is active, `prove_identity`'s `issuerKeys` input is the fallback — fetch the DID document via a real browser (it clears the JS challenge) and pass its `verificationMethod` entries' `publicKeyMultibase` (BBS+) / `publicKeyHex` (Ed25519) directly. |
|
|
334
360
|
| `OID4VP backend returned a malformed presentation definition` | Historical SDK bug: the live sandbox's `GET /v1/presentation/{id}` response has no `expires_at` field, but the SDK guard required one | **Fixed** in `x401-zetrix-client` — `expiresAt` is now optional on `PresentationDefinition`. If you see this, you're on a stale cached `npx` install — clear it (`npx clear-npx-cache` or bump the version) to pick up the current published `agentic-wallet-mcp`. |
|
|
335
361
|
| `SUBMIT_FAILED: OID4VP backend returned 401 ... Missing X-Wallet-Public-Key header` | Historical SDK gap: `POST /v1/presentation/submit` requires wallet-auth headers (`X-Wallet-Public-Key` / `X-Wallet-Signed-Data`, holder signs their own address) that the SDK didn't send | **Fixed** — `X401Wallet` now accepts an injected `submitAuth` provider and the wallet wires it automatically; nothing to configure. |
|
|
336
|
-
| `MBI /vp/ext/submit did not return vp` | MBI's `includeVp` opt-in not deployed on the target instance | Deployed on the sandbox (`mbi-vc.
|
|
362
|
+
| `MBI /vp/ext/submit did not return vp` | MBI's `includeVp` opt-in not deployed on the target instance | Deployed on the sandbox (`mbi-vc-sandbox.zetrix.com`). If you see this against a different MBI instance, that instance needs the same rollout. |
|
|
337
363
|
|
|
338
364
|
## Security notes
|
|
339
365
|
|
package/dist/server-bundle.cjs
CHANGED
|
@@ -12540,14 +12540,15 @@ __export(index_exports, {
|
|
|
12540
12540
|
buildToolList: () => buildToolList
|
|
12541
12541
|
});
|
|
12542
12542
|
module.exports = __toCommonJS(index_exports);
|
|
12543
|
-
var
|
|
12544
|
-
var
|
|
12545
|
-
var
|
|
12543
|
+
var import_node_crypto3 = require("node:crypto");
|
|
12544
|
+
var import_node_fs = require("node:fs");
|
|
12545
|
+
var import_node_os2 = require("node:os");
|
|
12546
|
+
var import_node_path4 = require("node:path");
|
|
12546
12547
|
|
|
12547
12548
|
// package.json
|
|
12548
12549
|
var package_default = {
|
|
12549
12550
|
name: "agentic-wallet-mcp",
|
|
12550
|
-
version: "0.
|
|
12551
|
+
version: "0.7.0",
|
|
12551
12552
|
description: "Agent-facing MCP wallet for Zetrix \u2014 orchestrates x401 identity proof, x402 payment, and MBI VC issuance",
|
|
12552
12553
|
keywords: [
|
|
12553
12554
|
"mcp",
|
|
@@ -21489,6 +21490,10 @@ var X401Wallet = class {
|
|
|
21489
21490
|
// src/index.ts
|
|
21490
21491
|
var import_zetrix_sdk_nodejs = __toESM(require("zetrix-sdk-nodejs"), 1);
|
|
21491
21492
|
|
|
21493
|
+
// src/config.ts
|
|
21494
|
+
var import_node_os = require("node:os");
|
|
21495
|
+
var import_node_path = require("node:path");
|
|
21496
|
+
|
|
21492
21497
|
// src/payment-guard.ts
|
|
21493
21498
|
var PaymentCapError = class extends Error {
|
|
21494
21499
|
constructor(message) {
|
|
@@ -21548,10 +21553,10 @@ function deriveZidResolverBaseUrl(network) {
|
|
|
21548
21553
|
return network.includes("testnet") ? "https://zid-resolver-sandbox.zetrix.com" : "https://zid-resolver.zetrix.com";
|
|
21549
21554
|
}
|
|
21550
21555
|
function deriveWalletBeUrl(network) {
|
|
21551
|
-
return network.includes("testnet") ? "https://wallet-api.
|
|
21556
|
+
return network.includes("testnet") ? "https://wallet-api-sandbox.zetrix.com/server" : "https://wallet-api.zetrix.com/server";
|
|
21552
21557
|
}
|
|
21553
21558
|
function deriveMbiBaseUrl(network) {
|
|
21554
|
-
return network.includes("testnet") ? "https://mbi-vc.
|
|
21559
|
+
return network.includes("testnet") ? "https://mbi-vc-sandbox.zetrix.com" : "https://mbi-vc.zetrix.com";
|
|
21555
21560
|
}
|
|
21556
21561
|
function deriveTemplateRegistryAddress(network) {
|
|
21557
21562
|
return network.includes("testnet") ? "ZTX3JszqPgRUx743SAp7q7zURfjvkWuH2FMEz" : "ZTX3GqJM1U6ifMPonwD4fGvrgoTKJua7b2cKX";
|
|
@@ -21574,24 +21579,28 @@ function loadConfig(env) {
|
|
|
21574
21579
|
const v = env[key];
|
|
21575
21580
|
return v && v.trim() ? v.trim() : void 0;
|
|
21576
21581
|
};
|
|
21577
|
-
const network =
|
|
21582
|
+
const network = opt("ZETRIX_NETWORK") ?? "zetrix:testnet";
|
|
21578
21583
|
const oid4vpBaseUrlOverride = opt("OID4VP_BASE_URL");
|
|
21579
21584
|
return {
|
|
21580
21585
|
walletBeUrl: stripTrailingSlash(opt("WALLET_BE_URL") ?? deriveWalletBeUrl(network)),
|
|
21581
21586
|
oid4vpBaseUrl: oid4vpBaseUrlOverride ? stripTrailingSlash(oid4vpBaseUrlOverride) : void 0,
|
|
21582
21587
|
mbiBaseUrl: stripTrailingSlash(opt("MBI_BASE_URL") ?? deriveMbiBaseUrl(network)),
|
|
21583
21588
|
network,
|
|
21589
|
+
stateDir: stripTrailingSlash(opt("ZETRIX_WALLET_STATE_DIR") ?? (0, import_node_path.join)((0, import_node_os.homedir)(), ".agentic-wallet-mcp")),
|
|
21584
21590
|
zetrixAddress: opt("ZETRIX_ADDRESS"),
|
|
21585
21591
|
holderDid: opt("HOLDER_DID"),
|
|
21586
21592
|
hsmPassword: req(
|
|
21587
21593
|
"HSM_PASSWORD",
|
|
21588
|
-
"
|
|
21594
|
+
"the server is normally started through main(), which generates a password when none exists \u2014 reaching this error means loadConfig was called directly without one"
|
|
21589
21595
|
),
|
|
21590
21596
|
nodeHost: opt("ZETRIX_NODE_HOST") ?? deriveNodeHost(network),
|
|
21591
21597
|
nodePort: opt("ZETRIX_NODE_PORT") ?? "",
|
|
21592
21598
|
templateRegistryAddress: opt("ZETRIX_TEMPLATE_REGISTRY_ADDRESS") ?? deriveTemplateRegistryAddress(network),
|
|
21593
21599
|
zidResolverBaseUrl: stripTrailingSlash(opt("ZID_RESOLVER_BASE_URL") ?? deriveZidResolverBaseUrl(network)),
|
|
21594
|
-
|
|
21600
|
+
// Fail closed: an unset cap means "spend nothing", not "spend anything". A wallet that
|
|
21601
|
+
// starts with no configuration at all (plugin install, see docs/openclaw) must not be
|
|
21602
|
+
// able to auto-pay a hostile x402 challenge. Raising it is a deliberate act.
|
|
21603
|
+
maxPaymentAmount: parsePaymentCaps(opt("MAX_PAYMENT_AMOUNT")) ?? { "*": "0" }
|
|
21595
21604
|
};
|
|
21596
21605
|
}
|
|
21597
21606
|
|
|
@@ -21654,6 +21663,15 @@ async function queryContract(input, query) {
|
|
|
21654
21663
|
|
|
21655
21664
|
// src/clients/token-balance-client.ts
|
|
21656
21665
|
var ZTX_DECIMALS = 6;
|
|
21666
|
+
function parseNativeBalance(res) {
|
|
21667
|
+
if (res?.errorCode !== 0) throw new Error(`getInfo failed with errorCode ${res?.errorCode}`);
|
|
21668
|
+
if (res.result === void 0 || res.result === null) throw new Error("getInfo returned no result");
|
|
21669
|
+
const balance = res.result.balance;
|
|
21670
|
+
if (typeof balance === "string") return balance;
|
|
21671
|
+
if (typeof balance === "number" && Number.isFinite(balance)) return String(balance);
|
|
21672
|
+
if (balance === void 0 || balance === null) return "0";
|
|
21673
|
+
throw new Error(`getInfo returned an unusable balance of type ${typeof balance}`);
|
|
21674
|
+
}
|
|
21657
21675
|
async function fetchZTP20BalanceStrict(contractAddress, address, query) {
|
|
21658
21676
|
const result = await query({
|
|
21659
21677
|
contractAddress,
|
|
@@ -22109,7 +22127,7 @@ function zetrixHexStringToBytes(s) {
|
|
|
22109
22127
|
// src/clients/vc-cache.ts
|
|
22110
22128
|
var import_node_crypto = require("node:crypto");
|
|
22111
22129
|
var import_promises = require("node:fs/promises");
|
|
22112
|
-
var
|
|
22130
|
+
var import_node_path2 = require("node:path");
|
|
22113
22131
|
function cacheFileName(templateId) {
|
|
22114
22132
|
return `${(0, import_node_crypto.createHash)("sha256").update(templateId).digest("hex")}.json`;
|
|
22115
22133
|
}
|
|
@@ -22120,7 +22138,7 @@ function createFsVcCache(baseDir) {
|
|
|
22120
22138
|
return {
|
|
22121
22139
|
async get(templateId) {
|
|
22122
22140
|
try {
|
|
22123
|
-
const raw = await (0, import_promises.readFile)((0,
|
|
22141
|
+
const raw = await (0, import_promises.readFile)((0, import_node_path2.join)(baseDir, cacheFileName(templateId)), "utf8");
|
|
22124
22142
|
const parsed = JSON.parse(raw);
|
|
22125
22143
|
return isCachedVcShape(parsed) ? parsed : null;
|
|
22126
22144
|
} catch {
|
|
@@ -22129,7 +22147,7 @@ function createFsVcCache(baseDir) {
|
|
|
22129
22147
|
},
|
|
22130
22148
|
async set(templateId, entry) {
|
|
22131
22149
|
await (0, import_promises.mkdir)(baseDir, { recursive: true, mode: 448 });
|
|
22132
|
-
await (0, import_promises.writeFile)((0,
|
|
22150
|
+
await (0, import_promises.writeFile)((0, import_node_path2.join)(baseDir, cacheFileName(templateId)), JSON.stringify(entry), { encoding: "utf8", mode: 384 });
|
|
22133
22151
|
},
|
|
22134
22152
|
async list() {
|
|
22135
22153
|
let files;
|
|
@@ -22141,7 +22159,7 @@ function createFsVcCache(baseDir) {
|
|
|
22141
22159
|
const entries = await Promise.all(
|
|
22142
22160
|
files.filter((f) => f.endsWith(".json")).map(async (f) => {
|
|
22143
22161
|
try {
|
|
22144
|
-
const parsed = JSON.parse(await (0, import_promises.readFile)((0,
|
|
22162
|
+
const parsed = JSON.parse(await (0, import_promises.readFile)((0, import_node_path2.join)(baseDir, f), "utf8"));
|
|
22145
22163
|
return isCachedVcShape(parsed) ? parsed : null;
|
|
22146
22164
|
} catch {
|
|
22147
22165
|
return null;
|
|
@@ -22189,8 +22207,14 @@ var PaymentReadinessError = class extends Error {
|
|
|
22189
22207
|
this.shortfall = shortfall;
|
|
22190
22208
|
}
|
|
22191
22209
|
};
|
|
22192
|
-
function toPaymentReadinessError(err, requestedAsset) {
|
|
22210
|
+
function toPaymentReadinessError(err, requestedAsset, activated) {
|
|
22193
22211
|
if (!(err instanceof import_x402_zetrix_client.InsufficientBalanceError)) return null;
|
|
22212
|
+
if (activated === false) {
|
|
22213
|
+
return new PaymentReadinessError(
|
|
22214
|
+
`payment blocked: this wallet's address has not been activated on chain yet \u2014 send ZTX to it from a funded account, then retry`,
|
|
22215
|
+
{ asset: err.asset, required: err.required, available: err.available, reason: "not_activated" }
|
|
22216
|
+
);
|
|
22217
|
+
}
|
|
22194
22218
|
const reason = err.asset === "ZTX" && requestedAsset !== "ZTX" ? "gas" : "resource_payment";
|
|
22195
22219
|
return new PaymentReadinessError(err.message, {
|
|
22196
22220
|
asset: err.asset,
|
|
@@ -22199,11 +22223,11 @@ function toPaymentReadinessError(err, requestedAsset) {
|
|
|
22199
22223
|
reason
|
|
22200
22224
|
});
|
|
22201
22225
|
}
|
|
22202
|
-
async function payWithReadinessCheck(requestedAsset, rawPay) {
|
|
22226
|
+
async function payWithReadinessCheck(requestedAsset, rawPay, activated) {
|
|
22203
22227
|
try {
|
|
22204
22228
|
return await rawPay();
|
|
22205
22229
|
} catch (err) {
|
|
22206
|
-
const readinessError = toPaymentReadinessError(err, requestedAsset);
|
|
22230
|
+
const readinessError = toPaymentReadinessError(err, requestedAsset, activated);
|
|
22207
22231
|
if (readinessError) throw readinessError;
|
|
22208
22232
|
throw err;
|
|
22209
22233
|
}
|
|
@@ -22412,9 +22436,9 @@ async function createHolderAccount(deps, input) {
|
|
|
22412
22436
|
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.`
|
|
22413
22437
|
};
|
|
22414
22438
|
}
|
|
22415
|
-
const { zetrixAddress, publicKeyHex, activated } = await deps.create(input.
|
|
22439
|
+
const { zetrixAddress, publicKeyHex, activated } = await deps.create(input.label, input.purpose);
|
|
22416
22440
|
const holderDid = deriveHolderDid(publicKeyHex);
|
|
22417
|
-
await deps.saveAccount({ zetrixAddress, holderDid,
|
|
22441
|
+
await deps.saveAccount({ zetrixAddress, holderDid, label: input.label, purpose: input.purpose });
|
|
22418
22442
|
const finalActivated = activated || await waitForActivation(deps.checkActivationStatus, zetrixAddress, deps.sleep);
|
|
22419
22443
|
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
22444
|
return { created: true, alreadyExists: Boolean(existing), zetrixAddress, holderDid, publicKeyHex, activated: finalActivated, message };
|
|
@@ -22922,9 +22946,123 @@ async function resolveHolder(deps, input) {
|
|
|
22922
22946
|
return { zetrixAddress: input.zetrixAddress, holderDid: derivedDid, created: false, didMismatch };
|
|
22923
22947
|
}
|
|
22924
22948
|
|
|
22949
|
+
// src/startup-env.ts
|
|
22950
|
+
function resolveStartupEnv(input) {
|
|
22951
|
+
const env = { ...input.fileEnv, ...input.processEnv };
|
|
22952
|
+
const stored = input.storedAccount;
|
|
22953
|
+
if (!env.ZETRIX_ADDRESS && stored) env.ZETRIX_ADDRESS = stored.zetrixAddress;
|
|
22954
|
+
if (!env.HOLDER_DID && stored) env.HOLDER_DID = stored.holderDid;
|
|
22955
|
+
if (!env.HSM_PASSWORD && stored) env.HSM_PASSWORD = stored.hsmPassword;
|
|
22956
|
+
let passwordGenerated = false;
|
|
22957
|
+
if (!env.HSM_PASSWORD) {
|
|
22958
|
+
env.HSM_PASSWORD = input.generatePassword();
|
|
22959
|
+
passwordGenerated = true;
|
|
22960
|
+
}
|
|
22961
|
+
return { env, passwordGenerated };
|
|
22962
|
+
}
|
|
22963
|
+
|
|
22964
|
+
// src/config-file.ts
|
|
22965
|
+
var KEY_TO_ENV = {
|
|
22966
|
+
network: "ZETRIX_NETWORK",
|
|
22967
|
+
zetrixAddress: "ZETRIX_ADDRESS",
|
|
22968
|
+
holderDid: "HOLDER_DID",
|
|
22969
|
+
maxPaymentAmount: "MAX_PAYMENT_AMOUNT",
|
|
22970
|
+
stateDir: "ZETRIX_WALLET_STATE_DIR",
|
|
22971
|
+
walletBeUrl: "WALLET_BE_URL",
|
|
22972
|
+
mbiBaseUrl: "MBI_BASE_URL"
|
|
22973
|
+
};
|
|
22974
|
+
var FORBIDDEN_KEYS = ["hsmPassword", "password", "HSM_PASSWORD"];
|
|
22975
|
+
function configPathFrom(argv) {
|
|
22976
|
+
const inline = argv.find((a) => a.startsWith("--config="));
|
|
22977
|
+
if (inline) {
|
|
22978
|
+
const path2 = inline.slice("--config=".length);
|
|
22979
|
+
if (!path2) throw new Error("agentic-wallet-mcp: --config requires a file path");
|
|
22980
|
+
return path2;
|
|
22981
|
+
}
|
|
22982
|
+
const i = argv.indexOf("--config");
|
|
22983
|
+
if (i === -1) return null;
|
|
22984
|
+
const path = argv[i + 1];
|
|
22985
|
+
if (!path || path.startsWith("--")) throw new Error("agentic-wallet-mcp: --config requires a file path");
|
|
22986
|
+
return path;
|
|
22987
|
+
}
|
|
22988
|
+
function loadConfigFileEnv(argv, readFile3) {
|
|
22989
|
+
const path = configPathFrom(argv);
|
|
22990
|
+
if (path === null) return {};
|
|
22991
|
+
let raw;
|
|
22992
|
+
try {
|
|
22993
|
+
raw = readFile3(path);
|
|
22994
|
+
} catch (e) {
|
|
22995
|
+
throw new Error(`agentic-wallet-mcp: cannot read config file ${path}: ${e.message}`);
|
|
22996
|
+
}
|
|
22997
|
+
let parsed;
|
|
22998
|
+
try {
|
|
22999
|
+
parsed = JSON.parse(raw);
|
|
23000
|
+
} catch (e) {
|
|
23001
|
+
throw new Error(`agentic-wallet-mcp: config file ${path} is not valid JSON: ${e.message}`);
|
|
23002
|
+
}
|
|
23003
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
23004
|
+
throw new Error(`agentic-wallet-mcp: config file ${path} must contain a JSON object`);
|
|
23005
|
+
}
|
|
23006
|
+
const env = {};
|
|
23007
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
23008
|
+
if (FORBIDDEN_KEYS.includes(key)) {
|
|
23009
|
+
throw new Error(
|
|
23010
|
+
`agentic-wallet-mcp: config file ${path} must not contain a secret ("${key}") \u2014 the wallet generates and stores its own HSM password; see docs/openclaw/DEPLOYMENT_DESIGN.md`
|
|
23011
|
+
);
|
|
23012
|
+
}
|
|
23013
|
+
const envKey = KEY_TO_ENV[key];
|
|
23014
|
+
if (!envKey) {
|
|
23015
|
+
throw new Error(
|
|
23016
|
+
`agentic-wallet-mcp: unknown config key "${key}" in ${path} \u2014 allowed: ${Object.keys(KEY_TO_ENV).join(", ")}`
|
|
23017
|
+
);
|
|
23018
|
+
}
|
|
23019
|
+
env[envKey] = typeof value === "string" ? value : JSON.stringify(value);
|
|
23020
|
+
}
|
|
23021
|
+
return env;
|
|
23022
|
+
}
|
|
23023
|
+
|
|
23024
|
+
// src/hsm-password.ts
|
|
23025
|
+
var import_node_crypto2 = require("node:crypto");
|
|
23026
|
+
function generateHsmPassword() {
|
|
23027
|
+
return (0, import_node_crypto2.randomBytes)(24).toString("base64url");
|
|
23028
|
+
}
|
|
23029
|
+
|
|
23030
|
+
// src/export-credentials.ts
|
|
23031
|
+
async function exportCredentials(deps) {
|
|
23032
|
+
if (!deps.isTty) {
|
|
23033
|
+
deps.writeErr(
|
|
23034
|
+
"agentic-wallet-mcp: export-credentials only runs in an interactive terminal, so the password cannot be piped into a file or a log. Run it directly in your shell.\n"
|
|
23035
|
+
);
|
|
23036
|
+
return { exitCode: 1 };
|
|
23037
|
+
}
|
|
23038
|
+
const account = await deps.getAccount();
|
|
23039
|
+
if (!account) {
|
|
23040
|
+
deps.writeErr(
|
|
23041
|
+
"agentic-wallet-mcp: no account has been created yet \u2014 start the wallet once to provision one, then run this again.\n"
|
|
23042
|
+
);
|
|
23043
|
+
return { exitCode: 1 };
|
|
23044
|
+
}
|
|
23045
|
+
deps.write(
|
|
23046
|
+
`
|
|
23047
|
+
Zetrix Agentic Wallet \u2014 credentials for backup
|
|
23048
|
+
|
|
23049
|
+
Address: ${account.zetrixAddress}
|
|
23050
|
+
Holder DID: ${account.holderDid}
|
|
23051
|
+
HSM password: ${account.hsmPassword}
|
|
23052
|
+
|
|
23053
|
+
Store these somewhere safe and private, such as a password manager.
|
|
23054
|
+
The HSM password is the only thing that can authorize signing for this wallet. If you lose it
|
|
23055
|
+
and lose this machine's wallet state, the account cannot be recovered and any funds in it are
|
|
23056
|
+
permanently inaccessible.
|
|
23057
|
+
|
|
23058
|
+
`
|
|
23059
|
+
);
|
|
23060
|
+
return { exitCode: 0 };
|
|
23061
|
+
}
|
|
23062
|
+
|
|
22925
23063
|
// src/clients/account-store.ts
|
|
22926
23064
|
var import_promises2 = require("node:fs/promises");
|
|
22927
|
-
var
|
|
23065
|
+
var import_node_path3 = require("node:path");
|
|
22928
23066
|
function isStoredAccountShape(value) {
|
|
22929
23067
|
return typeof value === "object" && value !== null && typeof value.zetrixAddress === "string" && typeof value.holderDid === "string" && typeof value.hsmPassword === "string";
|
|
22930
23068
|
}
|
|
@@ -22940,7 +23078,7 @@ function createFsAccountStore(filePath) {
|
|
|
22940
23078
|
}
|
|
22941
23079
|
},
|
|
22942
23080
|
async set(account) {
|
|
22943
|
-
await (0, import_promises2.mkdir)((0,
|
|
23081
|
+
await (0, import_promises2.mkdir)((0, import_node_path3.dirname)(filePath), { recursive: true, mode: 448 });
|
|
22944
23082
|
await (0, import_promises2.writeFile)(filePath, JSON.stringify(account, null, 2), { encoding: "utf8", mode: 384 });
|
|
22945
23083
|
}
|
|
22946
23084
|
};
|
|
@@ -23052,19 +23190,17 @@ function buildToolList() {
|
|
|
23052
23190
|
},
|
|
23053
23191
|
{
|
|
23054
23192
|
name: "create_holder_account",
|
|
23055
|
-
description: "Create a new holder HSM account on Wallet BE (onboarding).
|
|
23193
|
+
description: "Create a new holder HSM account on Wallet BE (onboarding). 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. The wallet manages its own credentials; you neither need nor can supply any. 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.",
|
|
23056
23194
|
inputSchema: {
|
|
23057
23195
|
type: "object",
|
|
23058
23196
|
properties: {
|
|
23059
|
-
password: { type: "string", description: "HSM password to protect the new account. Must come from the user." },
|
|
23060
23197
|
label: { type: "string" },
|
|
23061
23198
|
purpose: { type: "string" },
|
|
23062
23199
|
confirmNew: {
|
|
23063
23200
|
type: "boolean",
|
|
23064
23201
|
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."
|
|
23065
23202
|
}
|
|
23066
|
-
}
|
|
23067
|
-
required: ["password"]
|
|
23203
|
+
}
|
|
23068
23204
|
}
|
|
23069
23205
|
}
|
|
23070
23206
|
];
|
|
@@ -23074,12 +23210,25 @@ function asPayRequest(accept) {
|
|
|
23074
23210
|
return { ...accept, extra: { gasModel: "client", ...extra } };
|
|
23075
23211
|
}
|
|
23076
23212
|
async function main() {
|
|
23077
|
-
const
|
|
23213
|
+
const fileEnv = loadConfigFileEnv(process.argv, (p) => (0, import_node_fs.readFileSync)(p, "utf8"));
|
|
23214
|
+
const stateDir = (process.env.ZETRIX_WALLET_STATE_DIR ?? fileEnv.ZETRIX_WALLET_STATE_DIR ?? (0, import_node_path4.join)((0, import_node_os2.homedir)(), ".agentic-wallet-mcp")).replace(/\/+$/, "");
|
|
23215
|
+
const accountStore = createFsAccountStore((0, import_node_path4.join)(stateDir, "account.json"));
|
|
23216
|
+
if (process.argv[2] === "export-credentials") {
|
|
23217
|
+
const { exitCode } = await exportCredentials({
|
|
23218
|
+
getAccount: () => accountStore.get(),
|
|
23219
|
+
isTty: Boolean(process.stdout.isTTY),
|
|
23220
|
+
write: (s) => process.stdout.write(s),
|
|
23221
|
+
writeErr: (s) => process.stderr.write(s)
|
|
23222
|
+
});
|
|
23223
|
+
process.exit(exitCode);
|
|
23224
|
+
}
|
|
23078
23225
|
const storedAccount = process.env.ZETRIX_ADDRESS ? null : await accountStore.get();
|
|
23079
|
-
const env = {
|
|
23080
|
-
|
|
23081
|
-
|
|
23082
|
-
|
|
23226
|
+
const { env, passwordGenerated } = resolveStartupEnv({
|
|
23227
|
+
processEnv: process.env,
|
|
23228
|
+
storedAccount,
|
|
23229
|
+
fileEnv,
|
|
23230
|
+
generatePassword: generateHsmPassword
|
|
23231
|
+
});
|
|
23083
23232
|
const config2 = loadConfig(env);
|
|
23084
23233
|
const hsmPassword = config2.hsmPassword;
|
|
23085
23234
|
const be = new WalletBeClient(config2.walletBeUrl);
|
|
@@ -23096,8 +23245,9 @@ async function main() {
|
|
|
23096
23245
|
if (created) {
|
|
23097
23246
|
await accountStore.set({ zetrixAddress, holderDid, hsmPassword, createdAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
23098
23247
|
process.stderr.write(
|
|
23099
|
-
`agentic-wallet-mcp: no ZETRIX_ADDRESS was set \u2014 created a new HSM account and saved it
|
|
23100
|
-
`
|
|
23248
|
+
`agentic-wallet-mcp: no ZETRIX_ADDRESS was set \u2014 created a new HSM account and saved it 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).` + (passwordGenerated ? ` An HSM password was generated for this account \u2014 you never need to enter it, but it is the ONLY thing that can authorize signing for this wallet. If this file is lost the account cannot be recovered. Back it up with: npx agentic-wallet-mcp export-credentials
|
|
23249
|
+
` : `
|
|
23250
|
+
`)
|
|
23101
23251
|
);
|
|
23102
23252
|
} else if (storedAccount && config2.zetrixAddress === storedAccount.zetrixAddress) {
|
|
23103
23253
|
process.stderr.write(
|
|
@@ -23124,13 +23274,7 @@ async function main() {
|
|
|
23124
23274
|
const sdk = new import_zetrix_sdk_nodejs.default({ host: config2.nodeHost, port: config2.nodePort });
|
|
23125
23275
|
const contractQuery = (a) => sdk.contract.call(a);
|
|
23126
23276
|
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
|
-
};
|
|
23277
|
+
const fetchNativeBalance = async (address) => parseNativeBalance(await sdk.account.getInfo(address));
|
|
23134
23278
|
const tokenBalanceDeps = {
|
|
23135
23279
|
address: zetrixAddress,
|
|
23136
23280
|
fetchNativeBalance,
|
|
@@ -23141,13 +23285,14 @@ async function main() {
|
|
|
23141
23285
|
const nodeBaseUrl = `https://${config2.nodeHost}${config2.nodePort ? `:${config2.nodePort}` : ""}`;
|
|
23142
23286
|
const nodeMetaQuery = (url) => fetch(url, { headers: { Accept: "application/json" } }).then((r) => r.json());
|
|
23143
23287
|
const resolveTemplateFields = (templateId) => fetchTemplateFields(templateId, config2.templateRegistryAddress, nodeBaseUrl, nodeMetaQuery);
|
|
23144
|
-
const cacheScope = (0,
|
|
23145
|
-
const vcCache = createFsVcCache((0,
|
|
23288
|
+
const cacheScope = (0, import_node_crypto3.createHash)("sha256").update(`${config2.network}:${zetrixAddress}`).digest("hex");
|
|
23289
|
+
const vcCache = createFsVcCache((0, import_node_path4.join)(config2.stateDir, "vc-cache", cacheScope));
|
|
23146
23290
|
const pay = (accept) => {
|
|
23147
23291
|
assertWithinPaymentCap(accept, config2.maxPaymentAmount);
|
|
23148
23292
|
return payWithReadinessCheck(
|
|
23149
23293
|
String(accept.asset ?? ""),
|
|
23150
|
-
() => import_x402_zetrix_client2.PaymentEngine.pay(asPayRequest(accept), walletCfg, node, {}, walletBeSignerFn)
|
|
23294
|
+
() => import_x402_zetrix_client2.PaymentEngine.pay(asPayRequest(accept), walletCfg, node, {}, walletBeSignerFn),
|
|
23295
|
+
activated
|
|
23151
23296
|
);
|
|
23152
23297
|
};
|
|
23153
23298
|
const payer = async (req) => {
|
|
@@ -23199,8 +23344,10 @@ async function main() {
|
|
|
23199
23344
|
subscribeDeps: { mbi, sign: subscribeSign, pay, resolveSymbol, holderDid, resolveTemplateFields, cache: vcCache },
|
|
23200
23345
|
queryContract: (input) => queryContract(input, contractQuery),
|
|
23201
23346
|
queryTokenBalance: queryTokenBalance2,
|
|
23202
|
-
|
|
23203
|
-
|
|
23347
|
+
// The session password is bound here, in the wiring, so it never crosses into the tool
|
|
23348
|
+
// layer — create_holder_account has no password parameter for a model to be asked for.
|
|
23349
|
+
createAccount: (label, purpose) => be.createAccount(hsmPassword, label, purpose),
|
|
23350
|
+
saveAccount: (account) => accountStore.set({ ...account, hsmPassword, createdAt: (/* @__PURE__ */ new Date()).toISOString() }),
|
|
23204
23351
|
checkActivationStatus: (address) => be.checkActivationStatus(address),
|
|
23205
23352
|
sleep,
|
|
23206
23353
|
cache: vcCache
|
package/package.json
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "agentic-wallet-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Agent-facing MCP wallet for Zetrix — orchestrates x401 identity proof, x402 payment, and MBI VC issuance",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"mcp",
|
|
7
|
-
"model-context-protocol",
|
|
8
|
-
"zetrix",
|
|
9
|
-
"wallet",
|
|
10
|
-
"x401",
|
|
11
|
-
"x402",
|
|
12
|
-
"blockchain"
|
|
13
|
-
],
|
|
14
|
-
"license": "MIT",
|
|
15
|
-
"author": "MyEG Services Berhad",
|
|
16
|
-
"homepage": "https://github.com/Zetrix-Chain/zetrix-agentic-wallet",
|
|
17
|
-
"repository": {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "https://github.com/Zetrix-Chain/zetrix-agentic-wallet.git"
|
|
20
|
-
},
|
|
21
|
-
"bugs": {
|
|
22
|
-
"url": "https://github.com/Zetrix-Chain/zetrix-agentic-wallet/issues"
|
|
23
|
-
},
|
|
24
|
-
"type": "module",
|
|
25
|
-
"engines": {
|
|
26
|
-
"node": ">=18"
|
|
27
|
-
},
|
|
28
|
-
"bin": {
|
|
29
|
-
"agentic-wallet-mcp": "dist/server-bundle.cjs"
|
|
30
|
-
},
|
|
31
|
-
"files": [
|
|
32
|
-
"dist/server-bundle.cjs",
|
|
33
|
-
"README.md",
|
|
34
|
-
"CHANGELOG.md",
|
|
35
|
-
"LICENSE"
|
|
36
|
-
],
|
|
37
|
-
"scripts": {
|
|
38
|
-
"build": "tsc --noEmit && esbuild src/index.ts --bundle --platform=node --target=node18 --format=cjs --external:zetrix-sdk-nodejs --outfile=dist/server-bundle.cjs",
|
|
39
|
-
"test": "vitest run",
|
|
40
|
-
"test:watch": "vitest",
|
|
41
|
-
"typecheck": "tsc --noEmit"
|
|
42
|
-
},
|
|
43
|
-
"dependencies": {
|
|
44
|
-
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
45
|
-
"x401-zetrix-client": "^0.2.1",
|
|
46
|
-
"x402-zetrix-client": "^0.2.2",
|
|
47
|
-
"zetrix-sdk-nodejs": "^1.0.3"
|
|
48
|
-
},
|
|
49
|
-
"devDependencies": {
|
|
50
|
-
"@types/node": "^20.0.0",
|
|
51
|
-
"esbuild": "^0.24.0",
|
|
52
|
-
"typescript": "^5.9.0",
|
|
53
|
-
"vitest": "^1.6.0"
|
|
54
|
-
}
|
|
55
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "agentic-wallet-mcp",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Agent-facing MCP wallet for Zetrix — orchestrates x401 identity proof, x402 payment, and MBI VC issuance",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mcp",
|
|
7
|
+
"model-context-protocol",
|
|
8
|
+
"zetrix",
|
|
9
|
+
"wallet",
|
|
10
|
+
"x401",
|
|
11
|
+
"x402",
|
|
12
|
+
"blockchain"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "MyEG Services Berhad",
|
|
16
|
+
"homepage": "https://github.com/Zetrix-Chain/zetrix-agentic-wallet",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/Zetrix-Chain/zetrix-agentic-wallet.git"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/Zetrix-Chain/zetrix-agentic-wallet/issues"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18"
|
|
27
|
+
},
|
|
28
|
+
"bin": {
|
|
29
|
+
"agentic-wallet-mcp": "dist/server-bundle.cjs"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist/server-bundle.cjs",
|
|
33
|
+
"README.md",
|
|
34
|
+
"CHANGELOG.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc --noEmit && esbuild src/index.ts --bundle --platform=node --target=node18 --format=cjs --external:zetrix-sdk-nodejs --outfile=dist/server-bundle.cjs",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"test:watch": "vitest",
|
|
41
|
+
"typecheck": "tsc --noEmit"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
45
|
+
"x401-zetrix-client": "^0.2.1",
|
|
46
|
+
"x402-zetrix-client": "^0.2.2",
|
|
47
|
+
"zetrix-sdk-nodejs": "^1.0.3"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^20.0.0",
|
|
51
|
+
"esbuild": "^0.24.0",
|
|
52
|
+
"typescript": "^5.9.0",
|
|
53
|
+
"vitest": "^1.6.0"
|
|
54
|
+
}
|
|
55
|
+
}
|