@moonpay/cli 0.4.3 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,151 @@
1
+ import { createRequire as __createRequire } from "module"; const require = __createRequire(import.meta.url);
2
+ import {
3
+ bitcoinBalanceRetrieve,
4
+ callTool,
5
+ messageSign,
6
+ resolveBaseUrl,
7
+ schemas_default,
8
+ tokenSwap,
9
+ transactionSign,
10
+ virtualAccountWalletRegister,
11
+ walletCreate,
12
+ walletDelete,
13
+ walletImport,
14
+ walletList,
15
+ walletRetrieve,
16
+ x402Request
17
+ } from "./chunk-PBRXVTTG.js";
18
+ import "./chunk-V7MA7WNX.js";
19
+
20
+ // src/mcp.ts
21
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
22
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
23
+ import {
24
+ CallToolRequestSchema,
25
+ ListToolsRequestSchema
26
+ } from "@modelcontextprotocol/sdk/types.js";
27
+ import { zodToJsonSchema } from "zod-to-json-schema";
28
+ var LOCAL_TOOLS = [
29
+ walletCreate,
30
+ walletImport,
31
+ walletList,
32
+ walletRetrieve,
33
+ walletDelete,
34
+ transactionSign,
35
+ messageSign,
36
+ bitcoinBalanceRetrieve,
37
+ x402Request,
38
+ tokenSwap,
39
+ virtualAccountWalletRegister
40
+ ];
41
+ var localToolMap = new Map(LOCAL_TOOLS.map((t) => [t.schema.name, t]));
42
+ function resolveRemoteSchema(schema) {
43
+ const input = schema.inputSchema;
44
+ if (input.$ref && input.definitions) {
45
+ const defName = input.$ref.replace("#/definitions/", "");
46
+ return input.definitions[defName] ?? input;
47
+ }
48
+ return input;
49
+ }
50
+ async function startMcpServer() {
51
+ const server = new Server(
52
+ { name: "moonpay", version: "1.0.0" },
53
+ { capabilities: { tools: { listChanged: true } } }
54
+ );
55
+ const toolDefs = LOCAL_TOOLS.map((tool) => ({
56
+ name: tool.schema.name,
57
+ description: tool.schema.description,
58
+ inputSchema: zodToJsonSchema(tool.schema.input)
59
+ }));
60
+ const remotePropsMap = /* @__PURE__ */ new Map();
61
+ for (const schema of schemas_default) {
62
+ if (localToolMap.has(schema.name)) continue;
63
+ const resolved = resolveRemoteSchema(schema);
64
+ remotePropsMap.set(
65
+ schema.name,
66
+ resolved.properties ?? {}
67
+ );
68
+ toolDefs.push({
69
+ name: schema.name,
70
+ description: schema.description,
71
+ inputSchema: {
72
+ type: "object",
73
+ properties: resolved.properties ?? {},
74
+ ...resolved.required ? { required: resolved.required } : {},
75
+ ...resolved.additionalProperties !== void 0 ? { additionalProperties: resolved.additionalProperties } : {}
76
+ }
77
+ });
78
+ }
79
+ const toolDefMap = new Map(toolDefs.map((t) => [t.name, t]));
80
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
81
+ tools: toolDefs.map((t) => ({
82
+ name: t.name,
83
+ description: t.description,
84
+ inputSchema: t.inputSchema
85
+ }))
86
+ }));
87
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
88
+ const { name, arguments: params = {} } = request.params;
89
+ if (!toolDefMap.has(name)) {
90
+ return {
91
+ content: [{ type: "text", text: `Unknown tool: ${name}` }],
92
+ isError: true
93
+ };
94
+ }
95
+ try {
96
+ const localTool = localToolMap.get(name);
97
+ if (localTool) {
98
+ const shape = localTool.schema.input.shape ?? {};
99
+ for (const [key, field] of Object.entries(shape)) {
100
+ if (params[key] === void 0 && field._def.typeName === "ZodNullable") {
101
+ params[key] = null;
102
+ }
103
+ }
104
+ const result2 = await localTool.handler(params);
105
+ return {
106
+ content: [
107
+ { type: "text", text: JSON.stringify(result2, null, 2) }
108
+ ]
109
+ };
110
+ }
111
+ const props = remotePropsMap.get(name) ?? {};
112
+ for (const [key, prop] of Object.entries(props)) {
113
+ if (params[key] === void 0) {
114
+ const nullable = Array.isArray(prop.type) ? prop.type.includes("null") : prop.anyOf?.some((t) => t.type === "null");
115
+ if (nullable) params[key] = null;
116
+ }
117
+ const isNum = prop.type === "number" || prop.type === "integer" || Array.isArray(prop.type) && prop.type.some(
118
+ (t) => t === "number" || t === "integer"
119
+ ) || prop.anyOf?.some(
120
+ (t) => t.type === "number" || t.type === "integer"
121
+ );
122
+ if (isNum && typeof params[key] === "string") {
123
+ params[key] = Number(params[key]);
124
+ }
125
+ }
126
+ const baseUrl = resolveBaseUrl();
127
+ const result = await callTool(baseUrl, name, params);
128
+ return {
129
+ content: [
130
+ { type: "text", text: JSON.stringify(result, null, 2) }
131
+ ]
132
+ };
133
+ } catch (error) {
134
+ return {
135
+ content: [
136
+ {
137
+ type: "text",
138
+ text: error instanceof Error ? error.message : String(error)
139
+ }
140
+ ],
141
+ isError: true
142
+ };
143
+ }
144
+ });
145
+ const transport = new StdioServerTransport();
146
+ await server.connect(transport);
147
+ }
148
+ export {
149
+ startMcpServer
150
+ };
151
+ //# sourceMappingURL=mcp-TDQN25MO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\nimport { zodToJsonSchema } from \"zod-to-json-schema\";\nimport { callTool } from \"./client\";\nimport { resolveBaseUrl } from \"./auth\";\nimport type { Tool } from \"./tools/shared\";\nimport { walletCreate } from \"./tools/wallet/create/tool\";\nimport { walletImport } from \"./tools/wallet/import/tool\";\nimport { walletList } from \"./tools/wallet/list/tool\";\nimport { walletRetrieve } from \"./tools/wallet/retrieve/tool\";\nimport { walletDelete } from \"./tools/wallet/delete/tool\";\nimport { transactionSign } from \"./tools/transaction/sign/tool\";\nimport { messageSign } from \"./tools/message/sign/tool\";\nimport { bitcoinBalanceRetrieve } from \"./tools/bitcoin/balance/tool\";\nimport { x402Request } from \"./tools/x402/request/tool\";\nimport { tokenSwap } from \"./tools/token/swap/tool\";\nimport { virtualAccountWalletRegister } from \"./tools/virtual-account/wallet/register/tool\";\nimport schemas from \"./generated/schemas.json\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst LOCAL_TOOLS: Tool<any>[] = [\n walletCreate,\n walletImport,\n walletList,\n walletRetrieve,\n walletDelete,\n transactionSign,\n messageSign,\n bitcoinBalanceRetrieve,\n x402Request,\n tokenSwap,\n virtualAccountWalletRegister,\n];\n\nconst localToolMap = new Map(LOCAL_TOOLS.map((t) => [t.schema.name, t]));\n\n/**\n * Resolve a remote schema's $ref to the actual properties object.\n */\nfunction resolveRemoteSchema(schema: (typeof schemas)[number]) {\n const input = schema.inputSchema as any;\n if (input.$ref && input.definitions) {\n const defName = input.$ref.replace(\"#/definitions/\", \"\");\n return input.definitions[defName] ?? input;\n }\n return input;\n}\n\nexport async function startMcpServer() {\n const server = new Server(\n { name: \"moonpay\", version: \"1.0.0\" },\n { capabilities: { tools: { listChanged: true } } },\n );\n\n // Build tool definitions — local tools use Zod→JSON, remote use JSON directly\n const toolDefs: Array<{\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n }> = LOCAL_TOOLS.map((tool) => ({\n name: tool.schema.name,\n description: tool.schema.description,\n inputSchema: zodToJsonSchema(tool.schema.input) as Record<string, unknown>,\n }));\n\n const remotePropsMap = new Map<string, Record<string, any>>();\n for (const schema of schemas) {\n if (localToolMap.has(schema.name)) continue;\n const resolved = resolveRemoteSchema(schema);\n remotePropsMap.set(\n schema.name,\n (resolved.properties ?? {}) as Record<string, any>,\n );\n toolDefs.push({\n name: schema.name,\n description: schema.description,\n inputSchema: {\n type: \"object\",\n properties: resolved.properties ?? {},\n ...(resolved.required ? { required: resolved.required } : {}),\n ...(resolved.additionalProperties !== undefined\n ? { additionalProperties: resolved.additionalProperties }\n : {}),\n },\n });\n }\n\n const toolDefMap = new Map(toolDefs.map((t) => [t.name, t]));\n\n // tools/list — return raw JSON schemas directly\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: toolDefs.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n }));\n\n // tools/call\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: params = {} } = request.params;\n\n if (!toolDefMap.has(name)) {\n return {\n content: [{ type: \"text\" as const, text: `Unknown tool: ${name}` }],\n isError: true,\n };\n }\n\n try {\n // Local tool — coerce nullable fields then run handler\n const localTool = localToolMap.get(name);\n if (localTool) {\n // Fill missing nullable fields with null (MCP clients may omit them)\n const shape = (localTool.schema.input as z.ZodObject<z.ZodRawShape>).shape ?? {};\n for (const [key, field] of Object.entries(shape)) {\n if (\n params[key] === undefined &&\n (field as z.ZodTypeAny)._def.typeName === \"ZodNullable\"\n ) {\n params[key] = null;\n }\n }\n\n const result = await localTool.handler(params);\n return {\n content: [\n { type: \"text\" as const, text: JSON.stringify(result, null, 2) },\n ],\n };\n }\n\n // Remote tool — coerce types then call API\n const props = remotePropsMap.get(name) ?? {};\n for (const [key, prop] of Object.entries(props)) {\n if (params[key] === undefined) {\n const nullable = Array.isArray(prop.type)\n ? prop.type.includes(\"null\")\n : prop.anyOf?.some((t: any) => t.type === \"null\");\n if (nullable) params[key] = null;\n }\n const isNum =\n prop.type === \"number\" ||\n prop.type === \"integer\" ||\n (Array.isArray(prop.type) &&\n prop.type.some(\n (t: string) => t === \"number\" || t === \"integer\",\n )) ||\n prop.anyOf?.some(\n (t: any) => t.type === \"number\" || t.type === \"integer\",\n );\n if (isNum && typeof params[key] === \"string\") {\n params[key] = Number(params[key]);\n }\n }\n\n const baseUrl = resolveBaseUrl();\n const result = await callTool(baseUrl, name, params);\n return {\n content: [\n { type: \"text\" as const, text: JSON.stringify(result, null, 2) },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: error instanceof Error ? error.message : String(error),\n },\n ],\n isError: true,\n };\n }\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n"],"mappings":"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","names":["result"]}
@@ -0,0 +1,22 @@
1
+ import { createRequire as __createRequire } from "module"; const require = __createRequire(import.meta.url);
2
+ import {
3
+ addWallet,
4
+ findWallet,
5
+ findWalletOrThrow,
6
+ loadWallets,
7
+ mutateWallets,
8
+ removeWallet,
9
+ resolveSigningKey,
10
+ saveWallets
11
+ } from "./chunk-V7MA7WNX.js";
12
+ export {
13
+ addWallet,
14
+ findWallet,
15
+ findWalletOrThrow,
16
+ loadWallets,
17
+ mutateWallets,
18
+ removeWallet,
19
+ resolveSigningKey,
20
+ saveWallets
21
+ };
22
+ //# sourceMappingURL=store-HCN56E6A.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moonpay/cli",
3
- "version": "0.4.3",
3
+ "version": "0.5.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -21,7 +21,8 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@modelcontextprotocol/sdk": "^1.26.0",
24
- "@scure/bip32": "^1.4.0",
24
+ "@noble/hashes": "^1.7.0",
25
+ "@scure/bip32": "^1.4.0",
25
26
  "@scure/bip39": "^1.6.0",
