Index of every page on this site: llms.txt
All skills
base avatar

/wallet-mcp

@ef8bcd5 official
by basebase/skills120 stars
130
Run it yourself

Wallet MCP — gives your AI assistant access to a Coinbase Wallet Account via the Wallet MCP server (wallet-mcp.coinbase.com). Wallet, portfolio, sending, swapping, signing, x402 payments, batched contract calls, and transaction history across Base, Ethereum, Arbitrum, Optimism, Polygon, BNB Chain, Avalanche, and Robinhood Chain.

Use this Skill: https://skilld.dev/gh/base/skills/wallet-mcp

Nothing lands on disk. Nothing to clean up.

Fork this Skill

Edit a local copy. It keeps the author and licence.

pluginsuniswap.md

≈3.9k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Uniswap Plugin

[!IMPORTANT] Complete the short Wallet MCP onboarding flow defined in SKILL.md before calling any Uniswap endpoint. The user's wallet address — passed as walletAddress in every swap and LP call — is fetched lazily when needed.

Overview

Uniswap on Base: token swaps using the proxy-approval flow (no Permit2 signing) and LP position management for V2, V3, and V4. The plugin fetches unsigned calldata from the Uniswap API, then executes transaction previews with send_calls. No additional MCP server is required.

Chain: Base mainnet (chainId 8453).

Auth

Use these headers for all requests:

{
  "Content-Type": "application/json",
  "x-api-key": "NeoYO3V50_koJAipDEalYWbMO1XMaFPAQmpOm6_Npo0"
}

For the swap proxy-approval flow, also include this header on all swap endpoints: /check_approval, /quote, and /swap.

{
  "x-permit2-disabled": "true"
}

Without x-permit2-disabled, Uniswap can return Permit2 or Universal Router behavior instead of the proxy-approval flow described here.

Surface Routing

Uniswap is HTTP-only; every capability follows the standard HTTP routing in ../references/custom-plugins.md.

Capability Path
Swap quote / approval / calldata Harness HTTP tool if available, else web_request POST against trade-api.gateway.uniswap.org.
LP discovery / create / manage / claim Harness HTTP tool or web_request POST against liquidity.api.uniswap.org.
Execute the prepared calldata Wallet MCP send_calls (works on every surface).

Prerequisite: trade-api.gateway.uniswap.org and liquidity.api.uniswap.org must be in the MCP server's web_request allowlist. If requests are rejected by the allowlist and no harness HTTP tool is available, inform the user.

Endpoints

Swap endpoints: proxy approval, no Permit2

Base URL: https://trade-api.gateway.uniswap.org/v1

POST /check_approval  ->  if approval non-null, include approval calldata in send_calls
POST /quote           ->  get best route, read-only
POST /swap            ->  get unsigned swap tx, include swap calldata in send_calls
1. POST /check_approval

Headers: auth headers plus "x-permit2-disabled": "true".

{
  "walletAddress": "<address>",
  "token": "<tokenIn address>",
  "amount": "<base units string>",
  "chainId": 8453,
  "includeGasInfo": true
}

Response:

{
  "approval": {
    "to": "<token contract>",
    "data": "<calldata>",
    "value": "0x00",
    "chainId": 8453
  }
}

approval can be null, especially for native ETH. If it is non-null, pass it to send_calls. You can batch the approval and swap together after /swap returns.

2. POST /quote

Headers: auth headers plus "x-permit2-disabled": "true".

{
  "type": "EXACT_INPUT",
  "amount": "<base units string>",
  "tokenIn": "<address or 0x0000000000000000000000000000000000000000 for native ETH>",
  "tokenOut": "<address>",
  "tokenInChainId": 8453,
  "tokenOutChainId": 8453,
  "swapper": "<address>",
  "autoSlippage": "DEFAULT",
  "protocols": ["V4", "V3", "V2"],
  "routingPreference": "BEST_PRICE"
}

Use "slippageTolerance": <0-20> instead of autoSlippage if the user specifies slippage. See Risks & Warnings before submitting elevated values.

