---
name: openswaps
description: Give your Privy wallet swap superpowers. Multi-chain DEX meta-aggregator for AI agents - best prices from 15+ DEX aggregators across 13 blockchains.
metadata: {"openclaw":{"emoji":"🔄","requires":{"env":[]},"primaryEnv":"PRIVATE_KEY","install":[{"id":"mcp","kind":"mcp","label":"Add OnlySwaps MCP Server","config":{"command":"npx","args":["-y","@onlyswaps/mcp-server"],"env":{"ONLYSWAPS_API_URL":"https://api.onlyswaps.fyi","WALLET_MODE":"local"}}}]}}
homepage: https://onlyswaps.fyi
version: 0.2.0
---

# OnlySwaps - Multi-Chain DEX Aggregator for AI Agents

> **Privy handles wallets. OnlySwaps handles swaps. Together: AI agents that trade.**

OnlySwaps gives your AI agent **multi-DEX quote aggregation** and **optimal swap execution** across 13 blockchains. Get the best prices from 15+ DEX aggregators with a single tool call.

**Version:** 0.2.0 | **npm:** `@onlyswaps/mcp-server`

---

## Security Warning

> **IMPORTANT: Use a dedicated trading wallet with minimal funds.**
>
> - **Never use a wallet containing significant holdings** for automated trading
> - Create a separate "hot wallet" specifically for this agent
> - Only fund it with the tokens and gas you intend to trade
> - For production deployments, use **Privy mode** for better key management
> - Private keys in environment variables can be exposed through logs or process dumps

---

## Read-Only Tools (No Wallet Required)

**Want to try OnlySwaps without any setup?** The `get_quote` and `get_portfolio` tools work immediately - no wallet configuration needed.

```
Tool: get_quote
Parameters: {
  "fromToken": "ETH",
  "toToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "amount": "1000000000000000000",
  "chainId": 8453
}
```

This returns quotes from 15+ DEX aggregators without executing anything - great for testing and price discovery.

---

## Quick Start (Standalone / Local Wallet)

The simplest way to get started - use your own private key.

### Step 1: Add MCP Server