26
27
  "@solana/web3.js": "^1.98.4",
27
28
  "@x402/axios": "^2.4.0",
@@ -35,6 +36,7 @@
35
36
  "ed25519-hd-key": "^1.3.0",
36
37
  "tiny-secp256k1": "^2.2.4",
37
38
  "tweetnacl": "^1.0.3",
39
+ "viem": "^2.46.2",
38
40
  "zod": "^3.25.76",
39
41
  "zod-to-json-schema": "^3.25.1"
40
42
  },
@@ -36,14 +36,17 @@ mp logout
36
36
 
37
37
  ## Local wallet management
38
38
 
39
- The CLI manages local Solana wallets stored in `~/.config/moonpay/wallets/`. Private keys never leave the machinetransactions are signed locally.
39
+ The CLI manages local wallets stored encrypted in `~/.config/moonpay/wallets.json`. Private keys are encrypted with AES-256-GCM using a random key stored in your OS keychain. No password required keys never leave the machine.
40
40
 
41
41
  ```bash
42
- # Create a new wallet
42
+ # Create a new HD wallet (Solana, Ethereum, Bitcoin, Tron)
43
43
  mp wallet create --name "my-wallet"
44
44
 
45
- # Import from a base58 private key
46
- mp wallet import --name "imported" --privateKey <base58-key>
45
+ # Import from a mnemonic (all chains)
46
+ mp wallet import --name "restored" --mnemonic "word1 word2 ..."
47
+
48
+ # Import from a private key (single chain)
49
+ mp wallet import --name "imported" --key <hex-key> --chain ethereum
47
50
 
48
51
  # List all local wallets
49
52
  mp wallet list
@@ -51,6 +54,9 @@ mp wallet list
51
54
  # Get wallet details (by name or address)
52
55
  mp wallet retrieve --wallet "my-wallet"
53
56
 
57
+ # Export mnemonic/key (interactive only — agents cannot run this)
58
+ mp wallet export --wallet "my-wallet"
59
+
54
60
  # Delete a wallet (irreversible)
55
61
  mp wallet delete --wallet "my-wallet" --confirm
56
62
  ```