Response includes a top-level quote object plus metadata. Keep the response as a flat object for /swap; do not nest it under a quote key.

3. POST /swap

Headers: auth headers plus "x-permit2-disabled": "true".

Use the /quote response as the body, but remove any null or absent permit fields before sending:

const swapBody = { ...quoteResponse };
if (swapBody.permitData == null) delete swapBody.permitData;
if (swapBody.permitTransaction == null) delete swapBody.permitTransaction;
delete swapBody.signature;

Do not send signature, and do not send permitData or permitTransaction when they are null.

Response:

{
  "swap": {
    "to": "<router or proxy>",
    "data": "<calldata>",
    "value": "0x00",
    "chainId": 8453
  },
  "gasFee": "..."
}

LP endpoints

Base URL: https://liquidity.api.uniswap.org/lp

Use this host for LP endpoints in this plugin environment. Do not switch to api.uniswap.org or trade-api.gateway.uniswap.org/v1/lp/... unless that host is explicitly available to the API key and MCP allowlist.

Pool Discovery: POST /lp/pool_info

Use pool discovery before creating V3/V4 LP positions. poolReference must be a valid pool address for V3 or pool ID for V4.

{
  "protocol": "V4",
  "chainId": 8453,
  "poolParameters": {
    "tokenAddressA": "0x0000000000000000000000000000000000000000",
    "tokenAddressB": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "fee": 3000,
    "tickSpacing": 60
  }
}

Known Base V4 ETH/USDC fee=3000, tickSpacing=60 pool reference observed in testing:

0xe070797535b13431808f8fc81fdbe7b41362960ed0b55bc2b6117c49c51b7eb9

Pool references can change by pair, fee, tick spacing, and protocol. Prefer querying /lp/pool_info instead of hard-coding a pool reference unless the user explicitly selected a pool.

Approval Step: POST /lp/check_approval

Use this before LP create/increase/decrease operations. The body uses lpTokens as an array of token/amount objects.

{
  "protocol": "V4",
  "walletAddress": "<address>",
  "chainId": 8453,
  "lpTokens": [
    {
      "tokenAddress": "0x0000000000000000000000000000000000000000",
      "amount": "<base units>"
    },
    {
      "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "amount": "<base units>"
    }
  ],
  "action": "CREATE",
  "generatePermitAsTransaction": true,
  "simulateTransaction": true
}

action: "CREATE" | "INCREASE" | "DECREASE" | "MIGRATE"

Response:

{
  "requestId": "...",
  "transactions": [
    {
      "transaction": {
        "to": "...",
        "from": "...",
        "data": "...",
        "value": "0x00",
        "chainId": 8453
      },
      "cancelApproval": false,
      "action": "CREATE"
    }
  ]
}

If transactions is empty, no approval transaction is needed. If it has entries, map every transactions[*].transaction to send_calls.

const approvalCalls = approvalResponse.transactions.map((entry) => ({
  to: entry.transaction.to,
  value: entry.transaction.value ?? "0x0",
  data: entry.transaction.data
}));
Create V3/V4 Position: POST /lp/create
{
  "walletAddress": "<address>",
  "chainId": 8453,
  "protocol": "V4",
  "existingPool": {
    "token0Address": "0x0000000000000000000000000000000000000000",
    "token1Address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "poolReference": "0xe070797535b13431808f8fc81fdbe7b41362960ed0b55bc2b6117c49c51b7eb9"
  },
  "independentToken": {
    "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "amount": "1000000"
  },
  "tickBounds": {
    "tickLower": -200400,
    "tickUpper": -199200
  },
  "simulateTransaction": false,
  "nativeTokenBalance": "1000000000000000"
}

Use newPool instead of existingPool when creating a pool:

{
  "token0Address": "<address>",
  "token1Address": "<address>",
  "fee": 3000,
  "tickSpacing": 60,
  "initialPrice": "<sqrtRatioX96 string>"
}

Response:

