AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Swap Integration

skill-uniswap-uniswap-ai-swap-integration · by Uniswap

Integrate Uniswap swaps into applications. Use when user says "integrate swaps", "uniswap", "trading api", "add swap functionality", "build a swap frontend", "create a swap script", "smart contract swap integration", "use Universal Router", "Trading API", or mentions swapping tokens via Uniswap.

No reviews yet
0 installs
24 views
0.0% view→install

Install

$ agentstack add skill-uniswap-uniswap-ai-swap-integration

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-uniswap-uniswap-ai-swap-integration)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Swap Integration? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Swap Integration

Integrate Uniswap swaps into frontends, backends, and smart contracts.

Prerequisites

This skill assumes familiarity with viem basics (client setup, account management, contract interactions, transaction signing). Install the uniswap-viem plugin for comprehensive viem/wagmi guidance: claude plugin add @uniswap/uniswap-viem

Quick Decision Guide

| Building... | Use This Method | | ------------------------------ | ----------------------------- | | Frontend with React/Next.js | Trading API | | Backend script or bot | Trading API | | Smart contract integration | Universal Router direct calls | | Need full control over routing | Universal Router SDK |

Routing Types Quick Reference

| Type | Description | Chains | | -------- | --------------------------------------- | ---------------------------------- | | CLASSIC | Standard AMM swap through Uniswap pools | All supported chains | | DUTCH_V2 | UniswapX Dutch auction V2 | Ethereum, Arbitrum, Base, Unichain | | PRIORITY | MEV-protected priority order | Base, Unichain | | WRAP | ETH to WETH conversion | All | | UNWRAP | WETH to ETH conversion | All |