@@ -64,35 +70,20 @@ mp wallet delete --wallet "my-wallet" --confirm
64
70
 
65
71
  ## Config locations
66
72
 
73
+ - **Wallets:** `~/.config/moonpay/wallets.json` (encrypted, AES-256-GCM)
74
+ - **Encryption key:** OS keychain (`moonpay-cli` / `encryption-key`)
67
75
  - **Credentials:** `~/.config/moonpay/credentials.json` (OAuth tokens)
68
76
  - **Config:** `~/.config/moonpay/config.json` (base URL, client ID)
69
- - **Wallets:** `~/.config/moonpay/wallets/` (local keypairs, 0600 permissions)
70
-
71
- ## Output formats
72
-
73
- ```bash
74
- mp -f json wallet list # Pretty JSON (default)
75
- mp -f compact wallet list # Single-line JSON
76
- mp -f table wallet list # ASCII table
77
- ```
78
-
79
- ## Environment for scripts/agents
80
-
81
- If running `mp` from a script or agent subprocess, ensure PATH includes the npm global bin:
82
-
83
- ```bash
84
- export PATH="$(npm config get prefix)/bin:$PATH"
85
- mp --version
86
- ```
87
77
 
88
- ## Troubleshooting
78
+ ## Security
89
79
 
90
- - **"command not found"** `npm i -g @moonpay/cli` and check PATH.
91
- - **401 / auth errors** Run `mp login`.
92
- - **"fetch failed"**Check network connectivity to agents.moonpay.com.
93
- - Tokens auto-refresh on expiry. If refresh fails, run `mp login` again.
80
+ - Wallet secrets are always encrypted on disk
81
+ - Encryption key is stored in macOS Keychain / Linux libsecret
82
+ - No password to remember the OS handles authentication
83
+ - `wallet export` requires an interactive terminal (TTY) agents and scripts cannot extract secrets
84
+ - 24-word BIP39 mnemonics (256-bit entropy)
94
85
 
