@clawswap/mcp-server 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 ClawSwap
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # @clawswap/mcp-server
2
+
3
+ MCP server for gasless cross-chain bridges between Solana and Base via [ClawSwap](https://clawswap.dev).
4
+
5
+ One tool call bridges tokens end-to-end: quote → payment → signing → submission → settlement.
6
+
7
+ ## Quick Start
8
+
9
+ ### Claude Desktop
10
+
11
+ Add to your `claude_desktop_config.json`:
12
+
13
+ ```json
14
+ {
15
+ "mcpServers": {
16
+ "clawswap": {
17
+ "command": "npx",
18
+ "args": ["-y", "@clawswap/mcp-server"],
19
+ "env": {
20
+ "SOLANA_PRIVATE_KEY": "your-base58-solana-private-key",
21
+ "BASE_PRIVATE_KEY": "0x-your-hex-base-private-key"
22
+ }
23
+ }
24
+ }
25
+ }
26
+ ```
27
+
28
+ ### Claude Code
29
+
30
+ ```bash
31
+ claude mcp add clawswap -- npx -y @clawswap/mcp-server
32
+ ```
33
+
34
+ Then set environment variables for the MCP server:
35
+
36
+ ```bash
37
+ export SOLANA_PRIVATE_KEY="your-base58-solana-private-key"
38
+ export BASE_PRIVATE_KEY="0x-your-hex-base-private-key"
39
+ ```
40
+
41
+ ## Tools
42
+
43
+ ### `clawswap_bridge`
44
+
45
+ Bridge tokens between Solana and Base. Handles the entire flow automatically.
46
+
47
+ | Parameter | Type | Required | Description |
48
+ |-----------|------|----------|-------------|
49
+ | `amount` | string | yes | Human-readable amount, e.g. "100", "0.5" |
50
+ | `token` | string | yes | Token symbol: USDC, USDT, SOL, ETH, WETH |
51
+ | `from` | "solana" \| "base" | yes | Source chain |
52
+ | `to` | "solana" \| "base" | yes | Destination chain |
53
+ | `recipient` | string | no | Destination address (defaults to agent's own) |
54
+ | `destinationToken` | string | no | Destination token (defaults to same or USDC) |
55
+ | `slippage` | number | no | Slippage tolerance 0-1 (default 0.01) |
56
+
57
+ ### `clawswap_quote`
58
+
59
+ Preview a bridge quote without executing. Free, no payment required.
60
+
61
+ Same parameters as `clawswap_bridge` minus `recipient`.
62
+
63
+ ### `clawswap_status`
64
+
65
+ Check the status of a previously initiated bridge.
66
+
67
+ | Parameter | Type | Required | Description |
68
+ |-----------|------|----------|-------------|
69
+ | `requestId` | string | yes | The requestId from `clawswap_bridge` |
70
+
71
+ ## Environment Variables
72
+
73
+ | Variable | Required | Description |
74
+ |----------|----------|-------------|
75
+ | `SOLANA_PRIVATE_KEY` | For Solana→Base | Base58-encoded Solana private key |
76
+ | `BASE_PRIVATE_KEY` | For Base→Solana | 0x-prefixed hex Base/EVM private key |
77
+ | `SOLANA_RPC_URL` | No | Solana RPC (default: `https://api.mainnet-beta.solana.com`) |
78
+ | `BASE_RPC_URL` | No | Base RPC (default: `https://mainnet.base.org`) |
79
+
80
+ You only need keys for the direction(s) you want to bridge. The server starts with whatever is configured.
81
+
82
+ ## Fees
83
+
84
+ - **Solana → Base**: $0.50 USDC (paid automatically via x402 protocol, gas sponsored by ClawSwap)
85
+ - **Base → Solana**: Free (agent pays ~$0.001 Base gas directly)
86
+
87
+ ## Supported Tokens
88
+
89
+ - **Solana**: USDC, USDT, SOL
90
+ - **Base**: USDC, USDT, ETH, WETH
91
+
92
+ ## License
93
+
94
+ MIT
@@ -0,0 +1,13 @@
1
+ /**
2
+ * ClawSwapClient factory.
3
+ * Creates a single SDK client instance, optionally wrapped with x402 payment.
4
+ * The x402 wrapper is a no-op for non-402 responses, so one client handles everything.
5
+ */
6
+ import { ClawSwapClient } from '@clawswap/sdk';
7
+ import type { Config } from './config.js';
8
+ /**
9
+ * Create a ClawSwapClient with x402 payment wrapping (if Solana key is available).
10
+ * If no Solana key, returns a plain client — Solana-source executeSwap will fail
11
+ * with PaymentRequiredError, which the error handler turns into a clear message.
12
+ */
13
+ export declare function createClient(config: Config): Promise<ClawSwapClient>;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * ClawSwapClient factory.
3
+ * Creates a single SDK client instance, optionally wrapped with x402 payment.
4
+ * The x402 wrapper is a no-op for non-402 responses, so one client handles everything.
5
+ */
6
+ import { ClawSwapClient } from '@clawswap/sdk';
7
+ /**
8
+ * Create a ClawSwapClient with x402 payment wrapping (if Solana key is available).
9
+ * If no Solana key, returns a plain client — Solana-source executeSwap will fail
10
+ * with PaymentRequiredError, which the error handler turns into a clear message.
11
+ */
12
+ export async function createClient(config) {
13
+ // If Solana key is available, wrap fetch with x402 payment
14
+ if (config.solana.privateKey) {
15
+ try {
16
+ const { x402Client } = await import('@x402/core/client');
17
+ const { registerExactSvmScheme } = await import('@x402/svm/exact/client');
18
+ const { wrapFetchWithPayment } = await import('@x402/fetch');
19
+ const { createKeyPairSignerFromBytes } = await import('@solana/signers');
20
+ const bs58 = await import('bs58');
21
+ const secretKey = bs58.default.decode(config.solana.privateKey);
22
+ const signer = await createKeyPairSignerFromBytes(secretKey);
23
+ const client = new x402Client();
24
+ registerExactSvmScheme(client, {
25
+ signer,
26
+ networks: ['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'],
27
+ x402Versions: [2, 1],
28
+ });
29
+ const fetchWithPayment = wrapFetchWithPayment(fetch, client);
30
+ console.error('x402 payment configured (Solana SVM scheme)');
31
+ console.error(` API URL: ${config.apiUrl}`);
32
+ return new ClawSwapClient({ fetch: fetchWithPayment, baseUrl: config.apiUrl });
33
+ }
34
+ catch (err) {
35
+ console.error(`Warning: Failed to setup x402 payment: ${err instanceof Error ? err.message : err}`);
36
+ console.error('Falling back to plain client (Solana-source executeSwap will fail)');
37
+ return new ClawSwapClient({ baseUrl: config.apiUrl });
38
+ }
39
+ }
40
+ // No Solana key — plain client
41
+ console.error(` API URL: ${config.apiUrl}`);
42
+ return new ClawSwapClient({ baseUrl: config.apiUrl });
43
+ }
44
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAc;IAC/C,2DAA2D;IAC3D,IAAI,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;YACzD,MAAM,EAAE,sBAAsB,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;YAC1E,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;YAC7D,MAAM,EAAE,4BAA4B,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;YACzE,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;YAElC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAChE,MAAM,MAAM,GAAG,MAAM,4BAA4B,CAAC,SAAS,CAAC,CAAC;YAE7D,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAChC,sBAAsB,CAAC,MAAM,EAAE;gBAC7B,MAAM;gBACN,QAAQ,EAAE,CAAC,yCAAyC,CAAC;gBACrD,YAAY,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;aACd,CAAC,CAAC;YAEV,MAAM,gBAAgB,GAAG,oBAAoB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAE7D,OAAO,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;YAC7D,OAAO,CAAC,KAAK,CAAC,cAAc,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;YAC7C,OAAO,IAAI,cAAc,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QACjF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,0CAA0C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YACpG,OAAO,CAAC,KAAK,CAAC,oEAAoE,CAAC,CAAC;YACpF,OAAO,IAAI,cAAc,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED,+BAA+B;IAC/B,OAAO,CAAC,KAAK,CAAC,cAAc,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7C,OAAO,IAAI,cAAc,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;AACxD,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Environment variable loading and validation.
3
+ * Reads wallet keys and RPC URLs from process.env at startup.
4
+ * Missing keys are non-fatal — the server works for the configured direction(s).
5
+ */
6
+ export interface Config {
7
+ solana: {
8
+ privateKey: string | null;
9
+ rpcUrl: string;
10
+ address: string | null;
11
+ };
12
+ base: {
13
+ privateKey: string | null;
14
+ rpcUrl: string;
15
+ address: string | null;
16
+ };
17
+ apiUrl: string;
18
+ debug: boolean;
19
+ }
20
+ /**
21
+ * Load config from environment variables.
22
+ * Derives wallet addresses from private keys.
23
+ * Logs warnings to stderr for missing keys.
24
+ */
25
+ export declare function loadConfig(): Promise<Config>;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Environment variable loading and validation.
3
+ * Reads wallet keys and RPC URLs from process.env at startup.
4
+ * Missing keys are non-fatal — the server works for the configured direction(s).
5
+ */
6
+ /**
7
+ * Load config from environment variables.
8
+ * Derives wallet addresses from private keys.
9
+ * Logs warnings to stderr for missing keys.
10
+ */
11
+ export async function loadConfig() {
12
+ const solanaKey = process.env.SOLANA_PRIVATE_KEY || null;
13
+ const baseKey = process.env.BASE_PRIVATE_KEY || null;
14
+ const debug = process.env.MCP_DEBUG === 'true';
15
+ let solanaAddress = null;
16
+ let baseAddress = null;
17
+ // Derive Solana address from private key using @solana/signers
18
+ if (solanaKey) {
19
+ try {
20
+ const bs58 = await import('bs58');
21
+ const { createKeyPairSignerFromBytes } = await import('@solana/signers');
22
+ const secretKey = bs58.default.decode(solanaKey);
23
+ const signer = await createKeyPairSignerFromBytes(secretKey);
24
+ solanaAddress = signer.address;
25
+ }
26
+ catch (err) {
27
+ console.error(`Warning: Invalid SOLANA_PRIVATE_KEY format: ${err instanceof Error ? err.message : err}`);
28
+ }
29
+ }
30
+ else {
31
+ console.error('Warning: SOLANA_PRIVATE_KEY not set — Solana-to-Base bridging disabled');
32
+ }
33
+ // Derive Base address from private key
34
+ if (baseKey) {
35
+ try {
36
+ if (!baseKey.startsWith('0x') || baseKey.length !== 66) {
37
+ throw new Error('Must be 0x-prefixed hex, 66 characters');
38
+ }
39
+ const { privateKeyToAccount } = await import('viem/accounts');
40
+ const account = privateKeyToAccount(baseKey);
41
+ baseAddress = account.address;
42
+ }
43
+ catch (err) {
44
+ console.error(`Warning: Invalid BASE_PRIVATE_KEY format: ${err instanceof Error ? err.message : err}`);
45
+ }
46
+ }
47
+ else {
48
+ console.error('Warning: BASE_PRIVATE_KEY not set — Base-to-Solana bridging disabled');
49
+ }
50
+ const apiUrl = process.env.CLAWSWAP_API_URL || 'https://api.clawswap.dev';
51
+ return {
52
+ solana: {
53
+ privateKey: solanaKey,
54
+ rpcUrl: process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com',
55
+ address: solanaAddress,
56
+ },
57
+ base: {
58
+ privateKey: baseKey,
59
+ rpcUrl: process.env.BASE_RPC_URL || 'https://mainnet.base.org',
60
+ address: baseAddress,
61
+ },
62
+ apiUrl,
63
+ debug,
64
+ };
65
+ }
66
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAiBH;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,IAAI,CAAC;IACzD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,IAAI,CAAC;IACrD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,KAAK,MAAM,CAAC;IAE/C,IAAI,aAAa,GAAkB,IAAI,CAAC;IACxC,IAAI,WAAW,GAAkB,IAAI,CAAC;IAEtC,+DAA+D;IAC/D,IAAI,SAAS,EAAE,CAAC;QACd,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;YAClC,MAAM,EAAE,4BAA4B,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;YACzE,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACjD,MAAM,MAAM,GAAG,MAAM,4BAA4B,CAAC,SAAS,CAAC,CAAC;YAC7D,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC;QACjC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,+CAA+C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAC3G,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,wEAAwE,CAAC,CAAC;IAC1F,CAAC;IAED,uCAAuC;IACvC,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;gBACvD,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;YAC5D,CAAC;YACD,MAAM,EAAE,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;YAC9D,MAAM,OAAO,GAAG,mBAAmB,CAAC,OAAwB,CAAC,CAAC;YAC9D,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;QAChC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,6CAA6C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACzG,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,sEAAsE,CAAC,CAAC;IACxF,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,0BAA0B,CAAC;IAE1E,OAAO;QACL,MAAM,EAAE;YACN,UAAU,EAAE,SAAS;YACrB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,qCAAqC;YAC3E,OAAO,EAAE,aAAa;SACvB;QACD,IAAI,EAAE;YACJ,UAAU,EAAE,OAAO;YACnB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,0BAA0B;YAC9D,OAAO,EAAE,WAAW;SACrB;QACD,MAAM;QACN,KAAK;KACN,CAAC;AACJ,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Agent-friendly error formatting.
3
+ * Translates internal errors into structured JSON that agents can relay to users.
4
+ * Tool handlers never throw — they always return structured responses.
5
+ */
6
+ export interface ToolSuccess {
7
+ status: 'completed' | 'pending';
8
+ summary: string;
9
+ details: Record<string, unknown>;
10
+ }
11
+ export interface ToolError {
12
+ status: 'failed';
13
+ error: string;
14
+ message: string;
15
+ suggestion?: string;
16
+ }
17
+ export type ToolResult = ToolSuccess | ToolError;
18
+ /**
19
+ * Wrap a ToolResult into the MCP tool response format.
20
+ */
21
+ export declare function formatToolResult(result: ToolResult): {
22
+ content: Array<{
23
+ type: 'text';
24
+ text: string;
25
+ }>;
26
+ };
27
+ /**
28
+ * Convert any error into an agent-friendly ToolError.
29
+ */
30
+ export declare function formatError(error: unknown, context?: {
31
+ requestId?: string;
32
+ }): ToolResult;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Agent-friendly error formatting.
3
+ * Translates internal errors into structured JSON that agents can relay to users.
4
+ * Tool handlers never throw — they always return structured responses.
5
+ */
6
+ import { ClawSwapError, TimeoutError } from '@clawswap/sdk';
7
+ /**
8
+ * Wrap a ToolResult into the MCP tool response format.
9
+ */
10
+ export function formatToolResult(result) {
11
+ return {
12
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
13
+ };
14
+ }
15
+ /**
16
+ * Convert any error into an agent-friendly ToolError.
17
+ */
18
+ export function formatError(error, context) {
19
+ // Always log the full error to stderr for debugging
20
+ if (error instanceof ClawSwapError) {
21
+ console.error(` SDK error [${error.code}]: ${error.message}`);
22
+ }
23
+ else if (error instanceof Error) {
24
+ console.error(` Error: ${error.message}`);
25
+ }
26
+ else {
27
+ console.error(` Unknown error: ${String(error)}`);
28
+ }
29
+ // SDK timeout — return pending, not failed
30
+ if (error instanceof TimeoutError && context?.requestId) {
31
+ return {
32
+ status: 'pending',
33
+ summary: `Bridge submitted but not yet confirmed after timeout. Check back with requestId.`,
34
+ details: {
35
+ requestId: context.requestId,
36
+ suggestion: 'Use clawswap_status to check on the bridge progress.',
37
+ },
38
+ };
39
+ }
40
+ // SDK typed errors — pass through code, message, suggestion
41
+ if (error instanceof ClawSwapError) {
42
+ return {
43
+ status: 'failed',
44
+ error: error.code,
45
+ message: error.message,
46
+ suggestion: error.suggestion,
47
+ };
48
+ }
49
+ // Standard errors
50
+ if (error instanceof Error) {
51
+ // Solana transaction failures with logs
52
+ const anyErr = error;
53
+ if (anyErr.logs && Array.isArray(anyErr.logs)) {
54
+ return {
55
+ status: 'failed',
56
+ error: 'TX_FAILED',
57
+ message: `Solana transaction failed: ${error.message}`,
58
+ suggestion: 'Check that your wallet has sufficient balance and the transaction parameters are correct.',
59
+ };
60
+ }
61
+ // viem transaction reverts
62
+ if (anyErr.shortMessage) {
63
+ return {
64
+ status: 'failed',
65
+ error: 'TX_FAILED',
66
+ message: `Base transaction failed: ${anyErr.shortMessage}`,
67
+ suggestion: 'Check that your wallet has sufficient token balance and ETH for gas on Base.',
68
+ };
69
+ }
70
+ return {
71
+ status: 'failed',
72
+ error: 'INTERNAL_ERROR',
73
+ message: error.message,
74
+ suggestion: 'Try again. If the problem persists, check your configuration.',
75
+ };
76
+ }
77
+ return {
78
+ status: 'failed',
79
+ error: 'INTERNAL_ERROR',
80
+ message: String(error),
81
+ suggestion: 'An unexpected error occurred. Try again.',
82
+ };
83
+ }
84
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAiB5D;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAkB;IACjD,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;KACnE,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc,EAAE,OAAgC;IAC1E,oDAAoD;IACpD,IAAI,KAAK,YAAY,aAAa,EAAE,CAAC;QACnC,OAAO,CAAC,KAAK,CAAC,gBAAgB,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACjE,CAAC;SAAM,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,YAAY,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7C,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,oBAAoB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,2CAA2C;IAC3C,IAAI,KAAK,YAAY,YAAY,IAAI,OAAO,EAAE,SAAS,EAAE,CAAC;QACxD,OAAO;YACL,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,kFAAkF;YAC3F,OAAO,EAAE;gBACP,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,UAAU,EAAE,sDAAsD;aACnE;SACF,CAAC;IACJ,CAAC;IAED,4DAA4D;IAC5D,IAAI,KAAK,YAAY,aAAa,EAAE,CAAC;QACnC,OAAO;YACL,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,KAAK,CAAC,IAAI;YACjB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,UAAU,EAAE,KAAK,CAAC,UAAU;SAC7B,CAAC;IACJ,CAAC;IAED,kBAAkB;IAClB,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,wCAAwC;QACxC,MAAM,MAAM,GAAG,KAAY,CAAC;QAC5B,IAAI,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9C,OAAO;gBACL,MAAM,EAAE,QAAQ;gBAChB,KAAK,EAAE,WAAW;gBAClB,OAAO,EAAE,8BAA8B,KAAK,CAAC,OAAO,EAAE;gBACtD,UAAU,EAAE,2FAA2F;aACxG,CAAC;QACJ,CAAC;QAED,2BAA2B;QAC3B,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACxB,OAAO;gBACL,MAAM,EAAE,QAAQ;gBAChB,KAAK,EAAE,WAAW;gBAClB,OAAO,EAAE,4BAA4B,MAAM,CAAC,YAAY,EAAE;gBAC1D,UAAU,EAAE,8EAA8E;aAC3F,CAAC;QACJ,CAAC;QAED,OAAO;YACL,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,gBAAgB;YACvB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,UAAU,EAAE,+DAA+D;SAC5E,CAAC;IACJ,CAAC;IAED,OAAO;QACL,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,gBAAgB;QACvB,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC;QACtB,UAAU,EAAE,0CAA0C;KACvD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ClawSwap MCP Server entry point.
4
+ * Creates the MCP server, registers tools, and connects stdio transport.
5
+ *
6
+ * CRITICAL: stdout is exclusively for MCP JSON-RPC messages.
7
+ * All logging goes to stderr via console.error().
8
+ */
9
+ export {};
package/build/index.js ADDED
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ClawSwap MCP Server entry point.
4
+ * Creates the MCP server, registers tools, and connects stdio transport.
5
+ *
6
+ * CRITICAL: stdout is exclusively for MCP JSON-RPC messages.
7
+ * All logging goes to stderr via console.error().
8
+ */
9
+ // Safety: redirect any accidental console.log to stderr
10
+ const _originalLog = console.log;
11
+ console.log = (...args) => console.error('[LOG]', ...args);
12
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
13
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
14
+ import { z } from 'zod';
15
+ import { loadConfig } from './config.js';
16
+ import { createClient } from './client.js';
17
+ import { createSolanaWallet } from './wallet/solana.js';
18
+ import { createEvmWallet } from './wallet/evm.js';
19
+ import { createBridgeHandler } from './tools/bridge.js';
20
+ import { createQuoteHandler } from './tools/quote.js';
21
+ import { createStatusHandler } from './tools/status.js';
22
+ import { formatToolResult } from './errors.js';
23
+ const SERVER_INSTRUCTIONS = `ClawSwap bridges tokens between Solana and Base. Use clawswap_bridge to execute a bridge — it handles the entire flow including payment, signing, and submission. Use clawswap_quote first if the user wants to preview costs before committing. Use clawswap_status to check on an in-progress bridge.
24
+
25
+ The agent's wallets are pre-configured. No addresses need to be specified unless sending to a different recipient.
26
+
27
+ Supported tokens — Solana: USDC, USDT, SOL. Base: ETH, USDC, USDT, WETH. More tokens may work; try them and the error will tell you.
28
+
29
+ Solana → Base: $0.50 USDC fee (paid automatically via x402, gas sponsored by ClawSwap).
30
+ Base → Solana: Free (agent pays ~$0.001 Base gas directly).`;
31
+ async function main() {
32
+ // 1. Load config from environment
33
+ const config = await loadConfig();
34
+ // 2. Create SDK client (with x402 wrapping if Solana key available)
35
+ const client = await createClient(config);
36
+ // 3. Create wallets (nullable if not configured)
37
+ const solanaWallet = config.solana.privateKey
38
+ ? await createSolanaWallet(config.solana.privateKey, config.solana.rpcUrl)
39
+ : null;
40
+ const evmWallet = config.base.privateKey
41
+ ? createEvmWallet(config.base.privateKey, config.base.rpcUrl)
42
+ : null;
43
+ // 4. Create tool handlers
44
+ const bridgeHandler = createBridgeHandler(client, config, solanaWallet, evmWallet);
45
+ const quoteHandler = createQuoteHandler(client, config);
46
+ const statusHandler = createStatusHandler(client);
47
+ // 5. Create MCP server
48
+ const server = new McpServer({ name: 'clawswap', version: '1.0.0' }, { instructions: SERVER_INSTRUCTIONS });
49
+ // 6. Register tools
50
+ server.tool('clawswap_bridge', 'Bridge tokens between Solana and Base. Handles quoting, payment, signing, and submission automatically. Returns the final result when complete.', {
51
+ amount: z.string().describe("Human-readable amount to bridge, e.g. '100', '0.5'. NOT base units."),
52
+ token: z.string().describe("Token symbol on the source chain: USDC, USDT, SOL, ETH, WETH"),
53
+ from: z.enum(['solana', 'base']).describe("Source chain"),
54
+ to: z.enum(['solana', 'base']).describe("Destination chain"),
55
+ recipient: z.string().describe("Destination wallet address to receive the bridged tokens."),
56
+ destinationToken: z.string().optional().describe("Destination token symbol. Defaults to same token or USDC."),
57
+ slippage: z.number().min(0).max(1).optional().describe("Slippage tolerance 0-1. Default 0.01 (1%)."),
58
+ }, async (params) => formatToolResult(await bridgeHandler(params)));
59
+ server.tool('clawswap_quote', 'Preview a bridge quote without executing. Shows estimated output, fees, and time. Free, no payment required.', {
60
+ amount: z.string().describe("Human-readable amount, e.g. '100', '0.5'"),
61
+ token: z.string().describe("Token symbol on the source chain"),
62
+ from: z.enum(['solana', 'base']).describe("Source chain"),
63
+ to: z.enum(['solana', 'base']).describe("Destination chain"),
64
+ destinationToken: z.string().optional().describe("Destination token symbol. Defaults to same token or USDC."),
65
+ slippage: z.number().min(0).max(1).optional().describe("Slippage tolerance 0-1. Default 0.01 (1%)."),
66
+ }, async (params) => formatToolResult(await quoteHandler(params)));
67
+ server.tool('clawswap_status', 'Check the status of a previously initiated bridge using the requestId from clawswap_bridge.', {
68
+ requestId: z.string().describe("The requestId returned by clawswap_bridge"),
69
+ }, async (params) => formatToolResult(await statusHandler(params)));
70
+ // 7. Connect stdio transport
71
+ const transport = new StdioServerTransport();
72
+ await server.connect(transport);
73
+ // Log startup info to stderr
74
+ console.error('ClawSwap MCP server started');
75
+ console.error(` Solana wallet: ${solanaWallet ? solanaWallet.publicKey : 'not configured'}`);
76
+ console.error(` Base wallet: ${evmWallet ? evmWallet.address : 'not configured'}`);
77
+ }
78
+ main().catch((err) => {
79
+ console.error('Failed to start ClawSwap MCP server:', err);
80
+ process.exit(1);
81
+ });
82
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Static token map and amount conversion utilities.
3
+ * Maps human-readable token symbols to chain-specific addresses and decimals.
4
+ */
5
+ export type Chain = 'solana' | 'base';
6
+ export interface TokenInfo {
7
+ address: string;
8
+ decimals: number;
9
+ symbol: string;
10
+ }
11
+ /**
12
+ * Resolve a token symbol to its address and decimals on a given chain.
13
+ * Case-insensitive matching.
14
+ */
15
+ export declare function resolveToken(chain: Chain, symbol: string): TokenInfo;
16
+ /**
17
+ * Infer the destination token for a bridge.
18
+ * - If the same symbol exists on the destination chain, use it (USDC→USDC)
19
+ * - Otherwise, default to USDC on the destination chain
20
+ */
21
+ export declare function inferDestinationToken(sourceSymbol: string, destChain: Chain): TokenInfo;
22
+ /**
23
+ * Convert a human-readable amount to base units (smallest denomination).
24
+ * Uses string math to avoid floating-point drift.
25
+ * e.g., toBaseUnits("100", 6) → "100000000"
26
+ * toBaseUnits("0.5", 6) → "500000"
27
+ */
28
+ export declare function toBaseUnits(amount: string, decimals: number): string;
29
+ /**
30
+ * Convert base units to a human-readable formatted amount.
31
+ * e.g., fromBaseUnits("100000000", 6) → "100"
32
+ * fromBaseUnits("500000", 6) → "0.5"
33
+ */
34
+ export declare function fromBaseUnits(amount: string, decimals: number): string;
35
+ /**
36
+ * Get the list of supported token symbols for a chain.
37
+ */
38
+ export declare function supportedTokensForChain(chain: Chain): string[];
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Static token map and amount conversion utilities.
3
+ * Maps human-readable token symbols to chain-specific addresses and decimals.
4
+ */
5
+ const TOKEN_MAP = {
6
+ solana: {
7
+ USDC: { address: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', decimals: 6, symbol: 'USDC' },
8
+ USDT: { address: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', decimals: 6, symbol: 'USDT' },
9
+ SOL: { address: 'So11111111111111111111111111111111111111112', decimals: 9, symbol: 'SOL' },
10
+ },
11
+ base: {
12
+ USDC: { address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', decimals: 6, symbol: 'USDC' },
13
+ USDT: { address: '0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2', decimals: 6, symbol: 'USDT' },
14
+ ETH: { address: '0x0000000000000000000000000000000000000000', decimals: 18, symbol: 'ETH' },
15
+ WETH: { address: '0x4200000000000000000000000000000000000006', decimals: 18, symbol: 'WETH' },
16
+ },
17
+ };
18
+ /**
19
+ * Resolve a token symbol to its address and decimals on a given chain.
20
+ * Case-insensitive matching.
21
+ */
22
+ export function resolveToken(chain, symbol) {
23
+ const normalized = symbol.toUpperCase();
24
+ const chainTokens = TOKEN_MAP[chain];
25
+ const token = chainTokens[normalized];
26
+ if (!token) {
27
+ const supported = Object.keys(chainTokens).join(', ');
28
+ throw new Error(`Token "${symbol}" is not supported on ${chain}. Supported tokens: ${supported}.`);
29
+ }
30
+ return token;
31
+ }
32
+ /**
33
+ * Infer the destination token for a bridge.
34
+ * - If the same symbol exists on the destination chain, use it (USDC→USDC)
35
+ * - Otherwise, default to USDC on the destination chain
36
+ */
37
+ export function inferDestinationToken(sourceSymbol, destChain) {
38
+ const normalized = sourceSymbol.toUpperCase();
39
+ const destTokens = TOKEN_MAP[destChain];
40
+ // Same symbol on dest chain (e.g., USDC→USDC)
41
+ if (destTokens[normalized]) {
42
+ return destTokens[normalized];
43
+ }
44
+ // Default to USDC on destination chain
45
+ return destTokens['USDC'];
46
+ }
47
+ /**
48
+ * Convert a human-readable amount to base units (smallest denomination).
49
+ * Uses string math to avoid floating-point drift.
50
+ * e.g., toBaseUnits("100", 6) → "100000000"
51
+ * toBaseUnits("0.5", 6) → "500000"
52
+ */
53
+ export function toBaseUnits(amount, decimals) {
54
+ const parts = amount.split('.');
55
+ const wholePart = parts[0] || '0';
56
+ let fracPart = parts[1] || '';
57
+ // Pad or truncate fractional part to exactly `decimals` digits
58
+ if (fracPart.length > decimals) {
59
+ fracPart = fracPart.slice(0, decimals);
60
+ }
61
+ else {
62
+ fracPart = fracPart.padEnd(decimals, '0');
63
+ }
64
+ // Combine and strip leading zeros
65
+ const combined = wholePart + fracPart;
66
+ const stripped = combined.replace(/^0+/, '') || '0';
67
+ return stripped;
68
+ }
69
+ /**
70
+ * Convert base units to a human-readable formatted amount.
71
+ * e.g., fromBaseUnits("100000000", 6) → "100"
72
+ * fromBaseUnits("500000", 6) → "0.5"
73
+ */
74
+ export function fromBaseUnits(amount, decimals) {
75
+ // Pad with leading zeros if shorter than decimals
76
+ const padded = amount.padStart(decimals + 1, '0');
77
+ const insertAt = padded.length - decimals;
78
+ const wholePart = padded.slice(0, insertAt) || '0';
79
+ const fracPart = padded.slice(insertAt).replace(/0+$/, '');
80
+ if (fracPart.length === 0) {
81
+ return wholePart;
82
+ }
83
+ return `${wholePart}.${fracPart}`;
84
+ }
85
+ /**
86
+ * Get the list of supported token symbols for a chain.
87
+ */
88
+ export function supportedTokensForChain(chain) {
89
+ return Object.keys(TOKEN_MAP[chain]);
90
+ }
91
+ //# sourceMappingURL=tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * clawswap_bridge tool handler.
3
+ * Full end-to-end bridge: quote → execute → sign → submit → poll.
4
+ * This is the primary tool — most agent interactions use only this.
5
+ */
6
+ import { ClawSwapClient } from '@clawswap/sdk';
7
+ import type { Config } from '../config.js';
8
+ import type { ToolResult } from '../errors.js';
9
+ import type { SolanaWallet } from '../wallet/solana.js';
10
+ import type { EvmWallet } from '../wallet/evm.js';
11
+ interface BridgeParams {
12
+ amount: string;
13
+ token: string;
14
+ from: string;
15
+ to: string;
16
+ recipient: string;
17
+ destinationToken?: string;
18
+ slippage?: number;
19
+ }
20
+ export declare function createBridgeHandler(client: ClawSwapClient, config: Config, solanaWallet: SolanaWallet | null, evmWallet: EvmWallet | null): (params: BridgeParams) => Promise<ToolResult>;
21
+ export {};
@@ -0,0 +1,147 @@
1
+ /**
2
+ * clawswap_bridge tool handler.
3
+ * Full end-to-end bridge: quote → execute → sign → submit → poll.
4
+ * This is the primary tool — most agent interactions use only this.
5
+ */
6
+ import { isSolanaSource, isEvmSource, TimeoutError } from '@clawswap/sdk';
7
+ import { formatError } from '../errors.js';
8
+ import { resolveToken, inferDestinationToken, toBaseUnits, fromBaseUnits } from '../tokens.js';
9
+ export function createBridgeHandler(client, config, solanaWallet, evmWallet) {
10
+ return async (params) => {
11
+ let requestId;
12
+ try {
13
+ const from = params.from;
14
+ const to = params.to;
15
+ // ── Phase 1: Validate ──────────────────────────────────────────────
16
+ if (from === to) {
17
+ return {
18
+ status: 'failed',
19
+ error: 'INVALID_ROUTE',
20
+ message: `Source and destination chain cannot be the same ("${from}").`,
21
+ suggestion: 'Specify different chains for "from" and "to".',
22
+ };
23
+ }
24
+ // Resolve tokens
25
+ const sourceToken = resolveToken(from, params.token);
26
+ const destToken = params.destinationToken
27
+ ? resolveToken(to, params.destinationToken)
28
+ : inferDestinationToken(params.token, to);
29
+ // Check wallet configured for source chain
30
+ if (from === 'solana' && !solanaWallet) {
31
+ return {
32
+ status: 'failed',
33
+ error: 'WALLET_NOT_CONFIGURED',
34
+ message: 'No Solana wallet configured. Add SOLANA_PRIVATE_KEY to your MCP server environment variables.',
35
+ suggestion: 'See https://docs.clawswap.dev/mcp-setup for configuration instructions.',
36
+ };
37
+ }
38
+ if (from === 'base' && !evmWallet) {
39
+ return {
40
+ status: 'failed',
41
+ error: 'WALLET_NOT_CONFIGURED',
42
+ message: 'No Base wallet configured. Add BASE_PRIVATE_KEY to your MCP server environment variables.',
43
+ suggestion: 'See https://docs.clawswap.dev/mcp-setup for configuration instructions.',
44
+ };
45
+ }
46
+ // Determine addresses
47
+ const userWallet = from === 'solana' ? config.solana.address : config.base.address;
48
+ const recipient = params.recipient;
49
+ // Convert amount
50
+ const baseAmount = toBaseUnits(params.amount, sourceToken.decimals);
51
+ console.error(`Bridge: ${params.amount} ${sourceToken.symbol} (${from}) → ${destToken.symbol} (${to})`);
52
+ console.error(` Amount in base units: ${baseAmount}`);
53
+ console.error(` User wallet: ${userWallet}`);
54
+ console.error(` Recipient: ${recipient}`);
55
+ // ── Phase 2: Execute swap via SDK ──────────────────────────────────
56
+ console.error(' Calling executeSwap...');
57
+ const response = await client.executeSwap({
58
+ sourceChain: from,
59
+ sourceToken: sourceToken.address,
60
+ destinationChain: to,
61
+ destinationToken: destToken.address,
62
+ amount: baseAmount,
63
+ userWallet,
64
+ recipient,
65
+ slippageTolerance: params.slippage ?? 0.01,
66
+ });
67
+ requestId = response.requestId;
68
+ console.error(` Request ID: ${requestId}`);
69
+ console.error(` Estimated output: ${response.estimatedOutput}`);
70
+ // ── Phase 3: Sign and submit ───────────────────────────────────────
71
+ let sourceTxHash;
72
+ if (isSolanaSource(response)) {
73
+ console.error(' Signing and submitting Solana transaction...');
74
+ const result = await solanaWallet.signAndSubmit(response.transaction);
75
+ sourceTxHash = result.signature;
76
+ console.error(` Solana signature: ${sourceTxHash}`);
77
+ }
78
+ else if (isEvmSource(response)) {
79
+ console.error(` Signing and submitting ${response.transactions.length} EVM transaction(s)...`);
80
+ const result = await evmWallet.signAndSubmitAll(response.transactions);
81
+ sourceTxHash = result.txHashes[result.txHashes.length - 1]; // Last tx hash
82
+ console.error(` Base tx hashes: ${result.txHashes.join(', ')}`);
83
+ }
84
+ else {
85
+ return {
86
+ status: 'failed',
87
+ error: 'INTERNAL_ERROR',
88
+ message: 'Unexpected response from executeSwap: no transaction data.',
89
+ suggestion: 'This may be a temporary API issue. Try again.',
90
+ };
91
+ }
92
+ // ── Phase 4: Poll for settlement ───────────────────────────────────
93
+ console.error(' Polling for settlement (up to 300s)...');
94
+ try {
95
+ const result = await client.waitForSettlement(requestId, {
96
+ timeout: 300_000,
97
+ interval: 3_000,
98
+ onStatusUpdate: (s) => {
99
+ console.error(` Poll result: status=${s.status}, sourceTx=${s.sourceTxHash}, destTx=${s.destinationTxHash}`);
100
+ },
101
+ });
102
+ if (result.status === 'completed') {
103
+ const outputFormatted = fromBaseUnits(result.outputAmount, destToken.decimals);
104
+ return {
105
+ status: 'completed',
106
+ summary: `Bridged ${params.amount} ${sourceToken.symbol} from ${from} to ${to}. Received ${outputFormatted} ${destToken.symbol}.`,
107
+ details: {
108
+ amountSent: `${params.amount} ${sourceToken.symbol}`,
109
+ amountReceived: `${outputFormatted} ${destToken.symbol}`,
110
+ sourceTxHash,
111
+ destinationTxHash: result.destinationTxHash,
112
+ fee: `${response.fees.clawswap} (ClawSwap) + ${response.fees.relay} (bridge)`,
113
+ requestId,
114
+ },
115
+ };
116
+ }
117
+ else {
118
+ return {
119
+ status: 'failed',
120
+ error: 'BRIDGE_FAILED',
121
+ message: `Bridge reached terminal status: ${result.status}`,
122
+ suggestion: 'The bridge transaction was submitted but failed during settlement. Check your wallet balances.',
123
+ };
124
+ }
125
+ }
126
+ catch (err) {
127
+ // Timeout — return pending with requestId for follow-up
128
+ if (err instanceof TimeoutError) {
129
+ return {
130
+ status: 'pending',
131
+ summary: `Bridge submitted but not yet confirmed after 300 seconds. Check back with requestId.`,
132
+ details: {
133
+ requestId,
134
+ sourceTxHash,
135
+ suggestion: 'Use clawswap_status to check on the bridge progress.',
136
+ },
137
+ };
138
+ }
139
+ throw err;
140
+ }
141
+ }
142
+ catch (err) {
143
+ return formatError(err, { requestId });
144
+ }
145
+ };
146
+ }
147
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/tools/bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * clawswap_quote tool handler.
3
+ * Preview a bridge quote without executing — shows estimated output, fees, and time.
4
+ */
5
+ import type { ClawSwapClient } from '@clawswap/sdk';
6
+ import type { Config } from '../config.js';
7
+ import type { ToolResult } from '../errors.js';
8
+ interface QuoteParams {
9
+ amount: string;
10
+ token: string;
11
+ from: string;
12
+ to: string;
13
+ destinationToken?: string;
14
+ slippage?: number;
15
+ }
16
+ export declare function createQuoteHandler(client: ClawSwapClient, config: Config): (params: QuoteParams) => Promise<ToolResult>;
17
+ export {};
@@ -0,0 +1,67 @@
1
+ /**
2
+ * clawswap_quote tool handler.
3
+ * Preview a bridge quote without executing — shows estimated output, fees, and time.
4
+ */
5
+ import { formatError } from '../errors.js';
6
+ import { resolveToken, inferDestinationToken, toBaseUnits, fromBaseUnits } from '../tokens.js';
7
+ export function createQuoteHandler(client, config) {
8
+ return async (params) => {
9
+ try {
10
+ const from = params.from;
11
+ const to = params.to;
12
+ // Validate chains
13
+ if (from === to) {
14
+ return {
15
+ status: 'failed',
16
+ error: 'INVALID_ROUTE',
17
+ message: `Source and destination chain cannot be the same ("${from}").`,
18
+ suggestion: 'Specify different chains for "from" and "to".',
19
+ };
20
+ }
21
+ // Resolve source token
22
+ const sourceToken = resolveToken(from, params.token);
23
+ // Resolve destination token
24
+ const destToken = params.destinationToken
25
+ ? resolveToken(to, params.destinationToken)
26
+ : inferDestinationToken(params.token, to);
27
+ // Convert human amount to base units
28
+ const baseAmount = toBaseUnits(params.amount, sourceToken.decimals);
29
+ // Determine wallet addresses — use placeholders for quotes if wallets not configured.
30
+ // The API only needs valid-format addresses to compute a quote; it doesn't verify ownership.
31
+ const PLACEHOLDER_SOLANA = '11111111111111111111111111111111';
32
+ const PLACEHOLDER_EVM = '0x0000000000000000000000000000000000000001';
33
+ const userWallet = (from === 'solana' ? config.solana.address : config.base.address)
34
+ || (from === 'solana' ? PLACEHOLDER_SOLANA : PLACEHOLDER_EVM);
35
+ const recipient = (to === 'solana' ? config.solana.address : config.base.address)
36
+ || (to === 'solana' ? PLACEHOLDER_SOLANA : PLACEHOLDER_EVM);
37
+ // Call SDK
38
+ const quote = await client.getQuote({
39
+ sourceChain: from,
40
+ sourceToken: sourceToken.address,
41
+ destinationChain: to,
42
+ destinationToken: destToken.address,
43
+ amount: baseAmount,
44
+ userWallet,
45
+ recipient,
46
+ slippageTolerance: params.slippage ?? 0.01,
47
+ });
48
+ const outputFormatted = quote.estimatedOutputFormatted || fromBaseUnits(quote.estimatedOutput, destToken.decimals);
49
+ return {
50
+ status: 'completed',
51
+ summary: `Bridging ${params.amount} ${sourceToken.symbol} from ${from} to ${to}: you'd receive ~${outputFormatted} ${destToken.symbol}. Fees: ${quote.fees.clawswap} (ClawSwap) + ${quote.fees.relay} (bridge) + ${quote.fees.gas} (gas). Estimated time: ~${quote.estimatedTime}s.`,
52
+ details: {
53
+ estimatedOutput: outputFormatted,
54
+ sourceToken: sourceToken.symbol,
55
+ destinationToken: destToken.symbol,
56
+ fees: quote.fees,
57
+ estimatedTime: `${quote.estimatedTime} seconds`,
58
+ supported: quote.supported,
59
+ },
60
+ };
61
+ }
62
+ catch (err) {
63
+ return formatError(err);
64
+ }
65
+ };
66
+ }
67
+ //# sourceMappingURL=quote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote.js","sourceRoot":"","sources":["../../src/tools/quote.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,WAAW,EAAE,aAAa,EAAc,MAAM,cAAc,CAAC;AAW3G,MAAM,UAAU,kBAAkB,CAAC,MAAsB,EAAE,MAAc;IACvE,OAAO,KAAK,EAAE,MAAmB,EAAuB,EAAE;QACxD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAa,CAAC;YAClC,MAAM,EAAE,GAAG,MAAM,CAAC,EAAW,CAAC;YAE9B,kBAAkB;YAClB,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;gBAChB,OAAO;oBACL,MAAM,EAAE,QAAQ;oBAChB,KAAK,EAAE,eAAe;oBACtB,OAAO,EAAE,qDAAqD,IAAI,KAAK;oBACvE,UAAU,EAAE,+CAA+C;iBAC5D,CAAC;YACJ,CAAC;YAED,uBAAuB;YACvB,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;YAErD,4BAA4B;YAC5B,MAAM,SAAS,GAAG,MAAM,CAAC,gBAAgB;gBACvC,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,MAAM,CAAC,gBAAgB,CAAC;gBAC3C,CAAC,CAAC,qBAAqB,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAE5C,qCAAqC;YACrC,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;YAEpE,sFAAsF;YACtF,6FAA6F;YAC7F,MAAM,kBAAkB,GAAG,kCAAkC,CAAC;YAC9D,MAAM,eAAe,GAAG,4CAA4C,CAAC;YAErE,MAAM,UAAU,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;mBAC/E,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;YAChE,MAAM,SAAS,GAAG,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;mBAC5E,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;YAE9D,WAAW;YACX,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC;gBAClC,WAAW,EAAE,IAAI;gBACjB,WAAW,EAAE,WAAW,CAAC,OAAO;gBAChC,gBAAgB,EAAE,EAAE;gBACpB,gBAAgB,EAAE,SAAS,CAAC,OAAO;gBACnC,MAAM,EAAE,UAAU;gBAClB,UAAU;gBACV,SAAS;gBACT,iBAAiB,EAAE,MAAM,CAAC,QAAQ,IAAI,IAAI;aAC3C,CAAC,CAAC;YAEH,MAAM,eAAe,GAAG,KAAK,CAAC,wBAAwB,IAAI,aAAa,CAAC,KAAK,CAAC,eAAe,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;YAEnH,OAAO;gBACL,MAAM,EAAE,WAAW;gBACnB,OAAO,EAAE,YAAY,MAAM,CAAC,MAAM,IAAI,WAAW,CAAC,MAAM,SAAS,IAAI,OAAO,EAAE,oBAAoB,eAAe,IAAI,SAAS,CAAC,MAAM,WAAW,KAAK,CAAC,IAAI,CAAC,QAAQ,iBAAiB,KAAK,CAAC,IAAI,CAAC,KAAK,eAAe,KAAK,CAAC,IAAI,CAAC,GAAG,4BAA4B,KAAK,CAAC,aAAa,IAAI;gBACpR,OAAO,EAAE;oBACP,eAAe,EAAE,eAAe;oBAChC,WAAW,EAAE,WAAW,CAAC,MAAM;oBAC/B,gBAAgB,EAAE,SAAS,CAAC,MAAM;oBAClC,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,aAAa,EAAE,GAAG,KAAK,CAAC,aAAa,UAAU;oBAC/C,SAAS,EAAE,KAAK,CAAC,SAAS;iBAC3B;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * clawswap_status tool handler.
3
+ * Check the status of a previously initiated bridge.
4
+ */
5
+ import type { ClawSwapClient } from '@clawswap/sdk';
6
+ import type { ToolResult } from '../errors.js';
7
+ interface StatusParams {
8
+ requestId: string;
9
+ }
10
+ export declare function createStatusHandler(client: ClawSwapClient): (params: StatusParams) => Promise<ToolResult>;
11
+ export {};
@@ -0,0 +1,38 @@
1
+ /**
2
+ * clawswap_status tool handler.
3
+ * Check the status of a previously initiated bridge.
4
+ */
5
+ import { formatError } from '../errors.js';
6
+ export function createStatusHandler(client) {
7
+ return async (params) => {
8
+ try {
9
+ const status = await client.getStatus(params.requestId);
10
+ const summaryMap = {
11
+ completed: `Your bridge is complete. ${status.outputAmount} tokens arrived on ${status.destinationChain}.`,
12
+ failed: `Your bridge failed.`,
13
+ pending: `Your bridge is in progress (status: pending). Check again shortly.`,
14
+ submitted: `Your bridge transaction has been submitted. Waiting for confirmation.`,
15
+ filling: `Your bridge is being filled by the relay. Almost there.`,
16
+ };
17
+ const summary = summaryMap[status.status] || `Bridge status: ${status.status}`;
18
+ return {
19
+ status: status.status === 'completed' ? 'completed' : (status.status === 'failed' ? 'failed' : 'pending'),
20
+ summary,
21
+ details: {
22
+ requestId: status.requestId,
23
+ status: status.status,
24
+ sourceChain: status.sourceChain,
25
+ destinationChain: status.destinationChain,
26
+ outputAmount: status.outputAmount,
27
+ ...(status.sourceTxHash && { sourceTxHash: status.sourceTxHash }),
28
+ ...(status.destinationTxHash && { destinationTxHash: status.destinationTxHash }),
29
+ ...(status.completedAt && { completedAt: status.completedAt }),
30
+ },
31
+ };
32
+ }
33
+ catch (err) {
34
+ return formatError(err);
35
+ }
36
+ };
37
+ }
38
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/tools/status.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAM3C,MAAM,UAAU,mBAAmB,CAAC,MAAsB;IACxD,OAAO,KAAK,EAAE,MAAoB,EAAuB,EAAE;QACzD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAExD,MAAM,UAAU,GAA2B;gBACzC,SAAS,EAAE,4BAA4B,MAAM,CAAC,YAAY,sBAAsB,MAAM,CAAC,gBAAgB,GAAG;gBAC1G,MAAM,EAAE,qBAAqB;gBAC7B,OAAO,EAAE,oEAAoE;gBAC7E,SAAS,EAAE,uEAAuE;gBAClF,OAAO,EAAE,yDAAyD;aACnE,CAAC;YAEF,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,kBAAkB,MAAM,CAAC,MAAM,EAAE,CAAC;YAE/E,OAAO;gBACL,MAAM,EAAE,MAAM,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAe,CAAC,CAAC,CAAC,SAAS,CAAC;gBAChH,OAAO;gBACP,OAAO,EAAE;oBACP,SAAS,EAAE,MAAM,CAAC,SAAS;oBAC3B,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,WAAW,EAAE,MAAM,CAAC,WAAW;oBAC/B,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;oBACzC,YAAY,EAAE,MAAM,CAAC,YAAY;oBACjC,GAAG,CAAC,MAAM,CAAC,YAAY,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC;oBACjE,GAAG,CAAC,MAAM,CAAC,iBAAiB,IAAI,EAAE,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,EAAE,CAAC;oBAChF,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC;iBAC/D;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * EVM (Base) wallet module.
3
+ * Handles signing and sequential submission of EVM transactions.
4
+ */
5
+ import type { EvmTransaction } from '@clawswap/sdk';
6
+ export interface EvmWallet {
7
+ address: string;
8
+ signAndSubmitAll(transactions: EvmTransaction[]): Promise<{
9
+ txHashes: string[];
10
+ }>;
11
+ }
12
+ export declare function createEvmWallet(privateKey: string, rpcUrl: string): EvmWallet;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * EVM (Base) wallet module.
3
+ * Handles signing and sequential submission of EVM transactions.
4
+ */
5
+ import { createWalletClient, createPublicClient, http } from 'viem';
6
+ import { privateKeyToAccount } from 'viem/accounts';
7
+ import { base } from 'viem/chains';
8
+ export function createEvmWallet(privateKey, rpcUrl) {
9
+ const account = privateKeyToAccount(privateKey);
10
+ const walletClient = createWalletClient({
11
+ account,
12
+ chain: base,
13
+ transport: http(rpcUrl),
14
+ });
15
+ const publicClient = createPublicClient({
16
+ chain: base,
17
+ transport: http(rpcUrl),
18
+ });
19
+ return {
20
+ address: account.address,
21
+ async signAndSubmitAll(transactions) {
22
+ const txHashes = [];
23
+ // Fetch current nonce from the network to avoid stale nonce issues
24
+ let nonce = await publicClient.getTransactionCount({ address: account.address });
25
+ console.error(` Starting nonce: ${nonce}`);
26
+ // Transactions MUST be executed in order (e.g., approve before bridge)
27
+ for (const [i, tx] of transactions.entries()) {
28
+ const stepLabel = tx.description || `Step ${i + 1}/${transactions.length}`;
29
+ console.error(` Submitting EVM transaction: ${stepLabel} (nonce: ${nonce})`);
30
+ const txHash = await walletClient.sendTransaction({
31
+ to: tx.to,
32
+ data: tx.data,
33
+ value: BigInt(tx.value),
34
+ nonce,
35
+ });
36
+ console.error(` Transaction submitted: ${txHash}`);
37
+ // Wait for confirmation before next tx
38
+ await publicClient.waitForTransactionReceipt({ hash: txHash });
39
+ console.error(` Transaction confirmed: ${stepLabel}`);
40
+ txHashes.push(txHash);
41
+ nonce++;
42
+ }
43
+ return { txHashes };
44
+ },
45
+ };
46
+ }
47
+ //# sourceMappingURL=evm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evm.js","sourceRoot":"","sources":["../../src/wallet/evm.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAQnC,MAAM,UAAU,eAAe,CAAC,UAAkB,EAAE,MAAc;IAChE,MAAM,OAAO,GAAG,mBAAmB,CAAC,UAA2B,CAAC,CAAC;IAEjE,MAAM,YAAY,GAAG,kBAAkB,CAAC;QACtC,OAAO;QACP,KAAK,EAAE,IAAI;QACX,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC;KACxB,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,kBAAkB,CAAC;QACtC,KAAK,EAAE,IAAI;QACX,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC;KACxB,CAAC,CAAC;IAEH,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO;QAExB,KAAK,CAAC,gBAAgB,CAAC,YAA8B;YACnD,MAAM,QAAQ,GAAa,EAAE,CAAC;YAE9B,mEAAmE;YACnE,IAAI,KAAK,GAAG,MAAM,YAAY,CAAC,mBAAmB,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;YACjF,OAAO,CAAC,KAAK,CAAC,qBAAqB,KAAK,EAAE,CAAC,CAAC;YAE5C,uEAAuE;YACvE,KAAK,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC;gBAC7C,MAAM,SAAS,GAAG,EAAE,CAAC,WAAW,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,MAAM,EAAE,CAAC;gBAC3E,OAAO,CAAC,KAAK,CAAC,iCAAiC,SAAS,YAAY,KAAK,GAAG,CAAC,CAAC;gBAE9E,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,eAAe,CAAC;oBAChD,EAAE,EAAE,EAAE,CAAC,EAAmB;oBAC1B,IAAI,EAAE,EAAE,CAAC,IAAqB;oBAC9B,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC;oBACvB,KAAK;iBACN,CAAC,CAAC;gBAEH,OAAO,CAAC,KAAK,CAAC,4BAA4B,MAAM,EAAE,CAAC,CAAC;gBAEpD,uCAAuC;gBACvC,MAAM,YAAY,CAAC,yBAAyB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;gBAC/D,OAAO,CAAC,KAAK,CAAC,4BAA4B,SAAS,EAAE,CAAC,CAAC;gBAEvD,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACtB,KAAK,EAAE,CAAC;YACV,CAAC;YAED,OAAO,EAAE,QAAQ,EAAE,CAAC;QACtB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Solana wallet module using @solana/kit (modern Solana SDK).
3
+ * Handles deserialization of partially-signed transactions, signing, and RPC submission.
4
+ *
5
+ * Note: @solana/kit uses branded types designed for building transactions from scratch.
6
+ * Since we're working with pre-built, partially-signed transactions from the API,
7
+ * we use type assertions where the kit's type system is overly strict.
8
+ * The runtime behavior is correct — the underlying data structures are compatible.
9
+ */
10
+ export interface SolanaWallet {
11
+ publicKey: string;
12
+ signAndSubmit(base64Tx: string): Promise<{
13
+ signature: string;
14
+ }>;
15
+ }
16
+ export declare function createSolanaWallet(privateKey: string, rpcUrl: string): Promise<SolanaWallet>;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Solana wallet module using @solana/kit (modern Solana SDK).
3
+ * Handles deserialization of partially-signed transactions, signing, and RPC submission.
4
+ *
5
+ * Note: @solana/kit uses branded types designed for building transactions from scratch.
6
+ * Since we're working with pre-built, partially-signed transactions from the API,
7
+ * we use type assertions where the kit's type system is overly strict.
8
+ * The runtime behavior is correct — the underlying data structures are compatible.
9
+ */
10
+ import { createSolanaRpc, getTransactionDecoder, partiallySignTransaction, sendTransactionWithoutConfirmingFactory, getSignatureFromTransaction, } from '@solana/kit';
11
+ import { createKeyPairSignerFromBytes } from '@solana/signers';
12
+ import bs58 from 'bs58';
13
+ export async function createSolanaWallet(privateKey, rpcUrl) {
14
+ const secretKey = bs58.decode(privateKey);
15
+ const signer = await createKeyPairSignerFromBytes(secretKey);
16
+ const rpc = createSolanaRpc(rpcUrl);
17
+ const sendTransaction = sendTransactionWithoutConfirmingFactory({ rpc });
18
+ return {
19
+ publicKey: signer.address,
20
+ async signAndSubmit(base64Tx) {
21
+ // 1. Decode base64 → bytes (use Node.js Buffer since @solana/kit codecs expect Uint8Array)
22
+ const txBytes = new Uint8Array(Buffer.from(base64Tx, 'base64'));
23
+ // 2. Deserialize bytes → transaction object
24
+ const transactionDecoder = getTransactionDecoder();
25
+ const transaction = transactionDecoder.decode(txBytes);
26
+ // 3. Partially sign with our keypair
27
+ // The API transaction is already signed by the server (fee payer).
28
+ // We add our signature. Type assertion needed because the deserialized
29
+ // transaction doesn't carry the branded TransactionWithLifetime type.
30
+ const signedTx = await partiallySignTransaction([signer.keyPair], transaction);
31
+ // 4. Get signature for tracking
32
+ const sig = getSignatureFromTransaction(signedTx);
33
+ console.error(` Solana transaction signed, signature: ${sig}`);
34
+ // 5. Submit to RPC (without waiting for confirmation — we poll via ClawSwap API)
35
+ console.error(' Submitting to Solana RPC...');
36
+ try {
37
+ await sendTransaction(signedTx, { commitment: 'confirmed' });
38
+ console.error(' Transaction submitted to Solana');
39
+ }
40
+ catch (err) {
41
+ console.error(` Solana submission error: ${err instanceof Error ? err.message : err}`);
42
+ throw err;
43
+ }
44
+ return { signature: sig };
45
+ },
46
+ };
47
+ }
48
+ //# sourceMappingURL=solana.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solana.js","sourceRoot":"","sources":["../../src/wallet/solana.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,wBAAwB,EACxB,uCAAuC,EACvC,2BAA2B,GAE5B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,IAAI,MAAM,MAAM,CAAC;AAOxB,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,UAAkB,EAAE,MAAc;IACzE,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,MAAM,4BAA4B,CAAC,SAAS,CAAC,CAAC;IAE7D,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,eAAe,GAAG,uCAAuC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;IAEzE,OAAO;QACL,SAAS,EAAE,MAAM,CAAC,OAAO;QAEzB,KAAK,CAAC,aAAa,CAAC,QAAgB;YAClC,2FAA2F;YAC3F,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;YAEhE,4CAA4C;YAC5C,MAAM,kBAAkB,GAAG,qBAAqB,EAAE,CAAC;YACnD,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAEvD,qCAAqC;YACrC,mEAAmE;YACnE,uEAAuE;YACvE,sEAAsE;YACtE,MAAM,QAAQ,GAAG,MAAM,wBAAwB,CAC7C,CAAC,MAAM,CAAC,OAAO,CAAC,EAChB,WAA6D,CAC9D,CAAC;YAEF,gCAAgC;YAChC,MAAM,GAAG,GAAG,2BAA2B,CACrC,QAA6D,CAC9D,CAAC;YACF,OAAO,CAAC,KAAK,CAAC,2CAA2C,GAAG,EAAE,CAAC,CAAC;YAEhE,iFAAiF;YACjF,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;YAC/C,IAAI,CAAC;gBACH,MAAM,eAAe,CACnB,QAA4D,EAC5D,EAAE,UAAU,EAAE,WAAW,EAAE,CAC5B,CAAC;gBACF,OAAO,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;YACrD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,8BAA8B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;gBACxF,MAAM,GAAG,CAAC;YACZ,CAAC;YAED,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;QAC5B,CAAC;KACF,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@clawswap/mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for gasless cross-chain bridges between Solana and Base via ClawSwap",
5
+ "type": "module",
6
+ "main": "./build/index.js",
7
+ "bin": {
8
+ "clawswap-mcp": "./build/index.js"
9
+ },
10
+ "files": [
11
+ "build",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "start": "node build/index.js",
17
+ "dev": "tsx src/index.ts",
18
+ "prepublishOnly": "npm run build"
19
+ },
20
+ "keywords": [
21
+ "mcp",
22
+ "model-context-protocol",
23
+ "solana",
24
+ "base",
25
+ "cross-chain",
26
+ "bridge",
27
+ "swap",
28
+ "ai-agents",
29
+ "x402",
30
+ "usdc",
31
+ "gasless"
32
+ ],
33
+ "author": "ClawSwap",
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/WarTech9/clawswap-mcp.git"
38
+ },
39
+ "homepage": "https://clawswap.dev",
40
+ "bugs": {
41
+ "url": "https://github.com/WarTech9/clawswap-mcp/issues"
42
+ },
43
+ "publishConfig": {
44
+ "access": "public",
45
+ "registry": "https://registry.npmjs.org/"
46
+ },
47
+ "dependencies": {
48
+ "@modelcontextprotocol/sdk": "^1.12.0",
49
+ "@clawswap/sdk": "^0.2.0",
50
+ "@solana/kit": "^3.0.0",
51
+ "@solana/signers": "^6.0.1",
52
+ "@x402/core": "^2.3.0",
53
+ "@x402/svm": "^2.3.0",
54
+ "@x402/fetch": "^2.3.0",
55
+ "bs58": "^6.0.0",
56
+ "viem": "^2.0.0",
57
+ "zod": "^3.22.4"
58
+ },
59
+ "devDependencies": {
60
+ "typescript": "^5.3.3",
61
+ "@types/node": "^20.11.0",
62
+ "tsx": "^4.7.1"
63
+ },
64
+ "engines": {
65
+ "node": ">=18.0.0"
66
+ }
67
+ }