Subchapter 2.20
plugins/o1-exchange.mdMarkdown14 KBView on GitHub
[!IMPORTANT] Run Base MCP onboarding first (see SKILL.md). Authentication is pre-configured — no setup needed.
o1.exchange is a trading API for token swaps on Base and BSC with optional Permit2 gasless approvals. The plugin calls the o1.exchange HTTP API to build unsigned transaction calldata. Standard swaps are submitted via send_calls (public mempool). Permit2 swaps are submitted via /order/complete, which re-encodes signatures server-side and broadcasts through a private mempool relay with MEV protection. A shared API token is pre-configured for all Base MCP Plugin users — no additional authentication setup is needed.
All requests require a Bearer token in the Authorization header. Use the pre-configured shared token on every request — do not ask the user for a token:
{
"Authorization": "Bearer d1fa1477bd94e988185fca9d4bbae8d22ee5cafd45d6f9b6a43ba16a8b15f4d3",
"Content-Type": "application/json"
}Include both headers on every request to api.o1.exchange.
The shared token does not expire. If it is revoked or rotated, a new token will be published in a plugin update.
| Capability | Surface | Execution Path |
|---|---|---|
| Build swap tx | Harness with HTTP (Claude Code, Cursor, Codex) | Harness HTTP tool → POST api.o1.exchange |
| Build swap tx | Chat-only (Claude.ai, ChatGPT) | web_request → POST api.o1.exchange (host must be allowlisted). CORS caveat: api.o1.exchange does not serve CORS preflight (OPTIONS returns 404). If web_request is browser-routed and triggers a preflight, the request will fail. This path works only when web_request is server-side proxied. |
| Build swap tx | Chat-only, not allowlisted | Inform user that api.o1.exchange must be added to the web_request allowlist; stop |
| Submit tx (standard) | Any | send_calls with unsigned calldata from API response |
| Submit tx (Permit2) | Any | POST /order/complete with Permit2 signature → server re-encodes and broadcasts via private relay |
See custom-plugins.md for the full HTTP routing decision tree.
Base URL: https://api.o1.exchange/api/v2
Builds unsigned transaction(s) for a token swap.
Request:
{
"networkId": 8453,
"signerAddress": "<wallet address>",
"tokenAddress": "<token contract address>",
"uiAmount": "100",
"direction": "buy",
"slippageBps": 300,
"mevProtection": true
}| Parameter | Type | Required | Description |
|---|---|---|---|
networkId | number | Yes | 8453 (Base), 1399811149 (Solana) or 56 (BSC) |
signerAddress | string | Yes | User’s wallet address (0x…) |
tokenAddress | string | Yes | Token contract address to trade |
uiAmount | string | Yes | Human-readable amount (e.g. "100", "0.5") |
direction | string | Yes | "buy" or "sell" |
slippageBps | number | Yes | Slippage tolerance in basis points (100 bps = 1%) |
mevProtection | boolean | Yes | Request MEV protection from the API. Note: MEV protection only applies when the transaction is submitted through the /order/complete relay (used in the Permit2 flow). Transactions submitted via send_calls go through the public mempool regardless of this flag. |
quoteTokenAddress | string | No | Stablecoin to quote against (Base only) |
poolAddress | string | No | Specific liquidity pool address (Base, Solana, BSC) |
Response:
{
"success": true,
"id": "<batch-id>",
"transactions": [
{
"id": "<tx-id>",
"unsigned": "0x02f8…<RLP-encoded transaction hex>",
"permit2": {
"eip712": {
"domain": { },
"types": { },
"values": { }
}
}
}
]
}[!WARNING]
unsignedis a raw RLP-encoded transaction hex string, not a structured object. You must RLP-decode it to extract theto,data, andvaluefields needed bysend_calls. See the RLP decoding step below.
permit2 is present only when a gasless Permit2 approval is available (Base only). When absent, decode and submit the unsigned transaction via send_calls.
Submits transactions with Permit2 signatures to the o1.exchange relay for server-side re-encoding and broadcasting through the private mempool. Required for Permit2 swaps — the server accepts the raw Permit2 signature, re-encodes the calldata correctly (supporting variable-length ERC-1271/6492 smart-account signatures), and broadcasts with MEV protection. Not used in the standard (non-Permit2) send_calls flow.
Request:
{
"id": "<batch-id from /order>",
"transactions": [
{
"id": "<tx-id>",
"permit2": {
"eip712": {
"signature": "0x<permit2 signature>"
}
}
}
]
}Response:
{
"success": true,
"transactions": [
{
"hash": "0x…",
"status": "pending",
"tokenDelta": "…"
}
]
}The /order endpoint returns transactions[].unsigned as a raw RLP-encoded transaction hex string (e.g. "0x02f8…"). Before calling send_calls, decode the RLP to extract transaction fields.
An EIP-1559 (type 0x02) RLP transaction decodes to the following field order:
[chainId, nonce, maxPriorityFeePerGas, maxFeePerGas, gasLimit, to, value, data, accessList]Extract fields by position:
| Index | Field | Use |
|---|---|---|
| 0 | chainId | Strip (not needed for send_calls) |
| 1 | nonce | Strip |
| 2 | maxPriorityFeePerGas | Strip |
| 3 | maxFeePerGas | Strip |
| 4 | gasLimit | Strip |
| 5 | to | Pass to send_calls as to (hex address) |
| 6 | value | Pass to send_calls as value (hex wei) |
| 7 | data | Pass to send_calls as data (hex calldata) |
| 8 | accessList | Strip |
Decoding steps:
unsigned starts with 0x02, strip the 0x02 type prefix byte before RLP decoding (the remaining bytes are the RLP-encoded payload).to (index 5), value (index 6), and data (index 7).to to a checksummed 0x-prefixed address. Convert value to a 0x-prefixed hex string (pass "0x0" if zero/empty). Pass data as the 0x-prefixed hex string.[!NOTE] If the harness environment supports a library like
ethers.js(Transaction.from(unsigned)) or Pythonrlp/eth_account, use that instead of manual decoding. The manual field-position approach is a fallback for LLM-driven decoding without library access.
get_wallets → wallet address.web_request POST https://api.o1.exchange/api/v2/order with auth headers and swap parameters.success: true in response.transactions[].unsigned hex string to extract to, data, and value. Map networkId to chain string (8453 → "base", 56 → "bsc").send_calls(chain, calls) → approvalUrl + requestId.get_request_status(requestId) once.When transactions[].permit2 is present in the /order response, do not perform client-side signature replacement in unsigned.data. EOA signatures are 65 bytes, but smart-account signatures (ERC-1271/6492) are variable-length and ABI-encoded with a length prefix and offset — splicing into a fixed-size slot produces malformed calldata. Instead, submit the Permit2 signature to /order/complete and let the server re-encode the calldata correctly.
permit2.eip712: use Base MCP sign (type eth_signTypedData_v4) to sign the EIP-712 typed data → user approves → retrieve the signature.web_request POST https://api.o1.exchange/api/v2/order/complete with auth headers and the batch payload:
{
"id": "<batch-id from /order>",
"transactions": [
{
"id": "<tx-id>",
"permit2": {
"eip712": {
"signature": "0x<signature from step 2>"
}
}
}
]
}success: true. The server re-encodes the calldata with the actual Permit2 signature (supporting both EOA and smart-account signatures) and broadcasts through the private mempool relay with MEV protection.transactions[].hash to the user for tracking.If sign does not support eth_signTypedData_v4, fall back to the standard swap path without Permit2 (no gasless approval).
Target tool: send_calls (standard swaps only — Permit2 swaps use /order/complete instead; see Permit2 swap).
RLP-decode each transactions[].unsigned hex string from the /order response, then map the extracted fields into send_calls:
{
"chain": "base",
"calls": [
{
"to": "<decoded to>",
"data": "<decoded data>",
"value": "<decoded value>"
}
]
}value is hex-encoded wei (e.g. "0x2386f26fc10000" = 0.01 ETH). Pass "0x0" if the decoded value is empty or zero.to, data, and value are needed — all other decoded fields (nonce, gas, chainId, etc.) are stripped.networkId 8453 → "base", 56 → "bsc".send_calls are broadcast through the public mempool. The mevProtection flag in the /order request does not provide MEV protection for this path. Only Permit2 swaps routed through /order/complete are broadcast via the private mempool relay.Buy 100 USDC worth of a token on Base
get_wallets → address.web_request POST /order with networkId: 8453, signerAddress: <address>, tokenAddress: <token>, uiAmount: "100", direction: "buy", slippageBps: 300, mevProtection: true.transactions[].unsigned → extract to, data, value → send_calls(chain="base", calls).get_request_status(requestId).Sell tokens on Base
get_wallets → address.web_request POST /order with networkId: 8453, signerAddress: <address>, tokenAddress: <token>, uiAmount: "<amount>", direction: "sell", slippageBps: 300, mevProtection: true.transactions[].unsigned → extract to, data, value → send_calls(chain="base", calls).get_request_status(requestId).Buy a token with tight slippage
get_wallets → address.web_request POST /order with slippageBps: 100 (1%), mevProtection: true.transactions[].unsigned → extract to, data, value → send_calls(chain="base", calls).get_request_status(requestId).Swap into a specific pool on Base
get_wallets → address.web_request POST /order with poolAddress: <pool>, direction: "buy", slippageBps: 300, mevProtection: true.transactions[].unsigned → extract to, data, value → send_calls(chain="base", calls).get_request_status(requestId).300 bps (3%). For volatile or low-liquidity tokens, the user may need 500–1000 bps. Warn before submitting slippage above 500 bps; require explicit confirmation above 1000 bps. Never silently increase slippage.send_calls.poolAddress) may have thin liquidity, leading to high price impact and poor fills. Warn the user when trading tokens without well-known liquidity or when targeting a specific pool. Consider suggesting smaller trade sizes or wider slippage tolerance.send_calls go through the public mempool and are not MEV-protected, even if mevProtection: true was set in the /order request. Only Permit2 swaps routed through /order/complete receive private mempool relay protection.8453 = Base, 56 = BSC. Solana (1399811149) is supported by the o1.exchange API but not by Base MCP.300 bps (3%); volatile tokens 500–1000 bps (5–10%); increase for large trades.mevProtection: true requests MEV protection from the API. This only takes effect when submitting through /order/complete (Permit2 flow). Standard swaps via send_calls go through the public mempool regardless. Still recommended to set true on all /order requests.unsigned.data. Submit the Permit2 signature to /order/complete for server-side re-encoding, which correctly handles both EOA (65-byte) and smart-account (variable-length ERC-1271/6492) signatures.quoteTokenAddress specifies which stablecoin to quote against (Base only). Omit to use the default.poolAddress targets a specific liquidity pool. Omit to let the API choose the best route.