95
86
  ## Related skills
96
87
 
97
- - **moonpay-swap-tokens** — Swap tokens using local wallets.
88
+ - **moonpay-swap-tokens** — Swap or bridge tokens using local wallets.
98
89
  - **moonpay-check-wallet** — Check wallet balances.
@@ -44,13 +44,11 @@ Then restart Claude Desktop.
44
44
 
45
45
  All MoonPay CLI tools are available as MCP tools:
46
46
 
47
- - **Wallet management** — create, import, list, retrieve, delete wallets
48
- - **Token operations** — search, retrieve, swap, buy, sell, limit orders, recurring orders
49
- - **Market data** — token prices, trending, top traders, liquidity, holder data
50
- - **DeFi** — lending deposits/withdrawals, yield recommendations
51
- - **Fiat on/off-ramp** — buy crypto, virtual accounts, bank accounts
47
+ - **Wallet management** — create, import, list, retrieve, delete, export wallets
48
+ - **Token operations** — search, retrieve, trending, swap, bridge, transfer
49
+ - **Fiat** — buy crypto with card/bank, virtual accounts with on-ramp
52
50
  - **x402 payments** — paid API requests with automatic payment handling
53
- - **Transaction signing** — sign transactions with local wallets
51
+ - **Transactions** — sign locally, send, list, retrieve
54
52
 