{
  "create": { "to": "...", "data": "...", "value": "...", "chainId": 8453 },
  "token0": { "tokenAddress": "...", "amount": "..." },
  "token1": { "tokenAddress": "...", "amount": "..." },
  "adjustedMinPrice": "...",
  "adjustedMaxPrice": "...",
  "tickLower": -200400,
  "tickUpper": -199200
}

Prefer tickBounds when possible. priceBounds can be accepted by the API, but its price units are easy to misread; validate carefully before using it.

Manage Existing Positions
Action Endpoint Key params
Add liquidity POST /lp/increase walletAddress, chainId, protocol, token0Address, token1Address, nftTokenId, independentToken { tokenAddress, amount }
Remove liquidity POST /lp/decrease walletAddress, chainId, protocol, token0Address, token1Address, nftTokenId, liquidityPercentageToDecrease (0-100)
Collect fees POST /lp/claim_fees walletAddress, chainId, protocol, tokenId; optional simulateTransaction
Create V2 position POST /lp/create_classic walletAddress, poolParameters { token0Address, token1Address, chainId }, independentToken { tokenAddress, amount }

Optional on LP operation endpoints: "slippageTolerance": <decimal> where 0.5 means 0.5%. See Risks & Warnings before submitting elevated values.

Important: LP APIs can return calldata for syntactically valid nftTokenId values even if the connected wallet may not own the position. Treat generated calldata as a transaction preview input, not proof of ownership or guaranteed execution.

Claim Fees: POST /lp/claim_fees
{
  "protocol": "V4",
  "walletAddress": "<address>",
  "chainId": 8453,
  "tokenId": "<nft token id>",
  "simulateTransaction": false
}

Response:

{
  "claim": { "to": "...", "data": "...", "value": "0x00", "chainId": 8453 },
  "token0": { "tokenAddress": "...", "amount": "..." },
  "token1": { "tokenAddress": "...", "amount": "..." }
}

No approval step is needed for fee collection.

Create V2 Position: POST /lp/create_classic
{
  "walletAddress": "<address>",
  "poolParameters": {
    "token0Address": "0x4200000000000000000000000000000000000006",
    "token1Address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "chainId": 8453
  },
  "independentToken": {
    "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "amount": "1000000"
  },
  "simulateTransaction": false
}

For V2 create, /lp/check_approval may return multiple approval transactions. Batch all approvals plus the create transaction if you want one send_calls approval link.

Orchestration

Swap orchestration

1. get_wallets -> address; convert tokenIn amount to base units
2. web_request POST /check_approval with x-permit2-disabled
3. web_request POST /quote with x-permit2-disabled
4. Build swapBody from quoteResponse and remove null permit fields
5. web_request POST /swap with x-permit2-disabled
6. send_calls("base", approval + swap calls if approval exists, otherwise swap only)
7. Open the approvalUrl if requested; do not approve unless the user explicitly asks
8. get_request_status only after the user acts

LP orchestration

1. get_wallets -> address
2. For V3/V4 create, call /lp/pool_info to discover poolReference
3. Build LP token amount list in base units
4. web_request POST /lp/check_approval
5. web_request POST /lp/create, /lp/increase, /lp/decrease, /lp/claim_fees, or /lp/create_classic
6. send_calls("base", approval calls + operation call)
7. Open the approvalUrl if requested; do not approve unless the user explicitly asks
8. get_request_status only after the user acts

Submission

Target tool: send_calls (chain "base"). Open the returned approvalUrl only if the user asks, and poll get_request_status after they act (see ../references/approval-mode.md and ../references/batch-calls.md).

Swap send_calls

Approval and swap can be sent separately or batched in one send_calls preview:

{
  "chain": "base",
  "calls": [
    { "to": "<approval.to>", "value": "<approval.value>", "data": "<approval.data>" },
    { "to": "<swap.to>", "value": "<swap.value>", "data": "<swap.data>" }
  ]
}

LP send_calls

For any LP operation response field such as create, increase, decrease, or claim, map to:

{
  "chain": "base",
  "calls": [
    { "to": "<tx.to>", "value": "<tx.value>", "data": "<tx.data>" }
  ]
}

