@wafermapxyz/cli 0.0.0-stage → 0.4.2

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.
@@ -0,0 +1,36 @@
1
+ export declare const SOLANA_CAIP = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
2
+ export declare const SOLANA_USDC = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
3
+ /** Signs as one Solana address. `blockhash` returns a recent blockhash from any mainnet RPC. */
4
+ export type SolanaPayer = {
5
+ address: string;
6
+ sign: (message: Uint8Array) => Uint8Array | Promise<Uint8Array>;
7
+ blockhash: () => Promise<string>;
8
+ };
9
+ export declare function isSolanaAddress(v: string): boolean;
10
+ /** The agent's Solana keypair, derived from its EVM private key. */
11
+ export declare function solanaKeyFromEvm(privateKey: string): {
12
+ address: string;
13
+ /** seed ‖ public key, base58: the format Phantom and Solflare import. */
14
+ secretKey: string;
15
+ sign: (message: Uint8Array) => Uint8Array<ArrayBufferLike>;
16
+ };
17
+ /** The associated token account of `owner` for `mint` (classic SPL Token program). */
18
+ export declare function ataOf(owner: string, mint?: string): string;
19
+ export type SolanaQuote = {
20
+ amount: string;
21
+ payTo: string;
22
+ asset: string;
23
+ feePayer: string;
24
+ memo?: string;
25
+ };
26
+ /** The base64 wire transaction for an x402 `exact` payment on Solana, signed by `payer` only. */
27
+ export declare function solanaPayment(payer: SolanaPayer, q: SolanaQuote): Promise<string>;
28
+ export declare function solanaRpc(url: string, method: string, params: unknown[], f?: typeof fetch): Promise<any>;
29
+ export declare const latestBlockhash: (url: string, f?: typeof fetch) => Promise<string>;
30
+ /** USDC (6 decimals) and SOL balances of a wallet. A wallet that never held USDC has no token
31
+ * account: 0. Read with getAccountInfo, which free public RPCs serve (getTokenAccountBalance is
32
+ * an "indexed" call some of them reserve for paid keys). */
33
+ export declare function solanaBalances(url: string, owner: string, f?: typeof fetch): Promise<{
34
+ usdc: string;
35
+ sol: string;
36
+ }>;
@@ -0,0 +1,78 @@
1
+ import { type Address, type Hex, type PublicClient, type WalletClient } from "viem";
2
+ export type Net = "robinhood" | "base";
3
+ export type Token = {
4
+ symbol: string;
5
+ name: string;
6
+ address: Address;
7
+ decimals: number | null;
8
+ kind?: "native" | "stable" | "wrapped" | "stock";
9
+ };
10
+ export declare const NATIVE: Address;
11
+ export declare const TOKENS: Record<Net, Token[]>;
12
+ /** KyberSwap's MetaAggregationRouterV2, the only contract a swap may approve or call. The API's answer
13
+ * is checked against it, and the swap calldata is decoded and checked too (see executeSwap). */
14
+ export declare const KYBER_ROUTER: Record<Net, Address>;
15
+ export declare class SwapError extends Error {
16
+ code: string;
17
+ constructor(code: string, message: string);
18
+ }
19
+ /**
20
+ * Resolve "ETH", "USDC", "TSLA" or a 0x address to a token with decimals.
21
+ * `lookup` finds symbols outside the built-in list (e.g. Robinhood stock tokens).
22
+ */
23
+ export declare function resolveToken(net: Net, ref: string, client: PublicClient, lookup?: (net: Net, symbol: string) => Promise<Token | null>): Promise<Token>;
24
+ export type SwapQuote = {
25
+ network: Net;
26
+ tokenIn: Token;
27
+ tokenOut: Token;
28
+ amountIn: string;
29
+ amountOut: string;
30
+ amountInUsd: number | null;
31
+ amountOutUsd: number | null;
32
+ gasUsd: number | null;
33
+ /** Percent lost between USD in and USD out (route fees + impact), when both are known. */
34
+ impactPct: number | null;
35
+ routerAddress: Address;
36
+ route: unknown;
37
+ at: number;
38
+ };
39
+ /** Ask Kyber for the best route. `amount` is human units of tokenIn ("0.5"). */
40
+ export declare function quoteSwap(o: {
41
+ network: Net;
42
+ tokenIn: Token;
43
+ tokenOut: Token;
44
+ amount: string;
45
+ fetch?: typeof fetch;
46
+ }): Promise<SwapQuote>;
47
+ /**
48
+ * What the calldata will make the router do, read from the bytes themselves rather than from what the
49
+ * API says about them: which tokens, how much in, who receives the output, the minimum out, any fees.
50
+ */
51
+ export declare function decodeSwap(data: Hex): {
52
+ fn: "swap" | "swapSimpleMode";
53
+ srcToken: `0x${string}`;
54
+ dstToken: `0x${string}`;
55
+ receiver: `0x${string}`;
56
+ amount: bigint;
57
+ minReturn: bigint;
58
+ feeReceivers: readonly `0x${string}`[];
59
+ feeAmounts: readonly bigint[];
60
+ };
61
+ /**
62
+ * Build, check, approve (exact amount, only if needed) and send the swap from the wallet's account.
63
+ * The built transaction is checked before anything is signed: KyberSwap's router, the quoted tokens and
64
+ * amount, the output to this wallet, no fees, and a minimum output within the slippage of the quote.
65
+ * Every step is reported through `step` so a UI can show progress.
66
+ */
67
+ export declare function executeSwap(o: {
68
+ quote: SwapQuote;
69
+ wallet: WalletClient;
70
+ client: PublicClient;
71
+ slippageBps?: number;
72
+ fetch?: typeof fetch;
73
+ step?: (s: string, tx?: Hex) => void;
74
+ }): Promise<{
75
+ approveTx: `0x${string}` | undefined;
76
+ tx: `0x${string}`;
77
+ amountOutMin: string;
78
+ }>;
@@ -0,0 +1,116 @@
1
+ import { type SolanaPayer } from "./solana";
2
+ export type Hex = `0x${string}`;
3
+ /** Anything that can sign EIP-712 typed data for one address (a viem account works). */
4
+ export type Signer = {
5
+ address: Hex;
6
+ signTypedData: (args: {
7
+ domain: any;
8
+ types: any;
9
+ primaryType: string;
10
+ message: any;
11
+ }) => Promise<Hex>;
12
+ };
13
+ export type ChainKey = "robinhood" | "base" | "arbitrum" | "polygon" | "solana";
14
+ export type Chain = {
15
+ key: ChainKey;
16
+ vm: "evm" | "svm";
17
+ id: number;
18
+ caip: string;
19
+ v1: string;
20
+ stable: string;
21
+ symbol: string;
22
+ name: string;
23
+ domain?: {
24
+ name: string;
25
+ version: string;
26
+ };
27
+ };
28
+ export declare const PAY_CHAINS: Chain[];
29
+ export declare const EVM_CHAINS: Chain[];
30
+ export declare const CHAIN_KEYS: ChainKey[];
31
+ export type Requirement = {
32
+ version: 1 | 2;
33
+ chain: Chain;
34
+ /** Base units, 6 decimals. */
35
+ amount: string;
36
+ usd: number;
37
+ payTo: string;
38
+ asset: string;
39
+ maxTimeoutSeconds: number;
40
+ /** EVM: the token's EIP-712 domain. Solana: the facilitator's fee payer and an optional memo. */
41
+ extra: {
42
+ name?: string;
43
+ version?: string;
44
+ feePayer?: string;
45
+ memo?: string;
46
+ };
47
+ /** The requirement exactly as the server sent it (echoed back in the payment). */
48
+ raw: any;
49
+ };
50
+ export type Quote = {
51
+ version: 1 | 2;
52
+ resource: any;
53
+ accepts: Requirement[];
54
+ offered: string[];
55
+ inputError?: {
56
+ code: string;
57
+ message: string;
58
+ };
59
+ };
60
+ export declare class X402Error extends Error {
61
+ code: string;
62
+ constructor(code: string, message: string);
63
+ }
64
+ /** Read a 402 response into a quote. Only payable requirements are kept in `accepts`. */
65
+ export declare function readQuote(res: Response): Promise<Quote>;
66
+ /** Pick the requirement to pay: the first preferred chain that is offered. */
67
+ export declare function choose(q: Quote, prefer?: ChainKey[]): Requirement;
68
+ /** The longest an EVM payment authorization stays valid, whatever the server allows. Whoever receives
69
+ * a signed payment can execute it until then, so this bounds how long "failed calls cost $0" rests on
70
+ * the server's word. Long enough for a slow resource plus settlement (Wafermap needs at least 60 s). */
71
+ export declare const MAX_VALIDITY_SECONDS = 90;
72
+ /** Sign the payment for the exact amount and return the header to send: an EIP-3009 transfer on
73
+ * EVM chains, a partially signed USDC transfer transaction on Solana. */
74
+ export declare function signPayment(signer: Signer, req: Requirement, resource: any, solana?: SolanaPayer): Promise<{
75
+ name: string;
76
+ value: string;
77
+ auth?: SignedAuth;
78
+ }>;
79
+ /** Settlement details a server returns after a paid call, when it sends them. */
80
+ export declare function readSettlement(res: Response): {
81
+ tx?: string;
82
+ network?: string;
83
+ success?: boolean;
84
+ } | null;
85
+ /** What an EVM payment authorized on-chain: anyone holding it can execute it until `validBefore`
86
+ * (unix seconds), once. `authorizationState(from, nonce)` on the token says whether it was used. */
87
+ export type SignedAuth = {
88
+ nonce: Hex;
89
+ validBefore: number;
90
+ };
91
+ export type Signed = {
92
+ req: Requirement;
93
+ from: string;
94
+ auth?: SignedAuth;
95
+ };
96
+ export type PaidCall = {
97
+ res: Response;
98
+ paid: (Signed & {
99
+ tx: string | null;
100
+ }) | null;
101
+ };
102
+ /**
103
+ * Fetch an x402 resource, paying once when it answers 402.
104
+ * `approve` sees the chosen requirement before anything is signed; throw to stop.
105
+ * `onSigned` runs once the payment is signed and before it is sent: from then on the payment
106
+ * can be executed whatever the server answers (or if it never answers), so a spend limit has to
107
+ * count it here, at its full amount, not from what the server reports back.
108
+ */
109
+ export declare function x402fetch(url: string, init: RequestInit, opts: {
110
+ signer: Signer | (() => Promise<Signer>);
111
+ solana?: SolanaPayer;
112
+ prefer?: ChainKey[];
113
+ approve?: (req: Requirement) => Promise<void> | void;
114
+ onSigned?: (s: Signed) => void;
115
+ fetch?: typeof fetch;
116
+ }): Promise<PaidCall>;
@@ -0,0 +1,184 @@
1
+ import { type Hex, type LocalAccount, type PublicClient } from "viem";
2
+ import { type SwapQuote, type Token } from "../shared/swap";
3
+ import { type SolanaPayer } from "../shared/solana";
4
+ import { type ChainKey } from "../shared/x402client";
5
+ /** A payment network. Swaps run on Robinhood Chain and Base only. */
6
+ export type Net = ChainKey;
7
+ type EvmNet = Exclude<Net, "solana">;
8
+ export declare const NETS: Net[];
9
+ export declare const isNet: (v: unknown) => v is Net;
10
+ export type Limits = {
11
+ perPayment: number;
12
+ perDay: number;
13
+ perSwap: number;
14
+ };
15
+ /**
16
+ * One line of the spend log. A payment writes two lines: the signed amount the moment it is
17
+ * signed (`status: "signed"`, counted by the daily cap), then the outcome with `usd: 0` and
18
+ * `of` pointing back to it. Older logs have single lines without `id`; `mergeLog` joins them.
19
+ */
20
+ export type SpendEntry = {
21
+ at: number;
22
+ kind: "resource" | "service" | "swap";
23
+ what: string;
24
+ usd: number;
25
+ network: Net;
26
+ tx: string | null;
27
+ payer: string;
28
+ id?: string;
29
+ of?: string;
30
+ status?: PaymentStatus;
31
+ /** EVM payments: the EIP-3009 nonce and expiry, to check on-chain whether it was ever used. */
32
+ nonce?: string;
33
+ validBefore?: number;
34
+ /** What the server said it charged, for the record only. */
35
+ reported?: number | null;
36
+ };
37
+ /** signed: sent, outcome not known (or the request died) · ok: the server answered 2xx ·
38
+ * failed: it answered with an error · refused: it refused the payment itself ·
39
+ * aborted: signed but never sent, because another process had used the budget meanwhile. */
40
+ export type PaymentStatus = "signed" | "ok" | "failed" | "refused" | "aborted";
41
+ export type Approval = {
42
+ kind: SpendEntry["kind"];
43
+ what: string;
44
+ usd: number;
45
+ network: Net;
46
+ detail?: string;
47
+ };
48
+ export type WafermapOptions = {
49
+ /** A 0x private key, or pass `account` (any viem LocalAccount). A private key also gives the
50
+ * wallet its Solana side (derived from it); with `account`, pass `solana` to pay on Solana. */
51
+ privateKey?: Hex;
52
+ account?: LocalAccount;
53
+ /** Signs Solana payments. Derived from `privateKey` when not given. */
54
+ solana?: Omit<SolanaPayer, "blockhash">;
55
+ /** Read-only: a Solana address for balances when there is no key. */
56
+ solanaAddress?: string;
57
+ /** Default https://wafermap.xyz */
58
+ baseUrl?: string;
59
+ /** Preferred payment network. Default robinhood (USDG); base, arbitrum, polygon and solana pay in USDC. */
60
+ network?: Net;
61
+ limits?: Partial<Limits>;
62
+ /** Where spending is remembered for the daily cap. Default: in memory. */
63
+ ledger?: {
64
+ entries(): SpendEntry[];
65
+ record(e: SpendEntry): void;
66
+ };
67
+ /** Called before every payment or swap. Return false to refuse. Default: allow within caps. */
68
+ approve?: (a: Approval) => boolean | Promise<boolean>;
69
+ /** RPC URLs per network. Solana defaults to `${baseUrl}/api/solana`. */
70
+ rpc?: Partial<Record<Net, string>>;
71
+ /** Give up on any HTTP request (quote, paid call, discovery, swap API) after this long. Default 60000 ms.
72
+ * A paid call that times out after signing stays counted until its authorization expires. */
73
+ timeoutMs?: number;
74
+ };
75
+ export declare class WafermapError extends Error {
76
+ code: string;
77
+ constructor(code: string, message: string);
78
+ }
79
+ /** One row per payment: the signed line joined with its outcome line. Old single lines pass through. */
80
+ export declare function mergeLog(entries: SpendEntry[]): SpendEntry[];
81
+ /**
82
+ * Was a signed payment ever executed? Reads the token contract (EIP-3009 authorizationState),
83
+ * so the answer does not depend on any server and needs no key. "unused" after expiry means it
84
+ * can never be charged. Solana payments and old log lines without a nonce come back "unknown"; an RPC
85
+ * failure comes back "error".
86
+ */
87
+ export declare function checkPayment(e: Pick<SpendEntry, "network" | "payer" | "nonce" | "validBefore">, rpc?: string): Promise<"used" | "unused" | "pending" | "unknown" | "error">;
88
+ export declare const DEFAULT_LIMITS: Limits;
89
+ export declare const DEFAULT_TIMEOUT_MS = 60000;
90
+ export declare class Wafermap {
91
+ readonly account: LocalAccount;
92
+ readonly baseUrl: string;
93
+ network: Net;
94
+ limits: Limits;
95
+ private ledger;
96
+ private approveFn?;
97
+ private rpc;
98
+ private clients;
99
+ readonly solana: SolanaPayer | null;
100
+ readonly solanaAddress: string | null;
101
+ readonly timeoutMs: number;
102
+ /** fetch with this wallet's time limit: a server that never answers cannot hold a call forever. */
103
+ readonly fetch: typeof fetch;
104
+ constructor(o: WafermapOptions);
105
+ get address(): `0x${string}`;
106
+ client(n: EvmNet): PublicClient;
107
+ /** USD signed today (local midnight) on payments, not counting swaps. Every signed payment
108
+ * counts at its full amount, whatever the server answered, because a signed payment can be
109
+ * executed until it expires. `checkPayment` shows on-chain which ones were never used. */
110
+ spentToday(): number;
111
+ private gate;
112
+ private prefer;
113
+ /** A second 402 means the payment itself was refused (usually: not enough balance). */
114
+ private rejected;
115
+ /** Write the signed payment to the ledger before it leaves the machine. */
116
+ private booked;
117
+ /** The outcome line for a booked payment. It adds nothing to the daily total. */
118
+ private outcome;
119
+ /** The timeout error for `host`, saying whether a payment was signed and until when it can be executed. */
120
+ private timeoutError;
121
+ /** Read a JSON body; a time-out while reading is reported, anything else gives `fallback`. */
122
+ private body;
123
+ private queue;
124
+ private acquire;
125
+ private paidFetch;
126
+ resources(): Promise<any[]>;
127
+ call(id: string, input?: Record<string, unknown>, o?: {
128
+ network?: Net;
129
+ }): Promise<{
130
+ resource: string;
131
+ result: any;
132
+ receipt: {
133
+ network: string;
134
+ asset: string;
135
+ amountUsd: number;
136
+ payer: string;
137
+ tx: string;
138
+ ms: number;
139
+ } | null;
140
+ }>;
141
+ discover(query: string, limit?: number): Promise<{
142
+ url: string;
143
+ method: string;
144
+ name: string | null;
145
+ description: string;
146
+ priceUsd: number | null;
147
+ networks: string[];
148
+ payable: boolean;
149
+ input: unknown;
150
+ outputExample: unknown;
151
+ }[]>;
152
+ /** Call any x402 URL and pay it if it asks. GET sends `input` as the query string, POST as JSON. */
153
+ pay(url: string, o?: {
154
+ method?: "GET" | "POST";
155
+ input?: Record<string, unknown>;
156
+ network?: Net;
157
+ }): Promise<{
158
+ status: number;
159
+ result: any;
160
+ paid: {
161
+ usd: number;
162
+ network: ChainKey;
163
+ tx: string | null;
164
+ } | null;
165
+ }>;
166
+ balances(): Promise<Record<ChainKey, {
167
+ eth: string;
168
+ gas: string;
169
+ stable: string;
170
+ symbol: string;
171
+ } | null>>;
172
+ private tokenCache;
173
+ private lookup;
174
+ swapQuote(from: string, to: string, amount: string, network?: Net): Promise<SwapQuote>;
175
+ swap(quote: SwapQuote, o?: {
176
+ slippageBps?: number;
177
+ step?: (s: string) => void;
178
+ }): Promise<{
179
+ approveTx: `0x${string}` | undefined;
180
+ tx: `0x${string}`;
181
+ amountOutMin: string;
182
+ }>;
183
+ }
184
+ export type { SwapQuote, Token };
@@ -0,0 +1,5 @@
1
+ export { Wafermap, WafermapError, DEFAULT_LIMITS, DEFAULT_TIMEOUT_MS, checkPayment, mergeLog } from "./core.js";
2
+ export type { Approval, Limits, Net, PaymentStatus, SpendEntry, SwapQuote, Token, WafermapOptions } from "./core.js";
3
+ export { x402fetch, readQuote, choose, signPayment, PAY_CHAINS, MAX_VALIDITY_SECONDS, X402Error } from "../shared/x402client";
4
+ export { quoteSwap, executeSwap, decodeSwap, KYBER_ROUTER, SwapError } from "../shared/swap";
5
+ export type { Requirement, Quote, Signed, SignedAuth, Signer } from "../shared/x402client";
package/package.json CHANGED
@@ -1,6 +1,68 @@
1
- {
2
- "name": "@wafermapxyz/cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
1
+ {
2
+ "name": "@wafermapxyz/cli",
3
+ "version": "0.4.2",
4
+ "description": "Wafermap for agents: an encrypted local wallet that pays x402 resources, discovers and pays third-party x402 services (USDG on Robinhood Chain; USDC on Solana, Base, Arbitrum, Polygon), and swaps on Robinhood Chain and Base. CLI, SDK and MCP server.",
5
+ "type": "module",
6
+ "bin": {
7
+ "wafermap": "dist/cli.js"
8
+ },
9
+ "main": "dist/index.js",
10
+ "types": "dist/types/src/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/types/src/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "CHANGELOG.md",
21
+ "LICENSE"
22
+ ],
23
+ "engines": {
24
+ "node": ">=20"
25
+ },
26
+ "scripts": {
27
+ "build": "node build.mjs",
28
+ "test": "node --test"
29
+ },
30
+ "keywords": [
31
+ "x402",
32
+ "agents",
33
+ "mcp",
34
+ "wallet",
35
+ "robinhood-chain",
36
+ "base",
37
+ "arbitrum",
38
+ "polygon",
39
+ "solana",
40
+ "usdg",
41
+ "usdc"
42
+ ],
43
+ "homepage": "https://wafermap.xyz/docs#cli",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/wafermap/wafermap-cli.git"
47
+ },
48
+ "bugs": {
49
+ "url": "https://github.com/wafermap/wafermap-cli/issues"
50
+ },
51
+ "license": "MIT",
52
+ "publishConfig": {
53
+ "access": "public"
54
+ },
55
+ "dependencies": {
56
+ "@modelcontextprotocol/sdk": "1.32.1",
57
+ "@noble/curves": "^1.9.1",
58
+ "@noble/hashes": "^1.8.0",
59
+ "@scure/base": "^1.2.6",
60
+ "viem": "^2.56.8",
61
+ "zod": "4.6.7"
62
+ },
63
+ "devDependencies": {
64
+ "@types/node": "^22.10.0",
65
+ "esbuild": "^0.28.1",
66
+ "typescript": "^7.0.2"
67
+ }
68
+ }