55
53
  ## Verification
56
54
 
@@ -20,82 +20,80 @@ mp wallet list
20
20
  If no wallets exist, create one:
21
21
 
22
22
  ```bash
23
- mp wallet create --name "my-wallet"
23
+ mp wallet create --name "main"
24
24
  ```
25
25
 
26
- Tell the user their email, wallet address, and that this wallet's private key lives locally on their machine.
26
+ Tell the user their email, all wallet addresses (Solana, Ethereum, Bitcoin, Tron), and that keys are encrypted locally with a random key in the OS keychain.
27
27
 
28
28
  ## Mission 2: Recon
29
29
 
30
30
  **Goal:** See what's trending and research a token.
31
31
 
32
- First, check what's hot:
33
-
34
32
  ```bash
35
33
  mp token trending list --chain solana --limit 5 --page 1
36
34
  ```
37
35
 
38
- Present the top trending tokens with price, 24h change, and volume.
39
-
40
- Then ask the user to pick a token to research (or default to SOL):
36
+ Present the top trending tokens. Then ask the user to pick one to research:
41
37
 
42
38
  ```bash
43
39
  mp token search --query "<token>" --chain solana
44
40
  mp token retrieve --token <address-from-search> --chain solana
45
41
  ```
46
42
 
47
- Present: name, symbol, price, 24h change, market cap, liquidity, volume, and trading activity. Flag anything interesting (high volume, thin liquidity, etc).
43
+ Present: name, symbol, price, market cap, liquidity, volume.
48
44
 
49
45
  ## Mission 3: Portfolio Check
50
46
 
51
- **Goal:** See what's in the wallet.
47
+ **Goal:** See what's in the wallet across chains.
52
48
 
53
49
  ```bash
54
- mp token balance list --wallet <address> --chain solana
50
+ mp token balance list --wallet <solana-address> --chain solana
51
+ mp token balance list --wallet <eth-address> --chain ethereum
52
+ mp bitcoin balance retrieve --wallet <btc-address>
55
53
  ```