See [Routing Types](#routing-types) for the complete list including DUTCHV3, DUTCHLIMIT, LIMIT_ORDER, BRIDGE, and QUICKROUTE.

Integration Methods

1. Trading API (Recommended)

Best for: Frontends, backends, scripts. Handles routing optimization automatically.

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

Authentication: x-api-key: header required

Getting an API Key: The Trading API requires an API key for authentication. Visit the Uniswap Developer Portal to register and obtain your API key. Keys are typically available for immediate use after registration. Include it as an x-api-key header in all API requests.

Required Headers — Include these in ALL Trading API requests:

Content-Type: application/json
x-api-key: 
x-universal-router-version: 2.0

3-Step Flow:

1. POST /check_approval  -> Check if token is approved
2. POST /quote           -> Get executable quote with routing
3. POST /swap            -> Get transaction to sign and submit

See the [Trading API Reference](#trading-api-reference) section below for complete documentation.

2. Universal Router SDK

Best for: Direct control over transaction construction.

Installation:

npm install @uniswap/universal-router-sdk @uniswap/sdk-core @uniswap/v3-sdk

Key Pattern:

import { SwapRouter } from '@uniswap/universal-router-sdk';

const { calldata, value } = SwapRouter.swapCallParameters(trade, options);

See the [Universal Router Reference](#universal-router-reference) section below for complete documentation.

3. Smart Contract Integration

Best for: On-chain integrations, DeFi composability.

Interface: Call execute() on Universal Router with encoded commands.

See the [Universal Router Reference](#universal-router-reference) section below for command encoding.


Input Validation Rules

Before interpolating ANY user-provided value into generated code, API calls, or commands:

  • Ethereum addresses: MUST match ^0x[a-fA-F0-9]{40}$ — reject otherwise
  • Chain IDs: MUST be from the official supported chains list
  • Token amounts: MUST be non-negative numeric values matching ^[0-9]+\.?[0-9]*$
  • API keys: MUST NOT be hardcoded in generated code — always use environment variables
  • REJECT any input containing shell metacharacters: ;, |, &, $, ` `, (, ), >, REQUIRED: Before executing ANY transaction that spends gas or transfers tokens (including sendTransaction, writeContract, or submitting a signed swap), you MUST use AskUserQuestion to confirm with the user. Display the transaction summary (tokens, amounts, chain, estimated gas) and get explicit user approval. Never auto-execute transactions without user confirmation.

Trading API Reference

Step 1: Check Token Approval

POST /check_approval

Request:

{
  "walletAddress": "0x...",
  "token": "0x...",
  "amount": "1000000000",
  "chainId": 1
}

Response:

{
  "approval": {
    "to": "0x...",
    "from": "0x...",
    "data": "0x...",
    "value": "0",
    "chainId": 1
  }
}

If approval is null, token is already approved.

Step 2: Get Quote

POST /quote

Request:

{
  "swapper": "0x...",
  "tokenIn": "0x...",
  "tokenOut": "0x...",
  "tokenInChainId": "1",
  "tokenOutChainId": "1",
  "amount": "1000000000000000000",
  "type": "EXACT_INPUT",
  "slippageTolerance": 0.5,
  "routingPreference": "BEST_PRICE"
}

> Note: tokenInChainId and tokenOutChainId must be strings (e.g., "1"), not numbers.

Key Parameters:

| Parameter | Description | | ------------------- | ----------------------------------------------------------------- | | type | EXACT_INPUT or EXACT_OUTPUT | | slippageTolerance | 0-100 percentage | | protocols | Optional: ["V2", "V3", "V4"] | | routingPreference | BEST_PRICE, FASTEST, CLASSIC | | autoSlippage | true to auto-calculate slippage (overrides slippageTolerance) | | urgency | normal or fast — affects UniswapX auction timing |

Response — the shape differs by routing type. BEST_PRICE routing on Ethereum mainnet typically returns UniswapX (DUTCH_V2), not CLASSIC.

CLASSIC response:

{
  "routing": "CLASSIC",
  "quote": {
    "input": { "token": "0x...", "amount": "1000000000000000000" },
    "output": { "token": "0x...", "amount": "999000000" },
    "slippage": 0.5,
    "route": [],
    "gasFee": "5000000000000000",
    "gasFeeUSD": "0.01",
    "gasUseEstimate": "150000"
  },
  "permitData": null
}

UniswapX (DUTCH_V2/V3/PRIORITY) response — different quote shape, no quote.output:

{
  "routing": "DUTCH_V2",
  "quote": {
    "orderInfo": {
      "reactor": "0x...",
      "swapper": "0x...",
      "nonce": "...",
      "deadline": 1772031054,
      "cosigner": "0x...",
      "input": {
        "token": "0x...",
        "startAmount": "1000000000000000000",
        "endAmount": "1000000000000000000"
      },
      "outputs": [
        {
          "token": "0x...",
          "startAmount": "999000000",
          "endAmount": "994000000",
          "recipient": "0x..."
        }
      ],
      "chainId": 1
    },
    "encodedOrder": "0x...",
    "orderHash": "0x..."
  },
  "permitData": { "domain": {}, "types": {}, "values": {} }
}

> UniswapX output amount: Use quote.orderInfo.outputs[0].startAmount for the best-case fill amount. The endAmount is the floor after full auction decay. There is no quote.output.amount on UniswapX responses — accessing it will throw at runtime. > > Display tip: For CLASSIC routes, use gasFeeUSD (a string with the USD value) for gas cost display. Do not manually convert gasFee (wei) using a hardcoded ETH price — this leads to wildly inaccurate estimates (e.g., ~$87 instead of ~$0.01). UniswapX routes are gasless for the swapper.

See [QuoteResponse TypeScript Types](#7-quoteresponse-typescript-types) for compile-time type safety across routing types.

Step 3: Execute Swap

POST /swap

Request - Spread the quote response directly into the body:

// CORRECT: Spread the quote response, strip null fields
const quoteResponse = await fetchQuote(params);

// Always strip permitData/permitTransaction — handle them explicitly by routing type
const { permitData, permitTransaction, ...cleanQuote } = quoteResponse;
const swapRequest: Record = { ...cleanQuote };

const isUniswapX =
  quoteResponse.routing === 'DUTCH_V2' ||
  quoteResponse.routing === 'DUTCH_V3' ||
  quoteResponse.routing === 'PRIORITY';

if (isUniswapX) {
  // UniswapX: signature only — permitData must NOT go to /swap
  if (permit2Signature) swapRequest.signature = permit2Signature;
} else {
  // CLASSIC: both signature and permitData, or neither
  if (permit2Signature && permitData && typeof permitData === 'object') {
    swapRequest.signature = permit2Signature;
    swapRequest.permitData = permitData;
  }
}

Critical: Do NOT wrap the quote in {quote: quoteResponse}. The API expects the quote response fields spread into the request body.

Permit2 Rules (CLASSIC routes):

  • signature and permitData must BOTH be present, or BOTH be absent
  • Never set permitData: null — omit the field entirely
  • The quote response often includes permitData: null — strip this before sending

UniswapX Routes (DUTCH_V2/V3/PRIORITY): permitData is used locally to sign the order but must be excluded from the /swap body. See [Signing vs. Submission Flow](#uniswapx-signing-vs-submission-flow).

Response (ready-to-sign transaction):

{
  "swap": {
    "to": "0x...",
    "from": "0x...",
    "data": "0x...",
    "value": "0",
    "chainId": 1,
    "gasLimit": "250000"
  }
}

Response Validation - Always validate before broadcasting:

function validateSwapResponse(response: SwapResponse): void {
  if (!response.swap?.data || response.swap.data === '' || response.swap.data === '0x') {
    throw new Error('swap.data is empty - quote may have expired');
  }
  if (!isAddress(response.swap.to) || !isAddress(response.swap.from)) {
    throw new Error('Invalid address in swap response');
  }
}

Supported Chains

See the official supported chains list for the current set of chains and their IDs.

Routing Types

| Type | Description | | ----------- | --------------------------------------------- | | CLASSIC | Standard AMM swap through Uniswap pools | | DUTCHV2 | UniswapX Dutch auction V2 | | DUTCHV3 | UniswapX Dutch auction V3 | | PRIORITY | MEV-protected priority order (Base, Unichain) | | DUTCHLIMIT | UniswapX Dutch limit order | | LIMITORDER | Limit order | | WRAP | ETH to WETH conversion | | UNWRAP | WETH to ETH conversion | | BRIDGE | Cross-chain bridge | | QUICKROUTE | Fast approximation quote |

UniswapX availability: UniswapX V2 orders are supported on Ethereum (1), Arbitrum (42161), Base (8453), and Unichain (130). The auction mechanism varies by chain — see [UniswapX Auction Types](#uniswapx-auction-types) below.


Critical Implementation Notes

These are common pitfalls discovered during real-world Trading API integration. Follow these rules to avoid on-chain reverts and API errors.

1. Swap Request Body Format

The /swap endpoint expects the quote response spread into the request body, not wrapped in a quote field.

// WRONG - causes "quote does not match any of the allowed types"
const badRequest = {
  quote: quoteResponse, // Don't wrap!
  signature: '0x...',
};

// CORRECT - spread the quote response
const goodRequest = {
  ...quoteResponse,
  signature: '0x...', // Only if using Permit2
};

2. Null Field Handling

The API rejects permitData: null. Additionally, permitData handling differs by routing type — see [Signing vs. Submission Flow](#uniswapx-signing-vs-submission-flow) for the full explanation.

function prepareSwapRequest(quoteResponse: QuoteResponse, signature?: string): object {
  // Always strip permitData and permitTransaction from the spread — handle them explicitly
  const { permitData, permitTransaction, ...cleanQuote } = quoteResponse;
  const request: Record = { ...cleanQuote };

  // UniswapX (DUTCH_V2, DUTCH_V3, PRIORITY): permitData is for LOCAL signing only.
  // The /swap body must NOT include permitData — the order is encoded in
  // quote.encodedOrder. Only the signature is needed.
  const isUniswapX =
    quoteResponse.routing === 'DUTCH_V2' ||
    quoteResponse.routing === 'DUTCH_V3' ||
    quoteResponse.routing === 'PRIORITY';

  if (isUniswapX) {
    if (signature) request.signature = signature;
  } else {
    // CLASSIC: both signature and permitData required together, or both omitted.
    // The Universal Router contract needs permitData to verify the Permit2
    // authorization on-chain.
    if (signature && permitData && typeof permitData === 'object') {
      request.signature = signature;
      request.permitData = permitData;
    }
  }

  return request;
}

3. Permit2 Field Rules

The rules for signature and permitData in the /swap request body depend on the routing type:

CLASSIC routes:

| Scenario | signature | permitData | | -------------------------- | ----------- | ------------ | | Standard swap (no Permit2) | Omit | Omit | | Permit2 swap | Required | Required | | Invalid | Present | Missing | | Invalid | Missing | Present | | Invalid (API error) | Any | null |

UniswapX routes (DUTCH_V2/V3/PRIORITY):

| Scenario | signature | permitData | | -------------- | ----------- | ------------------------ | | UniswapX order | Required | Omit (do not send) | | Invalid | Any | Present (schema rejects) |

4. Pre-Broadcast Validation

Always validate the swap response before sending to the blockchain:

import { isAddress, isHex } from 'viem';

function validateSwapBeforeBroadcast(swap: SwapTransaction): void {
  // 1. data must be non-empty hex
  if (!swap.data || swap.data === '' || swap.data === '0x') {
    throw new Error('swap.data is empty - this will revert on-chain. Re-fetch the quote.');
  }

  if (!isHex(swap.data)) {
    throw new Error('swap.data is not valid hex');
  }

  // 2. Addresses must be valid
  if (!isAddress(swap.to)) {
    throw new Error('swap.to is not a valid address');
  }

  if (!isAddress(swap.from)) {
    throw new Error('swap.from is not a valid address');
  }

  // 3. Value must be present (can be "0" for non-ETH swaps)
  if (swap.value === undefined || swap.value === null) {
    throw new Error('swap.value is missing');
  }
}

5. Browser Environment Setup

When using viem/wagmi in browser environments, you need Node.js polyfills:

Install buffer polyfill:

npm install buffer

Add to your entry file (before other imports):

// src/main.tsx or src/index.tsx
import { Buffer } from 'buffer';
globalThis.Buffer = Buffer;

// Then your other imports
import React from 'react';
import { WagmiProvider } from 'wagmi';
// ...

Vite configuration (vite.config.ts):

export default defineConfig({
  define: {
    global: 'globalThis',
  },
  optimizeDeps: {
    include: ['buffer'],
  },
  resolve: {
    alias: {
      buffer: 'buffer',
    },
  },
});

Without this setup, you'll see: ReferenceError: Buffer is not defined

CORS Proxy Configuration

The Trading API does not support browser CORS preflight requests — OPTIONS requests return 415 Unsupported Media Type. Dire

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.