Add to your MCP configuration (`~/.claude/claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "onlyswaps": {
      "command": "npx",
      "args": ["-y", "@onlyswaps/mcp-server"],
      "env": {
        "WALLET_MODE": "local",
        "PRIVATE_KEY": "0xYourPrivateKeyHere",
        "ONLYSWAPS_API_URL": "https://api.onlyswaps.fyi"
      }
    }
  }
}
```

### Step 2: Verify Setup

```
Tool: check_setup
Parameters: { "chainId": 8453 }
```

**Response:**
```json
{
  "ready": true,
  "walletMode": "local",
  "address": "0xYourWallet...",
  "nativeBalance": "0.01",
  "message": "Wallet is ready for trading on Base"
}
```

### Step 3: Start Swapping

```
Tool: swap
Parameters: {
  "fromToken": "ETH",
  "toToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "amount": "100000000000000000",
  "chainId": 8453
}
```

---

## Quick Start (With Privy)

Using Privy for wallet management? OnlySwaps integrates seamlessly for swap execution.

### Prerequisites

- Privy account with Wallet API enabled
- Node.js 18+

### Step 1: Add Both MCP Servers

```json
{
  "mcpServers": {
    "privy": {
      "command": "npx",
      "args": ["-y", "@privy-io/mcp-server"],
      "env": {
        "PRIVY_APP_ID": "your-app-id",
        "PRIVY_APP_SECRET": "your-app-secret"
      }
    },
    "onlyswaps": {
      "command": "npx",
      "args": ["-y", "@onlyswaps/mcp-server"],
      "env": {
        "WALLET_MODE": "privy",
        "PRIVY_APP_ID": "your-app-id",
        "PRIVY_APP_SECRET": "your-app-secret",
        "PRIVY_USER_ID": "my-agent-1",
        "ONLYSWAPS_API_URL": "https://api.onlyswaps.fyi"
      }
    }
  }
}
```

### Why Use Both?

| Privy Handles | OnlySwaps Handles |
|--------------|-------------------|
| Wallet creation | Quote aggregation (15+ DEXs) |
| Transfers | Best price execution |
| Transaction signing | Permit2 gasless swaps |
| Key management | Cross-DEX optimization |

---

## Configuration Options

### Environment Variables

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `WALLET_MODE` | No | `"local"` | `"local"` or `"privy"` |
| `PRIVATE_KEY` | Local mode | - | 0x-prefixed private key |
| `PRIVY_APP_ID` | Privy mode | - | Your Privy application ID |
| `PRIVY_APP_SECRET` | Privy mode | - | Your Privy application secret |
| `PRIVY_USER_ID` | Privy mode | - | Unique identifier for this agent's wallet |
| `ONLYSWAPS_API_URL` | No | `https://api.onlyswaps.fyi` | API endpoint |

### Wallet Modes Comparison

| Mode | Setup Complexity | Best For |
|------|------------------|----------|
| **Local** | Simple - just add private key | Development, personal use, quick testing |
| **Privy** | Requires Privy account | Production agents, managed wallets, multi-user apps |

---

## Tools Reference

### Read-Only Tools (No Wallet Required)

These tools work without any wallet configuration - perfect for testing and exploration.

#### `get_quote`
Get swap quotes without executing.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `fromToken` | string | Yes | Token symbol or address |
| `toToken` | string | Yes | Token symbol or address |
| `amount` | string | Yes | Amount in base units |
| `chainId` | number/string | Yes | Chain ID or name |

**Response:**
```json
{
  "bestQuote": {
    "provider": "1inch",
    "buyAmount": "3245000000",
    "estimatedGas": "150000"
  },
  "allQuotes": [
    { "provider": "1inch", "buyAmount": "3245000000" },
    { "provider": "0x", "buyAmount": "3240000000" }
  ]
}
```

#### `get_portfolio`
Get token holdings for any address.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `userAddress` | string | Yes | Wallet address |
| `chains` | string[] | No | Filter by chains |

---

### Main Tools (Wallet Required)

#### `swap`
Execute a token swap with best price from 15+ DEX aggregators. **This is the main tool for simple swaps.**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `fromToken` | string | Yes | Token to sell (symbol or address) |
| `toToken` | string | Yes | Token to buy (symbol or address) |
| `amount` | string | Yes | Amount in **base units** (wei) |
| `chainId` | number | Yes | Chain ID (e.g., `8453` for Base) |
| `slippage` | number | No | Slippage % (default: 1) |
| `referrerAddress` | string | No | Earn fee share (see Fee Structure) |
| `extraFeeBps` | number | No | Extra fee 0-100 bps (suggested: 10) |

**Example - Swap 0.1 ETH to USDC on Base:**
```
Tool: swap
Parameters: {
  "fromToken": "ETH",
  "toToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "amount": "100000000000000000",
  "chainId": 8453
}
```

**Response:**
```json
{
  "success": true,
  "txHash": "0xabc123...",
  "provider": "1inch",
  "amountIn": "100000000000000000",
  "amountOut": "324500000",
  "amountOutFormatted": "324.50",
  "gasCostUsd": "0.12",
  "explorerUrl": "https://basescan.org/tx/0xabc123..."
}
```

#### `setup_wallet`
Create or retrieve a trading wallet for autonomous swaps.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `envPath` | string | No | Custom .env path (local mode only) |

**When to use:**
- **Privy mode**: Creates or retrieves the Privy-managed wallet for `PRIVY_USER_ID`
- **Local mode without `PRIVATE_KEY`**: Generates a new private key and saves to `.env`
- **Local mode with `PRIVATE_KEY` already set**: Returns existing wallet info

**Not needed if** `PRIVATE_KEY` is already configured in your environment - the wallet is ready to use.

**Response:**
```json
{
  "created": true,
  "address": "0xYourWallet...",
  "mode": "local",
  "message": "Wallet ready.",
  "nextSteps": ["Fund this address with tokens", "Run check_setup"]
}
```

#### `check_setup`
Verify wallet is ready for trading.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `chainId` | number | Yes | Chain to check |
| `tokenAddress` | string | No | Check this token's balance/approval |

**Response:**
```json
{
  "ready": true,
  "walletMode": "local",
  "address": "0xYourWallet...",
  "nativeBalance": "0.015",
  "permit2Approved": true,
  "missingSteps": ["Ready to trade! Use swap tool."]
}
```

#### `approve_permit2`
Approve token for Permit2 (usually automatic, but can be called manually).

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `chainId` | number | Yes | Chain ID |
| `tokenAddress` | string | Yes | Token to approve |

---

### Advanced Tools (For Custom Integrations)

These tools provide lower-level access for custom swap flows, external signing, or integration with other systems.

#### `build_swap_tx`
Build swap transaction data with Permit2 signature flow. **Core tool for the advanced ERC20 swap flow.**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `fromToken` | string | Yes | Token address to swap from |
| `toToken` | string | Yes | Token address to swap to |
| `amount` | string | Yes | Amount in base units (wei) |
| `chainId` | number/string | Yes | Chain ID or name |
| `userAddress` | string | Yes | User wallet address that will sign |
| `slippage` | number | No | Slippage % (default: 1) |
| `referrerAddress` | string | No | Referrer wallet for fee share |
| `extraFeeBps` | number | No | Extra fee 0-100 bps |

**Two flows based on input token:**

**Native token (ETH/BNB/MATIC):** Execute `transactions[]` directly.

**ERC20 token (Permit2 flow):**
1. If `approval.needed`, send `approval.transaction` (one-time per token)
2. Sign `permitTypedData` ONCE with `eth_signTypedData_v4`
3. Call `simulate_swap(signature, allQuotes)` to find working quotes
4. Execute the `recommended` quote from simulation

**Response:**
```json
{
  "transactions": [],
  "approval": {
    "needed": true,
    "tokenAddress": "0xToken...",
    "spender": "0x000000000022D473030F116dDEE9F6B43aC78BA3",
    "transaction": { "to": "...", "data": "...", "value": "0" }
  },
  "permit2SwapParams": {
    "adapterAddress": "0xED306e38BB930ec9646FF3D917B2e513a97530b1",
    "permitTypedData": { "domain": {...}, "types": {...}, "message": {...} },
    "sellOrderParams": {...},
    "nonce": "1234567890",
    "deadline": "1699999999"
  },
  "allQuotes": [...],
  "quote": {
    "provider": "1inch",
    "sellAmount": "1000000000000000000",
    "buyAmount": "3245000000"
  },
  "totalQuotes": 5,
  "meta": {
    "chain": "base",
    "chainId": 8453,
    "permit2Address": "0x000000000022D473030F116dDEE9F6B43aC78BA3",
    "permit2AdapterAddress": "0xED306e38BB930ec9646FF3D917B2e513a97530b1"
  }
}
```

#### `simulate_swap`
Simulate signed Permit2 swaps before execution. Tests ALL quotes and returns working ones.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `chainId` | number | Yes | Chain ID (EVM only) |
| `signature` | string | Yes | Signature from `eth_signTypedData_v4` |
| `quotes` | array | Yes | Array of `allQuotes` from `build_swap_tx` |
| `userAddress` | string | Yes | Wallet that signed the permit |

**Response:**
```json
{
  "successCount": 4,
  "failedCount": 1,
  "quotes": [
    {
      "provider": "1inch",
      "amountOut": "3245000000",
      "gasEstimate": "180000",
      "gasCostUsd": "0.15",
      "transaction": { "to": "...", "data": "...", "value": "0", "gas": "180000" }
    }
  ],
  "failed": [{ "provider": "paraswap", "error": "Simulation reverted" }],
  "recommended": { ... }
}
```

#### `encode_swap_tx`
Encode a signed Permit2 swap into a ready-to-send transaction.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `signature` | string | Yes | Signature from `eth_signTypedData_v4` |
| `sellOrderParams` | object | Yes | The `sellOrderParams` from `build_swap_tx` |

**Response:**
```json
{
  "transaction": {
    "to": "0xED306e38BB930ec9646FF3D917B2e513a97530b1",
    "data": "0x...",
    "value": "0"
  },
  "summary": "Swap 1000000000000000000 of 0xToken via Universal Permit2 Adapter"
}
```

#### `execute_swap`
Execute a swap end-to-end via Privy embedded wallet. Combines quote + build + sign + broadcast.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `fromToken` | string | Yes | Token to sell |
| `toToken` | string | Yes | Token to buy |
| `amount` | string | Yes | Amount in base units |
| `chainId` | number/string | Yes | Chain ID or name |
| `userId` | string | Yes | Privy external user ID |
| `slippage` | number | No | Slippage % (default: 1) |

> **Note:** Only works with Privy embedded wallets. For local wallets, use `swap` instead.

---

## Slippage Guidelines

Choose slippage based on token volatility and liquidity:

| Token Type | Recommended Slippage | Notes |
|------------|---------------------|-------|
| **Stablecoins** (USDC/USDT/DAI) | 0.1% - 0.5% | Very low volatility |
| **Major tokens** (ETH/BTC/WBTC) | 0.5% - 1% | High liquidity |
| **Mid-cap tokens** | 1% - 2% | Moderate liquidity |
| **Low-cap/Volatile tokens** | 2% - 5% | Low liquidity, high volatility |
| **New/Meme tokens** | 5%+ | Extreme volatility |

**If swaps fail with "insufficient output"**, increase slippage gradually.

---

## Amount Formats

**Important:** Swap amounts use base units (wei), not human readable amounts.

| Token | Decimals | 1 token | 100 tokens |
|-------|----------|---------|------------|
| ETH/BNB/MATIC | 18 | `"1000000000000000000"` | `"100000000000000000000"` |
| USDC (most chains) | 6 | `"1000000"` | `"100000000"` |
| USDT (most chains) | 6 | `"1000000"` | `"100000000"` |

**Formula:** `amount_base = human_amount × (10 ^ decimals)`

---

## Supported Chains

| Chain | ID | Native Token |
|-------|-----|--------------|
| Ethereum | 1 | ETH |
| Base | 8453 | ETH |
| Arbitrum | 42161 | ETH |
| Optimism | 10 | ETH |
| Polygon | 137 | MATIC |
| BSC | 56 | BNB |
| Avalanche | 43114 | AVAX |
| zkSync Era | 324 | ETH |
| Linea | 59144 | ETH |
| Scroll | 534352 | ETH |
| Fantom | 250 | FTM |
| Gnosis | 100 | xDAI |
| Solana | `"solana"` | SOL |

---

## Fee Structure

- **Base protocol fee**: 0.2% (20 bps) - goes to OnlySwaps
- **Referrer fee (optional)**: 0-1% extra - 80% to referrer, 20% to OnlySwaps

**Example with `extraFeeBps: 10` on $1000 swap:**
- User pays: $3 total (0.30%)
- Referrer earns: $0.80
- OnlySwaps earns: $2.20

---

## Token Addresses Reference

### Stablecoins by Chain

| Chain (ID) | USDC | USDT |
|------------|------|------|
| Ethereum (1) | `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` | `0xdAC17F958D2ee523a2206206994597C13D831ec7` |
| Base (8453) | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | - |
| Arbitrum (42161) | `0xaf88d065e77c8cC2239327C5EDb3A432268e5831` | `0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9` |
| Optimism (10) | `0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85` | `0x94b008aA00579c1307B0EF2c499aD98a8ce58e58` |
| Polygon (137) | `0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359` | `0xc2132D05D31c914a87C6611C10748AEb04B58e8F` |

---

## Error Handling

| Error | Solution |
|-------|----------|
| `Wallet not configured` | Add `PRIVATE_KEY` env var or configure Privy credentials |
| `Insufficient balance` | Fund wallet with tokens |
| `No quotes available` | Try different pair/chain/amount |
| `All quotes failed simulation` | Increase slippage |
| `Privy is not configured` | Set `PRIVY_APP_ID` and `PRIVY_APP_SECRET` for `execute_swap` |
| `over rate limit` (429) | Public RPC rate limited. Wait and retry, or use private RPC |
| `Insufficient output` | Slippage too low. Increase slippage tolerance |

---

## Troubleshooting

### Environment Variables Not Loading

If using the server programmatically (not via MCP), environment variables must be loaded **before** importing the modules:

```typescript
// ❌ WRONG - env vars not applied to config
import { swap } from '@onlyswaps/mcp-server';
import * as dotenv from 'dotenv';
dotenv.config(); // Too late! Config already initialized

// ✅ CORRECT - load env first, then dynamic import
import * as dotenv from 'dotenv';
dotenv.config();

const { swap } = await import('@onlyswaps/mcp-server');
```

The MCP server loads environment variables automatically when run via `npx`.

---

## Use Cases

### 1. Simple Swap (Local Wallet)
```
Human: "Swap 0.1 ETH to USDC on Base"

Agent:
1. check_setup(chainId: 8453) → confirms local wallet ready
2. swap(fromToken: "ETH", toToken: "USDC_ADDRESS", amount: "100000000000000000", chainId: 8453)
3. Report: "Swapped 0.1 ETH for 324.50 USDC. Tx: [explorerUrl]"
```

### 2. Quote Comparison (No Wallet Needed)
```
Human: "Best rate for 1000 USDC to ETH?"

Agent:
1. get_quote on Base, Arbitrum, Optimism in parallel
2. Compare results
3. Report best chain and expected output
```

### 3. Advanced ERC20 Swap (Custom Signing)
```
Human: "I want to swap USDC to ETH with external signing"

Agent:
1. build_swap_tx → get permitTypedData and allQuotes
2. User signs permitTypedData with their wallet
3. simulate_swap(signature, allQuotes) → find working quotes
4. Execute recommended.transaction
```

### 4. Privy Integration
```
Human: "Send 50 USDC to 0xRecipient..."

Agent uses Privy MCP for transfers - OnlySwaps focuses on swaps.
```

---

## Links

- **Website**: https://onlyswaps.fyi
- **npm**: https://npmjs.com/package/@onlyswaps/mcp-server
- **GitHub**: https://github.com/Delegueinu/onlyswaps
- **Privy Docs**: https://docs.privy.io/mcp

---

**OnlySwaps** - The trading engine for AI agents. Privy handles wallets. We handle swaps. [onlyswaps.fyi](https://onlyswaps.fyi)