56
54
 
57
- Present holdings as a mini portfolio report: each token, amount, USD value, and % allocation. Give the total portfolio value.
55
+ Present holdings as a multi-chain portfolio report with USD values and total.
58
56
 
59
57
  ## Mission 4: Swap
60
58
 
61
- **Goal:** Build a swap transaction, sign it locally, and show the quote.
59
+ **Goal:** Execute a swap end-to-end.
62
60
 
63
- Pick a small swap based on what the wallet holds (e.g. 0.01 USDC → SOL). If the wallet has no USDC, pick any token pair that makes sense.
61
+ Pick a small swap based on what the wallet holds:
64
62
 
65
63
  ```bash
66
- mp token swap build --input <input-mint> --output <output-mint> --amount <small-amount> --wallet <address>
64
+ mp token swap \
65
+ --wallet main --chain solana \
66
+ --from-token So11111111111111111111111111111111111111111 \
67
+ --from-amount 0.01 \
68
+ --to-token EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
67
69
  ```
68
70
 
69
- Show the quote from the `message` field. Ask the user: **"Want to execute this swap?"**
70
-
71
- If yes:
72
-
73
- ```bash
74
- mp transaction sign --transaction <tx> --wallet <address>
75
- mp token swap execute --transaction <signed-tx> --requestId <id>
76
- ```
71
+ This builds, signs locally, and broadcasts in one step. Show the result and explain: the transaction was built on the server, signed locally (key never left the machine), and submitted on-chain.
77
72
 
78
- If no, move on. Either way, explain what just happened: the transaction was built on the server, signed locally (key never left the machine), and submitted on-chain.
73
+ Three commands for moving tokens:
74
+ - **`mp token swap`** — same chain, different tokens (e.g. SOL → USDC)
75
+ - **`mp token bridge`** — cross chain (e.g. ETH on Ethereum → USDC on Polygon)
76
+ - **`mp token transfer`** — same chain, same token, different wallet (e.g. send USDC to a friend)
79
77
 
80
78
  ## Mission 5: Buy with Fiat
81
79
 
82
- **Goal:** Generate a fiat checkout link and open it.
80
+ **Goal:** Generate a fiat checkout link.
83
81
 
84
82
  ```bash
85
- mp buy --token sol --amount 1 --wallet <address> --email <email-from-mission-1>
83
+ mp buy --token sol --amount 1 --wallet <solana-address> --email <email>
86
84
  ```
87
85
 
88
- Present the checkout URL and open it in the user's browser. Explain: this is MoonPay's fiat gateway where they can buy crypto with a card or bank transfer. Tokens get sent directly to their wallet.
86
+ Open the checkout URL. Explain: MoonPay's fiat gateway for buying crypto with card or bank transfer.
89
87
 
90
88
  ## Mission 6: Message Signing
91
89
 
92
90
  **Goal:** Sign a message to prove wallet ownership.
93
91
 
94
92
  ```bash
95
- mp message sign --wallet <address> --message "I own this wallet"
93
+ mp message sign --wallet main --chain solana --message "I own this wallet"
96
94
  ```
97
95
 
98
- Present the signature. Explain: this is used for wallet verification (e.g. registering with virtual accounts, proving ownership to dApps).
96
+ Present the signature. Explain: used for wallet verification (e.g. registering with virtual accounts, proving ownership to dApps).
99
97
 
100
98
  ## Mission 7: Virtual Account (optional)
101
99
 