For approval + action batching:

const operationTx =
  operationResponse.create ??
  operationResponse.increase ??
  operationResponse.decrease ??
  operationResponse.claim;

const calls = [
  ...approvalResponse.transactions.map((entry) => ({
    to: entry.transaction.to,
    value: entry.transaction.value ?? "0x0",
    data: entry.transaction.data
  })),
  {
    to: operationTx.to,
    value: operationTx.value ?? "0x0",
    data: operationTx.data
  }
];

Example Prompts

Swap 1 USDC to WETH on Base

  1. get_wallets -> address; use USDC address 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, amount 1000000.
  2. web_request POST /check_approval with x-permit2-disabled: true.
  3. web_request POST /quote with x-permit2-disabled: true.
  4. Remove null permit fields from quote response.
  5. web_request POST /swap with x-permit2-disabled: true.
  6. send_calls approval + swap, or swap only if no approval was returned.

Create a V4 ETH/USDC LP position on Base

  1. get_wallets -> address.
  2. web_request POST /lp/pool_info to find the pool reference.
  3. web_request POST /lp/check_approval using lpTokens array.
  4. web_request POST /lp/create using existingPool.poolReference and tickBounds.
  5. send_calls approval transactions plus create transaction.

Add liquidity to an existing V4 position

  1. get_wallets -> address.
  2. Confirm the wallet owns the nftTokenId or warn that generated calldata may still fail.
  3. web_request POST /lp/check_approval with action "INCREASE".
  4. web_request POST /lp/increase.
  5. send_calls approval transactions plus increase transaction.

Collect LP fees

  1. get_wallets -> address.
  2. web_request POST /lp/claim_fees with tokenId.
  3. send_calls claim transaction.

Risks & Warnings

High slippage tolerance exposes the user to worse fills and sandwich/MEV attacks. Before calling /swap or any LP endpoint with an elevated value, warn the user and get explicit confirmation:

Tolerance Level Action
≤ 1% Normal Proceed.
> 1% and ≤ 5% Elevated Mention the value and ask the user to confirm.
> 5% and ≤ 20% High Warn that the trade can fill significantly below quote and is a likely sandwich target. Require explicit confirmation.
> 20% Very high Strongly warn; do not submit without the user re-confirming the exact number.

Apply the same thresholds to swap and LP slippage. If the user did not specify a value, prefer autoSlippage: "DEFAULT" on swaps rather than picking a high number.

Notes

  • Native ETH address: 0x0000000000000000000000000000000000000000
  • USDC on Base: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
  • WETH on Base: 0x4200000000000000000000000000000000000006
  • Token amounts are base units: USDC = 1e6 per token, ETH/WETH = 1e18 per token.
  • Use chain: "base" with send_calls, not numeric chain id.
  • For swap proxy approval, include x-permit2-disabled: true on /check_approval, /quote, and /swap.
  • For swap proxy approval, remove null permitData and permitTransaction fields before calling /swap.
  • /quote response fields must be spread directly into /swap body, not nested under a quote key.
  • Do not send signature in the proxy swap flow.

Source: SKILL.md on GitHub

skilld matched fixed text patterns in SKILL.md and file names. Patterns miss obfuscated code.

skilld run checks every file with the same patterns. It asks for approval before it loads a Skill with a behavior marked Needs approval.

1 alert14d3 checks · Risk CRITICAL
  • Gen Agent Trust Hub14d

    The skill integrates with various crypto protocols but contains severe security risks, including hardcoded API credentials and instructions to execute third-party code packages. Most importantly, it utilizes a service domain (flaunch.gg) that is blacklisted as malicious. It also instructions the AI to dynamically load additional instructions from remote servers.

  • Socket14d

    3 alerts: gptSecurity, gptAnomaly

  • Snyk14d

    Risk: MEDIUM · 2 issues

Signed by skilld at ef8bcd5. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 weeks ago.

Activeupdated 2 weeks ago
title
Wallet MCP Skill
version
0.1.0

README badge

README badge for base/skills/wallet-mcp