@ironwallet/mcp-server 1.0.1 → 1.0.3
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 +17 -11
- package/dist/api/rates.d.ts +62 -0
- package/dist/api/rates.js +198 -0
- package/dist/api/rates.js.map +1 -0
- package/dist/api/swap.d.ts +1 -1
- package/dist/api/swap.js +1 -1
- package/dist/config.d.ts +16 -12
- package/dist/config.js +48 -31
- package/dist/config.js.map +1 -1
- package/dist/consent/copy.d.ts +18 -0
- package/dist/consent/copy.js +27 -0
- package/dist/consent/copy.js.map +1 -0
- package/dist/consent/store.d.ts +23 -0
- package/dist/consent/store.js +63 -0
- package/dist/consent/store.js.map +1 -0
- package/dist/device-fingerprint.d.ts +18 -0
- package/dist/device-fingerprint.js +81 -0
- package/dist/device-fingerprint.js.map +1 -0
- package/dist/generated/env-config.js +3 -1
- package/dist/generated/env-config.js.map +1 -1
- package/dist/generated/env-profile.d.ts +4 -0
- package/dist/keystore/store.js +3 -7
- package/dist/keystore/store.js.map +1 -1
- package/dist/keystore/types.d.ts +8 -3
- package/dist/local-secrets.d.ts +2 -2
- package/dist/local-secrets.js +5 -9
- package/dist/local-secrets.js.map +1 -1
- package/dist/networks.d.ts +5 -0
- package/dist/networks.js +21 -0
- package/dist/networks.js.map +1 -0
- package/dist/policy.d.ts +33 -3
- package/dist/policy.js +121 -32
- package/dist/policy.js.map +1 -1
- package/dist/restrict-private-file.d.ts +9 -0
- package/dist/restrict-private-file.js +53 -0
- package/dist/restrict-private-file.js.map +1 -0
- package/dist/server.js +3 -2
- package/dist/server.js.map +1 -1
- package/dist/tools/definitions.d.ts +40 -0
- package/dist/tools/definitions.js +256 -0
- package/dist/tools/definitions.js.map +1 -0
- package/dist/tools/helpers.d.ts +0 -3
- package/dist/tools/helpers.js +0 -3
- package/dist/tools/helpers.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/swap.d.ts +1 -1
- package/dist/tools/swap.js +12 -76
- package/dist/tools/swap.js.map +1 -1
- package/dist/tools/transfer.d.ts +1 -1
- package/dist/tools/transfer.js +13 -54
- package/dist/tools/transfer.js.map +1 -1
- package/dist/tools/wallets.d.ts +1 -1
- package/dist/tools/wallets.js +110 -47
- package/dist/tools/wallets.js.map +1 -1
- package/dist/wallet/derive.d.ts +1 -1
- package/dist/wallet/derive.js +1 -1
- package/dist/web/manager.js +64 -3
- package/dist/web/manager.js.map +1 -1
- package/package.json +30 -5
- package/server.json +23 -0
- package/tools.json +632 -0
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @ironwallet/mcp-server
|
|
2
2
|
|
|
3
|
-
Local [MCP](https://modelcontextprotocol.io) server for **
|
|
3
|
+
Local [MCP](https://modelcontextprotocol.io) server for **IronWallet** (`ironwallet-mcp` bin).
|
|
4
4
|
|
|
5
|
-
The
|
|
5
|
+
The IronWallet MCP server gives AI agents secure access to a **non-custodial** wallet. Seed phrases stay encrypted on the host and never leave this machine — they never pass through the agent. Agents can retrieve balances, sign locally, transfer tokens, and swap across 10+ networks.
|
|
6
6
|
|
|
7
|
-
Seed-compatible with the [
|
|
7
|
+
Seed-compatible with the [IronWallet](https://ironwallet.io) app.
|
|
8
8
|
|
|
9
9
|
There is no per-transaction confirmation UI.
|
|
10
10
|
|
|
@@ -47,7 +47,7 @@ The plugin install on [ironwallet.io/ai](https://ironwallet.io/ai) wires this up
|
|
|
47
47
|
MCP client → ironwallet-mcp (stdio)
|
|
48
48
|
├── encrypted keystore on disk
|
|
49
49
|
├── signs on this machine
|
|
50
|
-
└── HTTPS to
|
|
50
|
+
└── HTTPS to IronWallet backends
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
The mnemonic never appears in tool inputs/outputs, logs meant for the agent, or requests to backends / the LLM / the MCP client’s cloud.
|
|
@@ -61,19 +61,22 @@ The mnemonic never appears in tool inputs/outputs, logs meant for the agent, or
|
|
|
61
61
|
| **Networks** | Ethereum, BSC, Polygon, Base, Arbitrum, Optimism, Avalanche, Tron, Bitcoin, Litecoin, Dogecoin, Solana, XRP, TON |
|
|
62
62
|
| **Wallets** | Create, import, list, deposit QR, and back up (local browser for secrets) |
|
|
63
63
|
| **Balances** | Native coins and tokens |
|
|
64
|
-
| **Transfers** | Fee estimate and send through
|
|
65
|
-
| **Swaps** | Quotes and execution through
|
|
66
|
-
| **Policy** | Optional per-wallet limits (`readOnly`, `
|
|
64
|
+
| **Transfers** | Fee estimate and send through IronWallet’s transfer relay |
|
|
65
|
+
| **Swaps** | Quotes and execution through IronWallet |
|
|
66
|
+
| **Policy** | Optional per-wallet limits (`readOnly`, `maxPerTxUsd`, transfer recipient allow-list) set via `set_wallet_policy`. Applies to `send_transfer` and `execute_swap`. |
|
|
67
67
|
|
|
68
68
|
---
|
|
69
69
|
|
|
70
70
|
## Tools
|
|
71
71
|
|
|
72
|
+
<!-- tools-table -->
|
|
72
73
|
| Tool | Purpose | Moves funds? |
|
|
73
74
|
|------|---------|:------------:|
|
|
74
75
|
| `list_wallets` | Names, addresses, and `policy` | no |
|
|
76
|
+
| `accept_mcp_consent` | Record chat acceptance of the MCP disclaimer | no |
|
|
75
77
|
| `create_wallets` | New wallets; returns a browser `backup_url` | no |
|
|
76
78
|
| `open_wallet_manager` | Local browser UI to import / create / back up | no |
|
|
79
|
+
| `set_wallet_policy` | Replace per-wallet limits (`readOnly`, `maxPerTxUsd`, allow-list) | no |
|
|
77
80
|
| `get_deposit_qr` | PNG QR (try chat; else local `qr_url`) | no |
|
|
78
81
|
| `get_balance` | Native or token balance | no |
|
|
79
82
|
| `estimate_transfer` | Fee estimate, no broadcast | no |
|
|
@@ -84,6 +87,7 @@ The mnemonic never appears in tool inputs/outputs, logs meant for the agent, or
|
|
|
84
87
|
| `estimate_swap` | Quote (may expire) | no |
|
|
85
88
|
| `execute_swap` | Fresh quote → sign → swap | **yes** |
|
|
86
89
|
| `get_swap_status` | Poll a swap | no |
|
|
90
|
+
<!-- /tools-table -->
|
|
87
91
|
|
|
88
92
|
No tool accepts or returns a seed. Import and backup only in the local browser (`open_wallet_manager` / `backup_url`).
|
|
89
93
|
|
|
@@ -107,7 +111,7 @@ The browser page binds to `127.0.0.1` under an unguessable path and shuts down a
|
|
|
107
111
|
|
|
108
112
|
### Swaps
|
|
109
113
|
|
|
110
|
-
Swaps
|
|
114
|
+
Swaps are a separate flow from transfers.
|
|
111
115
|
|
|
112
116
|
1. `list_swap_networks`
|
|
113
117
|
2. `list_swap_assets` (`direction=from`, then `direction=to` with the chosen sell asset)
|
|
@@ -123,13 +127,13 @@ Swaps use **Swap Proxy**, not the transfer relay.
|
|
|
123
127
|
**Operational notes**
|
|
124
128
|
|
|
125
129
|
- Quotes expire. Prefer `execute_swap` (fresh quote). If execute times out, **poll status** before retrying — do not blindly re-run execute.
|
|
126
|
-
- Wallet policy: `readOnly` blocks sends and swaps. `
|
|
130
|
+
- Wallet policy: `readOnly` blocks sends and swaps. `maxPerTxUsd` values the transfer amount (or the corrected swap sell amount) in USD at operation time via IronWallet backend rates; when no rate is available the operation is rejected (fail closed). The recipient allow-list applies to `send_transfer`. `set_wallet_policy` replaces the whole policy — read `list_wallets.policy` first.
|
|
127
131
|
|
|
128
132
|
---
|
|
129
133
|
|
|
130
134
|
## Configuration
|
|
131
135
|
|
|
132
|
-
Nothing to paste into MCP config for normal use. On first launch the server writes a relay API key, keystore wrapping secret, and device id under `~/.ironwallet-mcp/` (`keystore-passphrase`, `relay-api-key`, `device-id
|
|
136
|
+
Nothing to paste into MCP config for normal use. On first launch the server writes a relay API key, keystore wrapping secret, and device id under `~/.ironwallet-mcp/` (`keystore-passphrase`, `relay-api-key`, `device-id`; owner-only POSIX `0600` or NTFS ACL on Windows). Set the env vars only to override.
|
|
133
137
|
|
|
134
138
|
The user-facing backup is the **recovery phrase** in the wallet manager, not those files.
|
|
135
139
|
|
|
@@ -140,6 +144,8 @@ The user-facing backup is the **recovery phrase** in the wallet manager, not tho
|
|
|
140
144
|
| `IW_RELAY_API_KEY` | generated UUID | Override `x-api-key` |
|
|
141
145
|
| `IW_DEVICE_ID` | generated UUID | Override `X-Device-Id` (stable per keystore directory) |
|
|
142
146
|
| `IW_KEYSTORE_DIR` | `~/.ironwallet-mcp` | Keystore directory |
|
|
147
|
+
| `IW_RATES_API_URL` | baked profile | Rates backend for `maxPerTxUsd` valuation. Unset in the profile → USD limits reject (fail closed) |
|
|
148
|
+
| `IW_STATIC_RESOURCES_URL` | baked profile | Static asset catalogs (token → rates id) |
|
|
143
149
|
| `IW_HTTP_TIMEOUT_MS` | `15000` | General HTTP timeout (1s–120s) |
|
|
144
150
|
| `IW_HTTP_FORWARD_TIMEOUT_MS` | `60000` | Longer timeout for broadcast-style calls. A client timeout does not always mean the operation failed — check status |
|
|
145
151
|
| `IW_HTTP_RETRIES` | `2` | Retries for safe/idempotent calls; broadcasts are not auto-retried |
|
|
@@ -153,7 +159,7 @@ The user-facing backup is the **recovery phrase** in the wallet manager, not tho
|
|
|
153
159
|
## Security
|
|
154
160
|
|
|
155
161
|
- Seeds are encrypted at rest. They never appear in tool results, agent chat, or backend requests.
|
|
156
|
-
- The agent **can move funds without asking again**. Optional wallet policy (`readOnly`, `
|
|
162
|
+
- The agent **can move funds without asking again**. Optional wallet policy (`readOnly`, `maxPerTxUsd`, transfer recipient allow-list — set via `set_wallet_policy`) is off by default and applies to both `send_transfer` and `execute_swap`.
|
|
157
163
|
- Anyone with the keystore **and** the wrapping secret controls the funds. A leaked seed cannot be revoked.
|
|
158
164
|
- Timeout is not always failure: poll status before retrying a send or swap.
|
|
159
165
|
- Desktop / stdio only. A phone or remote agent would need a design where signing stays on a trusted device.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* USD rates for policy limits (`maxPerTxUsd`).
|
|
3
|
+
*
|
|
4
|
+
* Same backend the mobile app uses:
|
|
5
|
+
* GET {ratesApiUrl}/api/v1/rate/rates?Ids=<externalId,...>&Currencies=usd
|
|
6
|
+
* `Ids` are catalog `externalId`s (CoinGecko-style: "ethereum", "tether"), not
|
|
7
|
+
* symbols or contracts. Native coins map statically; tokens are resolved via the
|
|
8
|
+
* public static asset catalog
|
|
9
|
+
* GET {staticResourcesUrl}/assets/main/blockchains/<net>/assets.mainnet.json
|
|
10
|
+
* matched by contract address (symbol as fallback).
|
|
11
|
+
*
|
|
12
|
+
* Callers must fail closed: `undefined` means "no rate", never "skip the check".
|
|
13
|
+
*/
|
|
14
|
+
import type { NetworkId } from "../networks.js";
|
|
15
|
+
/** Verified against the mobile app's asset catalogs (init_assets_data). */
|
|
16
|
+
export declare const NATIVE_EXTERNAL_ID: Record<NetworkId, string>;
|
|
17
|
+
export interface CatalogAsset {
|
|
18
|
+
symbol?: string;
|
|
19
|
+
address?: string;
|
|
20
|
+
externalId?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface AssetCatalogDocument {
|
|
23
|
+
assets?: CatalogAsset[];
|
|
24
|
+
}
|
|
25
|
+
/** Hex (EVM) contracts compare case-insensitively; base58 chains compare exactly. */
|
|
26
|
+
export declare function sameTokenAddress(a: string, b: string): boolean;
|
|
27
|
+
/** Find a token's externalId in a static catalog document. Pure, for tests. */
|
|
28
|
+
export declare function findExternalIdInCatalog(doc: AssetCatalogDocument, query: {
|
|
29
|
+
symbol?: string;
|
|
30
|
+
tokenAddress?: string;
|
|
31
|
+
}): string | undefined;
|
|
32
|
+
/** Response shape of GET /api/v1/rate/rates (keys are externalIds). */
|
|
33
|
+
export type RatesResponse = Record<string, Record<string, {
|
|
34
|
+
value?: number;
|
|
35
|
+
dailyChange?: number;
|
|
36
|
+
isExpired?: boolean;
|
|
37
|
+
}> | null>;
|
|
38
|
+
/** Extract a positive USD rate from the backend response. Pure, for tests. */
|
|
39
|
+
export declare function parseUsdRate(response: RatesResponse, externalId: string): {
|
|
40
|
+
value: number;
|
|
41
|
+
isExpired: boolean;
|
|
42
|
+
} | undefined;
|
|
43
|
+
/** Test hook. */
|
|
44
|
+
export declare function clearRatesCaches(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Resolve the rates id for an asset. Native coins come from the static map;
|
|
47
|
+
* tokens are looked up in the network's asset catalog by contract address.
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveExternalId(asset: {
|
|
50
|
+
network: NetworkId;
|
|
51
|
+
symbol?: string;
|
|
52
|
+
tokenAddress?: string;
|
|
53
|
+
}, correlationId?: string): Promise<string | undefined>;
|
|
54
|
+
/**
|
|
55
|
+
* USD price for one asset unit, or `undefined` when the asset or its rate is
|
|
56
|
+
* unknown / the backend is unreachable. Cached for ~60s per externalId.
|
|
57
|
+
*/
|
|
58
|
+
export declare function getUsdRate(asset: {
|
|
59
|
+
network: NetworkId;
|
|
60
|
+
symbol?: string;
|
|
61
|
+
tokenAddress?: string;
|
|
62
|
+
}, correlationId?: string): Promise<number | undefined>;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* USD rates for policy limits (`maxPerTxUsd`).
|
|
3
|
+
*
|
|
4
|
+
* Same backend the mobile app uses:
|
|
5
|
+
* GET {ratesApiUrl}/api/v1/rate/rates?Ids=<externalId,...>&Currencies=usd
|
|
6
|
+
* `Ids` are catalog `externalId`s (CoinGecko-style: "ethereum", "tether"), not
|
|
7
|
+
* symbols or contracts. Native coins map statically; tokens are resolved via the
|
|
8
|
+
* public static asset catalog
|
|
9
|
+
* GET {staticResourcesUrl}/assets/main/blockchains/<net>/assets.mainnet.json
|
|
10
|
+
* matched by contract address (symbol as fallback).
|
|
11
|
+
*
|
|
12
|
+
* Callers must fail closed: `undefined` means "no rate", never "skip the check".
|
|
13
|
+
*/
|
|
14
|
+
import { getConfig } from "../config.js";
|
|
15
|
+
import { logInfo, logWarn } from "../log.js";
|
|
16
|
+
import { httpJson } from "./http.js";
|
|
17
|
+
/** Verified against the mobile app's asset catalogs (init_assets_data). */
|
|
18
|
+
export const NATIVE_EXTERNAL_ID = {
|
|
19
|
+
ethereum: "ethereum",
|
|
20
|
+
bsc: "binancecoin",
|
|
21
|
+
polygon: "polygon-ecosystem-token",
|
|
22
|
+
base: "ethereum",
|
|
23
|
+
arbitrum: "ethereum",
|
|
24
|
+
optimism: "ethereum",
|
|
25
|
+
avalanche: "avalanche-2",
|
|
26
|
+
tron: "tron",
|
|
27
|
+
bitcoin: "bitcoin",
|
|
28
|
+
litecoin: "litecoin",
|
|
29
|
+
doge: "dogecoin",
|
|
30
|
+
solana: "solana",
|
|
31
|
+
ton: "the-open-network",
|
|
32
|
+
xrp: "ripple",
|
|
33
|
+
};
|
|
34
|
+
/** Networks with a token catalog on static resources (litecoin/doge are native-only). */
|
|
35
|
+
const CATALOG_NETWORKS = [
|
|
36
|
+
"ethereum",
|
|
37
|
+
"bsc",
|
|
38
|
+
"polygon",
|
|
39
|
+
"base",
|
|
40
|
+
"arbitrum",
|
|
41
|
+
"optimism",
|
|
42
|
+
"avalanche",
|
|
43
|
+
"tron",
|
|
44
|
+
"bitcoin",
|
|
45
|
+
"solana",
|
|
46
|
+
"ton",
|
|
47
|
+
"xrp",
|
|
48
|
+
];
|
|
49
|
+
/** Hex (EVM) contracts compare case-insensitively; base58 chains compare exactly. */
|
|
50
|
+
export function sameTokenAddress(a, b) {
|
|
51
|
+
const ta = a.trim();
|
|
52
|
+
const tb = b.trim();
|
|
53
|
+
if (ta === tb)
|
|
54
|
+
return true;
|
|
55
|
+
if (ta.startsWith("0x") && tb.startsWith("0x")) {
|
|
56
|
+
return ta.toLowerCase() === tb.toLowerCase();
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
/** Find a token's externalId in a static catalog document. Pure, for tests. */
|
|
61
|
+
export function findExternalIdInCatalog(doc, query) {
|
|
62
|
+
const assets = doc.assets ?? [];
|
|
63
|
+
if (query.tokenAddress) {
|
|
64
|
+
const byAddress = assets.find((a) => a.address && sameTokenAddress(a.address, query.tokenAddress));
|
|
65
|
+
if (byAddress?.externalId)
|
|
66
|
+
return byAddress.externalId;
|
|
67
|
+
}
|
|
68
|
+
if (query.symbol) {
|
|
69
|
+
const want = query.symbol.trim().toLowerCase();
|
|
70
|
+
const bySymbol = assets.find((a) => !a.address && a.symbol?.trim().toLowerCase() === want);
|
|
71
|
+
if (bySymbol?.externalId)
|
|
72
|
+
return bySymbol.externalId;
|
|
73
|
+
}
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
/** Extract a positive USD rate from the backend response. Pure, for tests. */
|
|
77
|
+
export function parseUsdRate(response, externalId) {
|
|
78
|
+
const perCurrency = response[externalId];
|
|
79
|
+
if (!perCurrency)
|
|
80
|
+
return undefined;
|
|
81
|
+
const usdKey = Object.keys(perCurrency).find((k) => k.toLowerCase() === "usd");
|
|
82
|
+
if (!usdKey)
|
|
83
|
+
return undefined;
|
|
84
|
+
const rate = perCurrency[usdKey];
|
|
85
|
+
if (!rate || typeof rate.value !== "number" || !(rate.value > 0))
|
|
86
|
+
return undefined;
|
|
87
|
+
return { value: rate.value, isExpired: rate.isExpired === true };
|
|
88
|
+
}
|
|
89
|
+
const CATALOG_TTL_MS = 6 * 60 * 60 * 1000;
|
|
90
|
+
const RATE_TTL_MS = 60 * 1000;
|
|
91
|
+
/**
|
|
92
|
+
* Keep the policy check snappy: a send/swap should not hang for minutes when
|
|
93
|
+
* the rates backend is down — it will be rejected (fail closed) anyway.
|
|
94
|
+
*/
|
|
95
|
+
const FETCH_OPTS = { timeoutMs: 10000, retries: 1 };
|
|
96
|
+
const catalogCache = new Map();
|
|
97
|
+
const rateCache = new Map();
|
|
98
|
+
/** Test hook. */
|
|
99
|
+
export function clearRatesCaches() {
|
|
100
|
+
catalogCache.clear();
|
|
101
|
+
rateCache.clear();
|
|
102
|
+
}
|
|
103
|
+
async function fetchCatalog(network, correlationId) {
|
|
104
|
+
const cached = catalogCache.get(network);
|
|
105
|
+
if (cached && Date.now() - cached.at < CATALOG_TTL_MS)
|
|
106
|
+
return cached.doc;
|
|
107
|
+
const cfg = getConfig();
|
|
108
|
+
if (!cfg.staticResourcesUrl) {
|
|
109
|
+
logWarn("rates.catalog.no_url", { correlationId, network });
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
const url = `${cfg.staticResourcesUrl}/assets/main/blockchains/${network}/assets.mainnet.json`;
|
|
113
|
+
try {
|
|
114
|
+
const doc = await httpJson(url, { method: "GET" }, { correlationId, label: "rates.catalog", ...FETCH_OPTS });
|
|
115
|
+
catalogCache.set(network, { at: Date.now(), doc });
|
|
116
|
+
logInfo("rates.catalog.ok", {
|
|
117
|
+
correlationId,
|
|
118
|
+
network,
|
|
119
|
+
assets: doc.assets?.length ?? 0,
|
|
120
|
+
});
|
|
121
|
+
return doc;
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
logWarn("rates.catalog.fail", {
|
|
125
|
+
correlationId,
|
|
126
|
+
network,
|
|
127
|
+
error: e instanceof Error ? e.message : String(e),
|
|
128
|
+
});
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Resolve the rates id for an asset. Native coins come from the static map;
|
|
134
|
+
* tokens are looked up in the network's asset catalog by contract address.
|
|
135
|
+
*/
|
|
136
|
+
export async function resolveExternalId(asset, correlationId) {
|
|
137
|
+
if (!asset.tokenAddress)
|
|
138
|
+
return NATIVE_EXTERNAL_ID[asset.network];
|
|
139
|
+
if (!CATALOG_NETWORKS.includes(asset.network))
|
|
140
|
+
return undefined;
|
|
141
|
+
const doc = await fetchCatalog(asset.network, correlationId);
|
|
142
|
+
if (!doc)
|
|
143
|
+
return undefined;
|
|
144
|
+
return findExternalIdInCatalog(doc, {
|
|
145
|
+
symbol: asset.symbol,
|
|
146
|
+
tokenAddress: asset.tokenAddress,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* USD price for one asset unit, or `undefined` when the asset or its rate is
|
|
151
|
+
* unknown / the backend is unreachable. Cached for ~60s per externalId.
|
|
152
|
+
*/
|
|
153
|
+
export async function getUsdRate(asset, correlationId) {
|
|
154
|
+
const externalId = await resolveExternalId(asset, correlationId);
|
|
155
|
+
if (!externalId) {
|
|
156
|
+
logWarn("rates.usd.no_external_id", {
|
|
157
|
+
correlationId,
|
|
158
|
+
network: asset.network,
|
|
159
|
+
symbol: asset.symbol,
|
|
160
|
+
tokenAddress: asset.tokenAddress,
|
|
161
|
+
});
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
const cached = rateCache.get(externalId);
|
|
165
|
+
if (cached && Date.now() - cached.at < RATE_TTL_MS)
|
|
166
|
+
return cached.value;
|
|
167
|
+
const cfg = getConfig();
|
|
168
|
+
if (!cfg.ratesApiUrl) {
|
|
169
|
+
logWarn("rates.usd.no_url", { correlationId, externalId });
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
const url = `${cfg.ratesApiUrl}/api/v1/rate/rates` +
|
|
173
|
+
`?Ids=${encodeURIComponent(externalId)}&Currencies=usd`;
|
|
174
|
+
let response;
|
|
175
|
+
try {
|
|
176
|
+
response = await httpJson(url, { method: "GET" }, { correlationId, label: "rates.usd", ...FETCH_OPTS });
|
|
177
|
+
}
|
|
178
|
+
catch (e) {
|
|
179
|
+
logWarn("rates.usd.fetch_fail", {
|
|
180
|
+
correlationId,
|
|
181
|
+
externalId,
|
|
182
|
+
error: e instanceof Error ? e.message : String(e),
|
|
183
|
+
});
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
const rate = parseUsdRate(response ?? {}, externalId);
|
|
187
|
+
if (!rate) {
|
|
188
|
+
logWarn("rates.usd.missing", { correlationId, externalId });
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
if (rate.isExpired) {
|
|
192
|
+
logWarn("rates.usd.expired", { correlationId, externalId, value: rate.value });
|
|
193
|
+
}
|
|
194
|
+
rateCache.set(externalId, { at: Date.now(), value: rate.value });
|
|
195
|
+
logInfo("rates.usd.ok", { correlationId, externalId, value: rate.value });
|
|
196
|
+
return rate.value;
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=rates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rates.js","sourceRoot":"","sources":["../../src/api/rates.ts"],"names":[],"mappings":"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"}
|
package/dist/api/swap.d.ts
CHANGED
package/dist/api/swap.js
CHANGED
package/dist/config.d.ts
CHANGED
|
@@ -4,15 +4,11 @@
|
|
|
4
4
|
* generated on first launch. There is no runtime IW_ENV switch — each build
|
|
5
5
|
* embeds one profile via the repo-root `npm run bake`.
|
|
6
6
|
*/
|
|
7
|
+
import { type NetworkId } from "./networks.js";
|
|
7
8
|
/** `package.json` version of this package (works from `src/` and compiled `dist/`). */
|
|
8
9
|
export declare function packageVersion(): string;
|
|
9
|
-
|
|
10
|
-
export
|
|
11
|
-
/** Networks that share the secp256k1 EVM key and signing scheme. */
|
|
12
|
-
export declare const EVM_NETWORKS: NetworkId[];
|
|
13
|
-
/** Non-EVM chains, each with its own curve / address scheme / node API. */
|
|
14
|
-
export declare const NON_EVM_NETWORKS: NetworkId[];
|
|
15
|
-
export declare const ALL_NETWORKS: NetworkId[];
|
|
10
|
+
export type { NetworkId } from "./networks.js";
|
|
11
|
+
export { ALL_NETWORKS, EVM_NETWORKS, NON_EVM_NETWORKS } from "./networks.js";
|
|
16
12
|
/**
|
|
17
13
|
* Networks advertised during challenge login. Kept to the proven EVM+Tron set:
|
|
18
14
|
* the backend authenticates against these public keys and the flow is verified
|
|
@@ -22,7 +18,7 @@ export declare const ALL_NETWORKS: NetworkId[];
|
|
|
22
18
|
export declare const AUTH_NETWORKS: NetworkId[];
|
|
23
19
|
/**
|
|
24
20
|
* Networks whose transfer signing is implemented (send is allowed). All of these
|
|
25
|
-
* go through the
|
|
21
|
+
* go through the IronWallet relay forward flow (estimate → sign → forward),
|
|
26
22
|
* including TON (IR-673 / mobile RelayV2).
|
|
27
23
|
*/
|
|
28
24
|
export declare const SIGNABLE_NETWORKS: NetworkId[];
|
|
@@ -40,19 +36,25 @@ export declare function relaySegment(network: NetworkId): string;
|
|
|
40
36
|
export interface Config {
|
|
41
37
|
authUrl: string;
|
|
42
38
|
relayUrl: string;
|
|
43
|
-
/** Swap
|
|
39
|
+
/** Swap API host (`/swp/exchange/*`, `/swp/refs/*`). */
|
|
44
40
|
swapProxyUrl: string;
|
|
41
|
+
/** Rates API host (`/api/v1/rate/rates`). Used only for USD policy limits. */
|
|
42
|
+
ratesApiUrl: string;
|
|
43
|
+
/** Static resources base with asset catalogs (`/assets/main/blockchains/...`). */
|
|
44
|
+
staticResourcesUrl: string;
|
|
45
45
|
/** Optional relay API key (x-api-key). Empty string means header is omitted. */
|
|
46
46
|
relayApiKey: string;
|
|
47
47
|
/** App version string sent as x-iwt-cli / X-App-Version. */
|
|
48
48
|
appVersion: string;
|
|
49
49
|
/** Device id sent as X-Device-Id. UUID file under the keystore dir unless overridden. */
|
|
50
50
|
deviceId: string;
|
|
51
|
+
/** Machine fingerprint sent as X-Device-Fingerprint. OS install id + platform, or device-id if that id is missing. */
|
|
52
|
+
deviceFingerprint: string;
|
|
51
53
|
/** X-Device-Locale value, format: TimeZone=..;Language=..;Region=..; */
|
|
52
54
|
deviceLocale: string;
|
|
53
55
|
/**
|
|
54
|
-
* JSON for `X-Device-Info`.
|
|
55
|
-
* (
|
|
56
|
+
* JSON for `X-Device-Info`. The swap API keys routing off `systemName`
|
|
57
|
+
* (`Web` here — this is a desktop MCP, not a phone).
|
|
56
58
|
*/
|
|
57
59
|
deviceInfo: string;
|
|
58
60
|
/** Directory holding the encrypted keystore. */
|
|
@@ -88,8 +90,10 @@ export interface Config {
|
|
|
88
90
|
/** When true, `send_transfer` and `execute_swap` are rejected immediately. */
|
|
89
91
|
readOnly: boolean;
|
|
90
92
|
}
|
|
93
|
+
/** Host-honest Web payload. Do not pretend to be iOS/Android. */
|
|
94
|
+
export declare function defaultDeviceInfo(): string;
|
|
91
95
|
export declare function getConfig(): Config;
|
|
92
96
|
/** Reject send/swap when `IW_READ_ONLY` is set. */
|
|
93
97
|
export declare function assertServerWritable(action: "send" | "swap"): void;
|
|
94
|
-
/**
|
|
98
|
+
/** Device headers sent to auth, relay, and swap APIs. */
|
|
95
99
|
export declare function commonHeaders(cfg: Config): Record<string, string>;
|
package/dist/config.js
CHANGED
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
* embeds one profile via the repo-root `npm run bake`.
|
|
6
6
|
*/
|
|
7
7
|
import { readFileSync } from "node:fs";
|
|
8
|
+
import { arch, release, type as osType } from "node:os";
|
|
8
9
|
import { dirname, join } from "node:path";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
10
11
|
import { BAKED_ENV } from "./generated/env-config.js";
|
|
12
|
+
import { ALL_NETWORKS, EVM_NETWORKS } from "./networks.js";
|
|
13
|
+
import { resolveDeviceFingerprint } from "./device-fingerprint.js";
|
|
11
14
|
import { resolveDeviceId, resolveKeystoreDir, resolveKeystorePassphrase, resolveRelayApiKey, } from "./local-secrets.js";
|
|
12
15
|
/** `package.json` version of this package (works from `src/` and compiled `dist/`). */
|
|
13
16
|
export function packageVersion() {
|
|
@@ -20,27 +23,7 @@ export function packageVersion() {
|
|
|
20
23
|
return "0.0.0";
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
|
-
|
|
24
|
-
export const EVM_NETWORKS = [
|
|
25
|
-
"ethereum",
|
|
26
|
-
"bsc",
|
|
27
|
-
"polygon",
|
|
28
|
-
"base",
|
|
29
|
-
"arbitrum",
|
|
30
|
-
"optimism",
|
|
31
|
-
"avalanche",
|
|
32
|
-
];
|
|
33
|
-
/** Non-EVM chains, each with its own curve / address scheme / node API. */
|
|
34
|
-
export const NON_EVM_NETWORKS = [
|
|
35
|
-
"tron",
|
|
36
|
-
"bitcoin",
|
|
37
|
-
"litecoin",
|
|
38
|
-
"doge",
|
|
39
|
-
"solana",
|
|
40
|
-
"ton",
|
|
41
|
-
"xrp",
|
|
42
|
-
];
|
|
43
|
-
export const ALL_NETWORKS = [...EVM_NETWORKS, ...NON_EVM_NETWORKS];
|
|
26
|
+
export { ALL_NETWORKS, EVM_NETWORKS, NON_EVM_NETWORKS } from "./networks.js";
|
|
44
27
|
/**
|
|
45
28
|
* Networks advertised during challenge login. Kept to the proven EVM+Tron set:
|
|
46
29
|
* the backend authenticates against these public keys and the flow is verified
|
|
@@ -50,7 +33,7 @@ export const ALL_NETWORKS = [...EVM_NETWORKS, ...NON_EVM_NETWORKS];
|
|
|
50
33
|
export const AUTH_NETWORKS = [...EVM_NETWORKS, "tron"];
|
|
51
34
|
/**
|
|
52
35
|
* Networks whose transfer signing is implemented (send is allowed). All of these
|
|
53
|
-
* go through the
|
|
36
|
+
* go through the IronWallet relay forward flow (estimate → sign → forward),
|
|
54
37
|
* including TON (IR-673 / mobile RelayV2).
|
|
55
38
|
*/
|
|
56
39
|
export const SIGNABLE_NETWORKS = [...ALL_NETWORKS];
|
|
@@ -106,6 +89,37 @@ function envBool(key, fallback) {
|
|
|
106
89
|
return fallback;
|
|
107
90
|
return !/^(0|false|off|no)$/i.test(v);
|
|
108
91
|
}
|
|
92
|
+
/** Host-honest Web payload. Do not pretend to be iOS/Android. */
|
|
93
|
+
export function defaultDeviceInfo() {
|
|
94
|
+
return JSON.stringify({
|
|
95
|
+
systemName: "Web",
|
|
96
|
+
systemVersion: packageVersion(),
|
|
97
|
+
model: "ironwallet-mcp",
|
|
98
|
+
platform: process.platform,
|
|
99
|
+
arch: arch(),
|
|
100
|
+
os: osType(),
|
|
101
|
+
osRelease: release(),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function defaultDeviceLocale() {
|
|
105
|
+
let timeZone = "UTC";
|
|
106
|
+
let language = "en";
|
|
107
|
+
let region = "US";
|
|
108
|
+
try {
|
|
109
|
+
const opts = Intl.DateTimeFormat().resolvedOptions();
|
|
110
|
+
if (opts.timeZone)
|
|
111
|
+
timeZone = opts.timeZone;
|
|
112
|
+
const parts = (opts.locale ?? "").split(/[-_]/);
|
|
113
|
+
if (parts[0])
|
|
114
|
+
language = parts[0];
|
|
115
|
+
if (parts[1])
|
|
116
|
+
region = parts[1];
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// keep UTC/en/US
|
|
120
|
+
}
|
|
121
|
+
return `TimeZone=${timeZone};Language=${language};Region=${region};`;
|
|
122
|
+
}
|
|
109
123
|
/** Parse an integer env var, clamped to [min, max], falling back on invalid input. */
|
|
110
124
|
function envInt(key, fallback, min, max) {
|
|
111
125
|
const raw = process.env[key];
|
|
@@ -130,20 +144,22 @@ export function getConfig() {
|
|
|
130
144
|
if (cached)
|
|
131
145
|
return cached;
|
|
132
146
|
resolveKeystorePassphrase();
|
|
147
|
+
const deviceId = resolveDeviceId();
|
|
133
148
|
const profile = BAKED_ENV;
|
|
134
149
|
cached = {
|
|
135
150
|
authUrl: envOr("IW_AUTH_URL", profile.authUrl),
|
|
136
151
|
relayUrl: envOr("IW_RELAY_URL", profile.relayUrl),
|
|
137
152
|
swapProxyUrl: envOr("IW_SWAP_PROXY_URL", profile.swapProxyUrl),
|
|
153
|
+
// Hosts live only in the baked configurations profile (no fallbacks in
|
|
154
|
+
// code). Empty string means USD policy limits fail closed.
|
|
155
|
+
ratesApiUrl: envOr("IW_RATES_API_URL", profile.ratesApiUrl ?? ""),
|
|
156
|
+
staticResourcesUrl: envOr("IW_STATIC_RESOURCES_URL", profile.staticResourcesUrl ?? ""),
|
|
138
157
|
relayApiKey: resolveRelayApiKey(),
|
|
139
158
|
appVersion: envOr("IW_APP_VERSION", `ironwallet-mcp/${packageVersion()}`),
|
|
140
|
-
deviceId
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
systemVersion: "17.0",
|
|
145
|
-
model: "iPhone",
|
|
146
|
-
})),
|
|
159
|
+
deviceId,
|
|
160
|
+
deviceFingerprint: resolveDeviceFingerprint(deviceId),
|
|
161
|
+
deviceLocale: envOr("IW_DEVICE_LOCALE", defaultDeviceLocale()),
|
|
162
|
+
deviceInfo: envOr("IW_DEVICE_INFO", defaultDeviceInfo()),
|
|
147
163
|
keystoreDir: resolveKeystoreDir(),
|
|
148
164
|
evmRpcUrls: resolveEvmRpcUrls(profile.evmRpcUrls),
|
|
149
165
|
tronApiUrl: envOr("IW_TRON_API", profile.tronApiUrl),
|
|
@@ -171,15 +187,16 @@ export function assertServerWritable(action) {
|
|
|
171
187
|
? "Server is read-only (IW_READ_ONLY); swaps are disabled."
|
|
172
188
|
: "Server is read-only (IW_READ_ONLY); sending is disabled.");
|
|
173
189
|
}
|
|
174
|
-
/**
|
|
190
|
+
/** Device headers sent to auth, relay, and swap APIs. */
|
|
175
191
|
export function commonHeaders(cfg) {
|
|
176
192
|
const headers = {
|
|
177
193
|
"x-iwt-cli": cfg.appVersion,
|
|
178
194
|
"X-App-Version": cfg.appVersion,
|
|
179
195
|
"X-Device-Id": cfg.deviceId,
|
|
196
|
+
"X-Device-Fingerprint": cfg.deviceFingerprint,
|
|
180
197
|
// Backend validates the format: TimeZone=..;Language=..;Region=..;
|
|
181
198
|
"X-Device-Locale": cfg.deviceLocale,
|
|
182
|
-
//
|
|
199
|
+
// Swap routing reads UserData.XDeviceInfo.systemName (`Web` for this client).
|
|
183
200
|
"X-Device-Info": cfg.deviceInfo,
|
|
184
201
|
"Content-Language": "en",
|
|
185
202
|
};
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,yBAAyB,EACzB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAE5B,uFAAuF;AACvF,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAClB,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EACvC,IAAI,EACJ,cAAc,CACf,CAAC;QACF,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAyB,CAAC;QAC9E,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical MCP consent copy (English). Chat, create_wallets, and the local
|
|
3
|
+
* manager must show this text. Bump CONSENT_VERSION when the wording changes.
|
|
4
|
+
*/
|
|
5
|
+
export declare const CONSENT_VERSION = "v1";
|
|
6
|
+
export declare const CONSENT_TITLE = "Before connecting an AI agent to your wallet";
|
|
7
|
+
export declare const CONSENT_LEAD = "Connecting MCP lets the AI agent view balances, transfer assets, and swap on behalf of this wallet.";
|
|
8
|
+
export declare const CONSENT_BULLETS: readonly ["By default the agent can send funds and swap without extra confirmation on each operation.", "Use a dedicated wallet with a small balance, not your main or savings wallet.", "The recovery phrase stays encrypted on this device. Loss or compromise means permanent loss of access to the funds.", "IronWallet is not responsible for the AI agent's actions, misconfiguration, or loss of funds from using this feature.", "You can ask the agent to set limits in chat (read-only, a per-transaction USD limit, a transfer recipient allow-list)."];
|
|
9
|
+
export declare const CONSENT_CHECKBOX = "I understand the risks and want to continue";
|
|
10
|
+
export declare const CONSENT_CHAT_INSTRUCTION = "Show this text in full in chat. Do not shorten it. After the user explicitly confirms, call accept_mcp_consent with accepted=true, or open the wallet manager so they can accept there.";
|
|
11
|
+
export declare function consentDocument(): {
|
|
12
|
+
version: string;
|
|
13
|
+
title: string;
|
|
14
|
+
lead: string;
|
|
15
|
+
bullets: readonly string[];
|
|
16
|
+
checkbox: string;
|
|
17
|
+
instruction: string;
|
|
18
|
+
};
|