@@ -105,28 +103,28 @@ Present the signature. Explain: this is used for wallet verification (e.g. regis
105
103
  mp virtual-account retrieve
106
104
  ```
107
105
 
108
- If they have one, show the status and next step. If not, explain what a virtual account is (KYC-verified fiat bridge) and that they can set one up with `mp virtual-account create`.
106
+ If they have one, show the status and next step. If not, explain what it is and that they can set one up with `mp virtual-account create`.
109
107
 
110
108
  ## Mission 8: Skills
111
109
 
112
- **Goal:** Show the user what skills are available.
110
+ **Goal:** Show what skills are available.
113
111
 
114
112
  ```bash
115
113
  mp skill list
116
114
  ```
117
115
 
118
- Explain: skills are guides that teach agents how to use the CLI. They can install them for Claude Code with `mp skill install`.
116
+ Explain: skills are guides that teach agents how to use the CLI. Install them with `mp skill install`.
119
117
 
120
118
  ## Debrief
121
119
 
122
- Summarize everything the user just did:
123
- - Authenticated and set up a wallet
120
+ Summarize everything:
121
+ - Set up a multi-chain HD wallet (encrypted, OS keychain secured)
124
122
  - Searched and analyzed tokens
125
- - Checked portfolio
126
- - Built and signed a swap (and maybe executed it)
123
+ - Checked portfolio across Solana, Ethereum, Bitcoin
124
+ - Executed a swap (built, signed locally, broadcast)
127
125
  - Generated a fiat buy link
128
- - Signed a message
126
+ - Signed a message for verification
129
127
  - Explored virtual accounts
130
128
  - Discovered skills
131
129
 
132
- End with: "You're oriented. Run `mp --help` to see all commands, or ask me anything."
130
+ End with: "You're all set. Run `mp --help` to see all commands, or ask me anything."
@@ -1,73 +1,106 @@
1
1
  ---
2
2
  name: moonpay-swap-tokens
3
- description: Swap tokens using the build, sign, execute flow. Use when the user wants to swap token A for token B on Solana.
3
+ description: Swap tokens on the same chain or bridge tokens across chains. Use when the user wants to swap, bridge, or move tokens.
4
4
  tags: [trading]
5
5
  ---
6
6
 
7
- # Swap tokens
7
+ # Swap or bridge tokens
8
8
 
9
9
  ## Goal
10
10
 
11
- Swap tokens on Solana using the three-step flow: build an unsigned transaction, sign it locally with a wallet, then execute it on-chain.
11
+ Swap tokens on the same chain, or bridge tokens across chains. Two commands:
12
12
 
13
- ## Tools
13
+ - **`mp token swap`** — same chain, different tokens
14
+ - **`mp token bridge`** — cross chain
14
15
 
15
- - `token swap build` Build an unsigned swap transaction (returns base64 tx + quote)
16
- - `transaction sign` — Sign the transaction with a local wallet
17
- - `token swap execute` — Submit the signed transaction on-chain
16
+ Both build via swaps.xyz, sign locally, broadcast, and register for tracking.
18
17
 
19
- ## Workflow
18
+ ## Commands
20
19
 
21
- ### 1. Build the swap transaction
20
+ ### Swap (same chain)
22
21
 
23
22
  ```bash
24
- mp token swap build \
25
- --input <input-token-mint> \
26
- --output <output-token-mint> \
27
- --amount <amount-of-input-token> \
28
- --wallet <wallet-address>
23
+ mp token swap \
24
+ --wallet <wallet-name> \
25
+ --chain <chain> \
26
+ --from-token <token-address> \
27
+ --from-amount <amount> \
28
+ --to-token <token-address>
29
29
  ```
30
30
 
31
- This returns a `transaction` (base64), `message` (human-readable quote), and `requestId`.
31
+ Supports exact-out: use `--to-amount` instead of `--from-amount`.
32
32
 
33
- ### 2. Sign the transaction locally
33
+ ### Bridge (cross chain)
34
34
 
35
35
  ```bash
36
- mp transaction sign \
37
- --transaction <base64-transaction> \
38
- --wallet <wallet-address>
36
+ mp token bridge \
37
+ --from-wallet <wallet-name> \
38
+ --from-chain <chain> \
39
+ --from-token <token-address> \
40
+ --from-amount <amount> \
41
+ --to-chain <chain> \
42
+ --to-token <token-address> \
43
+ --to-wallet <wallet-name> # optional, defaults to from-wallet
39
44
  ```
40
45
 
41
- This returns a signed `transaction` (base64). The private key never leaves the machine.
46
+ Supports exact-out: use `--to-amount` instead of `--from-amount`.
42
47
 
43
- ### 3. Execute the swap
48
+ ## Examples
49
+
50
+ ### Same-chain swap (SOL → USDC on Solana)
51
+
52
+ ```bash
53
+ mp token swap \
54
+ --wallet main --chain solana \
55
+ --from-token So11111111111111111111111111111111111111111 \
56
+ --from-amount 0.1 \
57
+ --to-token EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
58
+ ```
59
+
60
+ ### Cross-chain bridge (ETH → USDC.e on Polygon)
44
61
 
45
62
  ```bash
46
- mp token swap execute \
47
- --transaction <signed-base64-transaction> \
48
- --requestId <request-id-from-build>
63
+ mp token bridge \
64
+ --from-wallet funded --from-chain ethereum \
65
+ --from-token 0x0000000000000000000000000000000000000000 \
66
+ --from-amount 0.003 \
67
+ --to-chain polygon \
68
+ --to-token 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174
49
69
  ```
50
70
 
51
- This returns a `signature` (the on-chain transaction hash).
71
+ ### ERC20 swap (auto-approves if needed)
72
+
73
+ ```bash
74
+ mp token swap \
75
+ --wallet funded --chain polygon \
76
+ --from-token 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174 \
77
+ --from-amount 5 \
78
+ --to-token 0x0000000000000000000000000000000000000000
79
+ ```
80
+
81
+ ## Supported chains
82
+
83
+ solana, ethereum, base, polygon, arbitrum, optimism, bnb, avalanche, bitcoin (bridges only)
84
+
85
+ ## How it works
52
86
 
53
- ## Example flow
87
+ 1. Resolves wallet name → address
88
+ 2. Builds unsigned transaction via swaps.xyz (handles decimal conversion)
89
+ 3. If ERC20 token needs approval, sends an approve transaction first, then re-builds
90
+ 4. Signs locally — private key never leaves the machine
91
+ 5. Broadcasts to the network
92
+ 6. Registers for tracking
54
93
 
55
- 1. User: "Swap 1 USDC for WBTC"
56
- 2. Resolve token addresses via `mp token search --query "USDC" --chain solana`.
57
- 3. Build: `mp token swap build --input EPjF...Dt1v --output 3NZ9...cmJh --amount 1 --wallet <addr>`
58
- 4. Show the quote from the `message` field. Ask for confirmation.
59
- 5. Sign: `mp transaction sign --transaction <tx> --wallet <addr>`
60
- 6. Execute: `mp token swap execute --transaction <signed-tx> --requestId <id>`
61
- 7. Return the transaction signature.
94
+ `token swap` calls `token bridge` under the hood with `from-chain` = `to-chain`.
62
95
 
63
- ## Notes
96
+ ## Tips
64
97
 
65
- - If the user provides token names/symbols, resolve to addresses with `mp token search`.
66
- - Always show the quote and ask for confirmation before signing.
67
- - The wallet must be a local wallet (see `mp wallet list`).
98
+ - If the user provides token names/symbols, resolve to addresses with `mp token search --query "USDC" --chain solana`
99
+ - Check balances first with `mp token balance list --wallet <address> --chain <chain>`
100
+ - Native tokens use `0x0000000000000000000000000000000000000000` (EVM) or `So11111111111111111111111111111111111111111` (Solana)
68
101
 
69
102
  ## Related skills
70
103
 
71
- - **moonpay-discover-tokens** — Search for token addresses.
72
- - **moonpay-check-wallet** — Check balances before swapping.
73
- - **moonpay-auth** — Set up wallets for signing.
104
+ - **moonpay-discover-tokens** — Search for token addresses
105
+ - **moonpay-check-wallet** — Check balances before swapping
106
+ - **moonpay-auth** — Set up wallets for signing