diff --git a/api/feedback.ts b/api/feedback.ts new file mode 100644 index 000000000..e6332ca12 --- /dev/null +++ b/api/feedback.ts @@ -0,0 +1,64 @@ +const HUBSPOT_PORTAL_ID = '47435488' +const HUBSPOT_FORM_ID = '80c7a6ab-9b96-412f-9469-aa2bc14faa18' +const HUBSPOT_SUBMIT_URL = `https://api.hsforms.com/submissions/v3/integration/submit/${HUBSPOT_PORTAL_ID}/${HUBSPOT_FORM_ID}` + +function isValidEmail(value: string): boolean { + return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) +} + +export default async function handler(req: any, res: any) { + if (req.method !== 'POST') { + return res.status(405).json({ success: false, error: 'Method not allowed' }) + } + + try { + const body = typeof req.body === 'string' ? JSON.parse(req.body) : req.body || {} + const { email, feedbackType, issue, followUp, challenges, docsUsefulness, pageUrl, website } = body + + if (website) return res.status(200).json({ success: true }) // honeypot + if (!email || !isValidEmail(email)) return res.status(400).json({ success: false, error: 'Invalid email' }) + if (!feedbackType || !issue) return res.status(400).json({ success: false, error: 'Missing required fields' }) + + const fields = [ + { name: 'email', value: String(email) }, + { name: 'type_of_feedback', value: String(feedbackType) }, + { name: 'whats_the_issue_idea_or_question', value: String(issue) }, + { name: 'can_we_follow_up_with_you_about_your_feedback', value: followUp ? 'Yes' : 'No' }, + ...(challenges?.trim() + ? [{ name: 'what_has_been_the_most_challenging_part_of_building_on_or_integrating_with_uniswap', value: challenges.trim() }] + : []), + ...(docsUsefulness?.trim() + ? [{ name: 'have_you_found_uniswap_docs_to_be_useful', value: docsUsefulness.trim() }] + : []), + ] + + const payload = { + fields, + legalConsentOptions: { + consent: { + consentToProcess: true, + text: 'By submitting, I agree to Uniswap Labs Terms of Service and Privacy Policy.', + }, + }, + context: { + pageUri: pageUrl || '', + pageName: 'Feedback | Uniswap Docs', + }, + } + + const hsRes = await fetch(HUBSPOT_SUBMIT_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }) + + if (!hsRes.ok) { + const hsText = await hsRes.text() + return res.status(502).json({ success: false, error: 'HubSpot submission failed', details: hsText }) + } + + return res.status(200).json({ success: true }) + } catch { + return res.status(500).json({ success: false, error: 'Internal server error' }) + } +} \ No newline at end of file diff --git a/docs/api/overview.md b/docs/api/overview.md index 4ffa7605b..3a9a9a323 100644 --- a/docs/api/overview.md +++ b/docs/api/overview.md @@ -3,10 +3,9 @@ id: overview sidebar_position: 1 title: Overview --- - ## Uniswap APIs -Welcome to the Uniswap API documentation. Uniswap provides several APIs and data sources to help developers integrate with and build on top of the Uniswap protocol. +Uniswap provides several APIs and data sources to help developers integrate with and build on top of the Uniswap protocol. ## Available APIs @@ -14,7 +13,7 @@ Welcome to the Uniswap API documentation. Uniswap provides several APIs and data The Uniswap Trading API provides quote generation and transaction building for token swaps across 25+ chains. - **[Trading API Overview](./trading/overview)** - Get started with the Trading API -- **[Integration Guide](./trading/integration-guide)** - Complete implementation guide with schemas and best practices +- **[Integration Guide](https://api-docs.uniswap.org/guides/integration_guide)** - Complete implementation guide with schemas and best practices - **Quote & Swap Endpoints** - Generate quotes and build unsigned transactions - **Permit2 Support** - Gasless approvals via EIP-712 signatures - **Cross-Chain Swaps** - Multi-step cross-chain swap support @@ -44,10 +43,10 @@ Get real-time and historical price data for tokens on Uniswap. ### For Developers If you're building applications that need to: -- Execute token swaps → Use the **[Trading API](./trading/overview)** -- Query historical trading data → Use the **Subgraph API** -- Get optimal swap routes → Use the **Routing API** -- Display token prices → Use the **Price APIs** +- Execute token swaps - Use the **[Trading API](https://developers.uniswap.org/dashboard/)** +- Query historical trading data - Use the **Subgraph API** +- Get optimal swap routes - Use the **Routing API** +- Display token prices - Use the **Price APIs** ### For Data Analysis The Subgraph API is perfect for: @@ -60,12 +59,4 @@ The Subgraph API is perfect for: - **Subgraph API**: Generous rate limits via The Graph - **Routing API**: Production-ready with caching -- **Price APIs**: Real-time updates with historical data - -## Support and Resources - -- **Discord**: Join the Uniswap developer community -- **GitHub**: Explore code examples and integrations -- **Documentation**: Comprehensive guides and references - -Ready to start building? Choose the API that fits your needs from the navigation menu. \ No newline at end of file +- **Price APIs**: Real-time updates with historical data \ No newline at end of file diff --git a/docs/api/trading/integration-guide.md b/docs/api/trading/integration-guide.md deleted file mode 100644 index 9566545bf..000000000 --- a/docs/api/trading/integration-guide.md +++ /dev/null @@ -1,818 +0,0 @@ ---- -id: integration-guide -sidebar_position: 2 -title: Integration Guide ---- - -# Trading API Integration Guide - -This guide provides comprehensive documentation for integrating with the Uniswap Trading API, including schema definitions, validation requirements, error handling, and best practices. - -## Table of Contents - -- [API Endpoints](#api-endpoints) -- [Schema Reference](#schema-reference) -- [Permit2 Flow](#permit2-flow) -- [Error Handling](#error-handling) -- [Best Practices](#best-practices) -- [Troubleshooting](#troubleshooting) - -## API Endpoints - -### POST /quote - -Generate a quote for a token swap. - -**Request Body:** - -```typescript -interface QuoteRequest { - // Required - tokenIn: string; // Token address to swap from - tokenOut: string; // Token address to swap to - tokenInChainId: number; // Chain ID for input token - tokenOutChainId: number; // Chain ID for output token - type: 'EXACT_INPUT' | 'EXACT_OUTPUT'; - amount: string; // Amount in token's smallest unit (wei) - swapper: string; // User's wallet address - - // Slippage (choose one, mutually exclusive) - slippageTolerance?: number; // Manual slippage as percentage (e.g., 0.5 = 0.5%) - autoSlippage?: 'DEFAULT'; // Automatic slippage calculation - - // Optional - protocols?: Protocol[]; // Limit to specific protocols - routingPreference?: RoutingPreference; - urgency?: 'urgent' | 'normal'; - permitAmount?: 'FULL' | 'EXACT'; - gasStrategies?: GasStrategy[]; -} -``` - -**Response:** - -```typescript -interface QuoteResponse { - requestId: string; - routing: SwapType; // See SwapType enum - - // Quote (one of, depends on routing type) - classicQuote?: ClassicQuote; - bridgeQuote?: BridgeQuote; - dutchLimitQuote?: DutchLimitQuote; - dutchLimitV2Quote?: DutchLimitV2Quote; - dutchLimitV3Quote?: DutchLimitV3Quote; - wrapUnwrapQuote?: WrapUnwrapQuote; - priorityQuote?: PriorityQuote; - chainedQuote?: ChainedQuote; - - // Permit data (if permit requested) - permitSingleData?: PermitSingleData; - permitTransferFromData?: PermitTransferFromData; - - permitTransaction?: TransactionRequest; - permitGasFee?: string; -} -``` - -### POST /swap - -Convert a quote into an unsigned transaction ready for signing. - -**Request Body:** - -```typescript -interface SwapRequest { - // Quote (required) - quote: ClassicQuote | WrapUnwrapQuote | BridgeQuote; - - // Permit2 signature (optional, but both required if either present) - signature?: string; // EIP-712 signature for Permit2 - permitData?: PermitSingleData; // Required if signature provided - - // Optional parameters - safetyMode?: 'RELAXED' | 'SAFE'; - deadline?: number; // Unix timestamp - simulateTransaction?: boolean; - refreshGasPrice?: boolean; - urgency?: 'urgent' | 'normal'; - gasStrategies?: GasStrategy[]; -} -``` - -:::warning Important -The `signature` and `permitData` fields must either both be present or both be omitted. See [Permit2 Flow](#permit2-flow) for details. -::: - -**Response:** - -```typescript -interface SwapResponse { - requestId: string; - swap: TransactionRequest; // Transaction to sign and broadcast - gasFee?: string; - gasEstimates?: GasEstimate[]; - txFailureReasons?: TransactionFailureReason[]; -} -``` - -### POST /check_approval - -Check if token approval is required before swapping. - -**Request Body:** - -```typescript -interface CheckApprovalRequest { - walletAddress: string; - token: string; - amount: string; - chainId: number; - urgency?: 'urgent' | 'normal'; - includeGasInfo?: boolean; - tokenOut?: string; - tokenOutChainId?: number; -} -``` - -**Response:** - -```typescript -interface CheckApprovalResponse { - requestId: string; - approval: TransactionRequest; // Approval transaction (if needed) - cancel: TransactionRequest; // Cancel approval transaction - gasFee?: string; - cancelGasFee?: string; - gasEstimates?: GasEstimate[]; -} -``` - -### POST /swap_5792 - -Generate a batch of transactions for EIP-5792 wallet_sendCalls. - -**Request Body:** - -```typescript -interface Swap5792Request { - classicQuote?: ClassicQuote; - wrapUnwrapQuote?: WrapUnwrapQuote; - bridgeQuote?: BridgeQuote; - permitData?: PermitSingleData; - deadline: number; // Unix timestamp (required) - urgency?: 'urgent' | 'normal'; -} -``` - -**Response:** - -```typescript -interface Swap5792Response { - requestId: string; - from: string; - chainId: number; - calls: TransactionRequest5792[]; - gasFee?: string; - gasEstimates?: GasEstimate[]; -} - -interface TransactionRequest5792 { - to: string; - data: string; - value: string; -} -``` - -### POST /swap_7702 - -Generate a transaction with EIP-7702 delegation. - -**Request Body:** - -```typescript -interface Swap7702Request { - classicQuote?: ClassicQuote; - wrapUnwrapQuote?: WrapUnwrapQuote; - bridgeQuote?: BridgeQuote; - smartContractDelegationAddress: string; // Required - permitData?: PermitSingleData; - deadline?: number; - urgency?: 'urgent' | 'normal'; - gasStrategies?: GasStrategy[]; -} -``` - -### Cross-Chain Plans - -For cross-chain swaps (where `tokenInChainId !== tokenOutChainId`): - -- **POST /plan** - Create a plan from a CHAINED quote -- **GET /plan/:planId** - Check plan status -- **PATCH /plan/:planId** - Update plan with transaction hashes/signatures - -```typescript -// Example: Create cross-chain plan -const quoteResponse = await fetch('/quote', { - method: 'POST', - headers: { - 'x-api-key': API_KEY, - 'x-chained-actions-enabled': 'true', // Required header - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - tokenIn: '0x...', - tokenInChainId: 1, // Ethereum - tokenOut: '0x...', - tokenOutChainId: 8453, // Base - // ... other params - }) -}); -``` - -## Schema Reference - -### TransactionRequest - -The `TransactionRequest` object returned by `/swap` contains all fields needed to broadcast a transaction: - -```typescript -interface TransactionRequest { - to: string; // Contract address to call - from: string; // User's wallet address - data: string; // Encoded function call (hex string) - value: string; // Native token amount (wei) - chainId: number; - gasLimit?: string; - maxFeePerGas?: string; - maxPriorityFeePerGas?: string; - gasPrice?: string; // Legacy gas price -} -``` - -#### Critical Field: `data` - -The `data` field contains the encoded contract call and **must always be present** in swap transactions. - -:::danger Validation Requirements -1. **Never Empty**: The `data` field must be a non-empty hex string (not `""` or `"0x"`) -2. **Always Validate**: Check `data` exists before broadcasting -3. **Revert Prevention**: Empty `data` causes on-chain transaction reverts -::: - -**Validation Example:** - -```typescript -function validateTransaction(tx: TransactionRequest): void { - // Critical validation - if (!tx.data || tx.data === '' || tx.data === '0x') { - throw new Error('Transaction data is empty - invalid swap transaction'); - } - - if (!tx.to || !tx.from) { - throw new Error('Missing required transaction fields'); - } - - // Verify valid hex - if (!/^0x[0-9a-fA-F]*$/.test(tx.data)) { - throw new Error('Transaction data is not valid hex'); - } -} - -// Use before broadcasting -const swapResponse = await fetch('/swap', {...}); -const {swap} = await swapResponse.json(); - -validateTransaction(swap); // Validate before signing - -const signedTx = await wallet.signTransaction(swap); -const txHash = await provider.sendTransaction(signedTx); -``` - -### PermitSingleData - -For Permit2-based approvals: - -```typescript -interface PermitSingleData { - domain: TypedDataDomain; - values: PermitSingle; - types: Record; -} - -interface PermitSingle { - details: { - token: string; - amount: string; - expiration: string; - nonce: string; - }; - spender: string; - sigDeadline: string; -} -``` - -## Permit2 Flow - -Permit2 enables gasless token approvals via EIP-712 signatures. - -### When to Use Permit2 - -Use Permit2 when: -- Swapping ERC-20 tokens (not native tokens) -- User hasn't approved the token for Permit2 -- You want to minimize transaction count (1 tx instead of 2) - -### Implementation Steps - -#### 1. Get Quote with Permit Data - -```typescript -const quoteResponse = await fetch('/quote', { - method: 'POST', - headers: { - 'x-api-key': API_KEY, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - tokenIn: '0x...', - tokenOut: '0x...', - amount: '1000000', - type: 'EXACT_INPUT', - swapper: '0x...', - tokenInChainId: 1, - tokenOutChainId: 1, - permitAmount: 'EXACT', // or 'FULL' - slippageTolerance: 0.5 - }) -}); - -const {quote} = await quoteResponse.json(); -``` - -#### 2. Sign the Permit - -```typescript -let signature: string | undefined; -let permitData: PermitSingleData | undefined; - -if (quote.permitData) { - signature = await wallet._signTypedData( - quote.permitData.domain, - quote.permitData.types, - quote.permitData.values - ); - permitData = quote.permitData; -} -``` - -#### 3. Submit to /swap - -:::warning Critical Field Requirements -When including a Permit2 signature, both `signature` AND `permitData` must be provided: -::: - -```typescript -// CORRECT - Both fields provided -const swapRequest = { - classicQuote: quote, - signature: signature, - permitData: permitData -}; - -// WRONG - Missing permitData -const swapRequest = { - classicQuote: quote, - signature: signature // Will fail validation -}; - -// WRONG - Null permitData -const swapRequest = { - classicQuote: quote, - signature: signature, - permitData: null // API rejects null, omit field instead -}; - -// CORRECT - No permit (both fields omitted) -const swapRequest = { - classicQuote: quote - // signature and permitData omitted entirely -}; -``` - -**Field Omission Rules:** - -1. **If using Permit2**: Include both `signature` and `permitData` -2. **If NOT using Permit2**: Omit both fields entirely (don't set to `null`) -3. **Never mix**: Don't provide one without the other - -#### 4. Broadcast Transaction - -```typescript -const {swap} = await swapResponse.json(); - -validateTransaction(swap); - -const signedTx = await wallet.signTransaction(swap); -const txReceipt = await provider.sendTransaction(signedTx); -``` - -### Permit2 Error Messages - -| Error | Cause | Solution | -|-------|-------|----------| -| `"permitData" must be of type object` | Field set to `null` | Omit field entirely | -| `signature provided without permitData` | Missing `permitData` | Include both or neither | -| `Invalid permit signature` | Wrong data signed or expired | Re-request quote and sign fresh | - -## Error Handling - -### HTTP Status Codes - -| Code | Meaning | -|------|---------| -| 200 | Request succeeded | -| 400 | Invalid request (validation error) | -| 401 | Invalid API key | -| 429 | Rate limit exceeded | -| 500 | API error (retry with backoff) | -| 503 | Temporary unavailability (retry) | - -### Error Response Format - -```typescript -interface ErrorResponse { - error: string; - message: string; - details?: Record; -} -``` - -### Common Errors - -#### NO_QUOTES_AVAILABLE - -**Cause**: No route found for the requested swap - -**Solutions**: -- Verify token addresses are correct and on the specified chain -- Check sufficient liquidity exists for the trade size -- Try different protocols or routing preferences -- Reduce trade size - -#### INSUFFICIENT_RESERVES - -**Cause**: Liquidity insufficient for the requested amount - -**Solutions**: -- Reduce trade amount -- Split into multiple smaller trades -- Try alternative routing - -#### VALIDATION_ERROR - -**Cause**: Invalid request parameters - -**Solutions**: -- Check all required fields are present -- Verify addresses are valid checksummed addresses -- Ensure amount is in correct units (wei, not ether) -- Validate chain IDs are supported - -### Transaction Revert Scenarios - -If a transaction reverts on-chain: - -1. **Check `data` field**: Verify it's not empty -2. **Verify token balance**: User has sufficient tokens at broadcast time -3. **Check allowance**: Token approval is sufficient (if not using Permit2) -4. **Check slippage**: Price moved beyond slippage tolerance -5. **Check deadline**: Quote expired before broadcast -6. **Nonce collision**: Another transaction used the same nonce - -**Recommended Client-Side Checks:** - -```typescript -async function validateBeforeBroadcast( - tx: TransactionRequest, - provider: Provider, - token: string, - amount: string -): Promise { - // 1. Validate transaction structure - if (!tx.data || tx.data === '' || tx.data === '0x') { - throw new Error('Invalid transaction: empty data field'); - } - - // 2. Check native balance - const balance = await provider.getBalance(tx.from); - if (balance.lt(tx.value)) { - throw new Error('Insufficient native token balance'); - } - - // 3. Check ERC-20 balance (if applicable) - if (token !== NATIVE_TOKEN_ADDRESS) { - const tokenContract = new Contract(token, ERC20_ABI, provider); - const tokenBalance = await tokenContract.balanceOf(tx.from); - if (tokenBalance.lt(amount)) { - throw new Error('Insufficient token balance'); - } - } - - // 4. Simulate transaction (optional but recommended) - try { - await provider.call(tx); - } catch (error) { - throw new Error(`Transaction simulation failed: ${error.message}`); - } -} -``` - -## Best Practices - -### 1. Quote Freshness - -Quotes are time-sensitive due to price volatility: - -- **Refresh quotes** if more than 30 seconds old before broadcasting -- **Use `deadline`** parameter to prevent execution of stale quotes -- **Monitor price impact** and warn users of significant changes - -```typescript -const QUOTE_EXPIRY_MS = 30000; // 30 seconds - -const quoteTimestamp = Date.now(); -// ... user reviews and signs ... -if (Date.now() - quoteTimestamp > QUOTE_EXPIRY_MS) { - quote = await fetchQuote(params); // Fetch fresh quote -} -``` - -### 2. Transaction Validation - -Always validate transaction payloads before broadcasting: - -```typescript -function validateSwapTransaction(tx: TransactionRequest): void { - if (!tx.data || tx.data === '' || tx.data === '0x') { - throw new Error('Transaction data is empty'); - } - - if (!tx.to || !isAddress(tx.to)) { - throw new Error('Invalid recipient address'); - } - - if (!tx.from || !isAddress(tx.from)) { - throw new Error('Invalid sender address'); - } - - if (tx.maxFeePerGas && tx.gasPrice) { - throw new Error('Cannot set both maxFeePerGas and gasPrice'); - } - - if (tx.value && BigNumber.from(tx.value).lt(0)) { - throw new Error('Invalid transaction value'); - } -} -``` - -### 3. Gas Management - -The API provides gas estimates, but clients should: - -- **Apply gas buffer**: Add 10-20% to estimated gas limit -- **Update gas prices**: Use `refreshGasPrice: true` for fresh estimates -- **Handle gas spikes**: Warn users when gas is unusually high -- **EIP-1559 vs Legacy**: Use EIP-1559 on supported chains - -```typescript -function applyGasBuffer(tx: TransactionRequest): TransactionRequest { - if (tx.gasLimit) { - const buffered = BigNumber.from(tx.gasLimit) - .mul(120) // 120% - .div(100); - return {...tx, gasLimit: buffered.toString()}; - } - return tx; -} -``` - -### 4. Slippage Configuration - -Balance protection vs execution success: - -| Setting | Slippage | Use Case | -|---------|----------|----------| -| Conservative | 0.1-0.5% | Stable pairs, low volatility | -| Moderate | 0.5-1% | Most swaps | -| Aggressive | 1-5% | Large trades, volatile markets, low liquidity | - -### 5. Error Recovery - -Implement retry logic with exponential backoff: - -```typescript -async function fetchQuoteWithRetry( - params: QuoteRequest, - maxRetries = 3 -): Promise { - for (let attempt = 0; attempt < maxRetries; attempt++) { - try { - const response = await fetch('/quote', { - method: 'POST', - headers: { - 'x-api-key': API_KEY, - 'Content-Type': 'application/json' - }, - body: JSON.stringify(params) - }); - - if (!response.ok) { - if (response.status === 429) { - await sleep(Math.pow(2, attempt) * 1000); - continue; - } - throw new Error(`API error: ${response.status}`); - } - - return await response.json(); - } catch (error) { - if (attempt === maxRetries - 1) throw error; - await sleep(Math.pow(2, attempt) * 1000); - } - } - throw new Error('Max retries exceeded'); -} -``` - -### 6. Monitoring & Logging - -Track key metrics for production reliability: - -```typescript -interface SwapMetrics { - quoteLatency: number; - swapLatency: number; - quoteFreshness: number; - gasEstimateAccuracy: number; - revertRate: number; -} - -function logSwapAttempt( - params: QuoteRequest, - quote: QuoteResponse, - txHash?: string, - error?: Error -): void { - const metrics = { - timestamp: Date.now(), - chainId: params.tokenInChainId, - tokenIn: params.tokenIn, - tokenOut: params.tokenOut, - amount: params.amount, - routing: quote.routing, - txHash, - success: !!txHash && !error, - error: error?.message - }; - - analytics.track('swap_attempt', metrics); -} -``` - -## Troubleshooting - -### Quote Issues - -**Problem**: No quotes returned - -**Checklist**: -- [ ] Token addresses are valid for the specified chains -- [ ] Liquidity exists for the trading pair -- [ ] Amount is within reasonable bounds -- [ ] Chain IDs are supported by the API -- [ ] Protocols specified (if any) are available - -**Problem**: Quote price seems incorrect - -**Checklist**: -- [ ] Amount is in correct units (wei, not ether) -- [ ] Token decimals are correct -- [ ] Slippage tolerance is appropriate -- [ ] Price impact is acceptable for trade size - -### Transaction Issues - -**Problem**: Transaction reverts on-chain - -**Checklist**: -- [ ] `data` field is not empty -- [ ] Token balance sufficient at broadcast time -- [ ] Token approval sufficient (if not using Permit2) -- [ ] Quote not expired (deadline not passed) -- [ ] Slippage tolerance not too tight -- [ ] Gas limit sufficient -- [ ] Nonce correct (no collisions) - -**Problem**: Transaction takes too long to confirm - -**Checklist**: -- [ ] Gas price competitive (use `refreshGasPrice: true`) -- [ ] Network not congested -- [ ] Transaction not stuck (check nonce) - -### API Issues - -**Problem**: 429 Rate Limit Exceeded - -**Solution**: Implement exponential backoff and request caching: - -```typescript -const cache = new Map(); - -async function fetchWithCache( - url: string, - params: any, - cacheDuration = 30000 -): Promise { - const cacheKey = `${url}:${JSON.stringify(params)}`; - const cached = cache.get(cacheKey); - - if (cached && Date.now() - cached.timestamp < cacheDuration) { - return cached.data; - } - - const data = await fetchWithRetry(url, params); - cache.set(cacheKey, {data, timestamp: Date.now()}); - return data; -} -``` - -**Problem**: Inconsistent responses - -**Solution**: Include `requestId` in all related requests for debugging: - -```typescript -const requestId = generateUUID(); - -const quote = await fetchQuote({...params, requestId}); -const swap = await fetchSwap({classicQuote: quote, requestId}); -``` - -### Integration Issues - -**Problem**: Empty `data` field in transaction - -**Solution**: -1. Add validation before broadcasting -2. Log the full response for debugging -3. Contact support with `requestId` if persistent - -```typescript -const {swap} = await swapResponse.json(); - -if (!swap.data || swap.data === '' || swap.data === '0x') { - console.error('Invalid swap response:', { - requestId: swap.requestId, - swap - }); - throw new Error('Empty transaction data - please contact support'); -} -``` - -**Problem**: Permit2 validation errors - -**Solution**: Follow exact field requirements: - -```typescript -const swapRequest: SwapRequest = { - classicQuote: quote -}; - -// Only add permit fields if both are present -if (signature && permitData) { - swapRequest.signature = signature; - swapRequest.permitData = permitData; -} -// Otherwise omit entirely (don't set to null) -``` - -## Limitations - -- **UniswapX V2**: Mainnet, Arbitrum, Base, Unichain only -- **UniswapX V3**: Arbitrum only -- **L2 UniswapX minimum**: 300 USDC equivalent -- **Native token swaps**: No UniswapX for native token input or wrapping - -## Support - -For additional support: - -- **Discord**: Join the Uniswap developer community -- **GitHub Issues**: Report bugs and issues - -When reporting issues, include: -- Request ID from API response -- Full request/response payloads (sanitize sensitive data) -- Chain ID and transaction hash (if applicable) -- Timestamp of the request diff --git a/docs/api/trading/overview.md b/docs/api/trading/overview.md index 567d546e3..75b1b50cf 100644 --- a/docs/api/trading/overview.md +++ b/docs/api/trading/overview.md @@ -6,122 +6,51 @@ title: Overview # Trading API -The Uniswap Trading API provides quote generation and transaction building for token swaps across 25+ chains. This API handles route optimization, gas estimation, and transaction encoding - you handle balance checks, transaction signing, and broadcasting. +The Uniswap Trading API provides quote generation and transaction building for token swaps across 25+ chains. It handles route optimization, gas estimation, and transaction encoding, while your application manages balances, signing, and transaction broadcasting. -## Quick Start - -### Authentication - -All requests require an API key: - -```bash -curl -X POST https://trade.api.uniswap.org/v1/quote \ - -H "x-api-key: YOUR_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{"tokenIn":"0x...","tokenOut":"0x...","amount":"1000000",...}' -``` - -### Basic Quote Request - -```typescript -const response = await fetch('https://trade.api.uniswap.org/v1/quote', { - method: 'POST', - headers: { - 'x-api-key': 'YOUR_API_KEY', - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - tokenIn: '0x0000000000000000000000000000000000000000', // ETH - tokenOut: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT - tokenInChainId: 1, - tokenOutChainId: 1, - type: 'EXACT_INPUT', - amount: '1000000000000000000', // 1 ETH in wei - swapper: '0x...', // User's wallet address - slippageTolerance: 0.5 // 0.5% - }) -}); - -const quote = await response.json(); -``` - -## Architecture +## Get your API key -### Client-Side Responsibilities +Create an account in the [Trading API Developer Portal](https://developers.uniswap.org/dashboard/) to generate your API key. -The Trading API is a quote and transaction building service. Your application handles: +:::info +For complete endpoint coverage, authentication requirements, and implementation patterns, see the [API Integration Guide](https://api-docs.uniswap.org/guides/integration_guide). +::: -1. **Balance Checks**: Verify token balances before requesting quotes -2. **Allowance Management**: Check and request token approvals (ERC-20 `approve` or Permit2) -3. **Nonce Management**: Track transaction nonces for the user's wallet -4. **Gas Estimation**: Verify gas estimates before broadcasting -5. **Transaction Broadcasting**: Sign and submit transactions via your RPC provider -6. **Transaction Monitoring**: Track confirmations and handle reverts - -### Required Infrastructure +## Quick Start -Your integration must include: +Get started with the Uniswap Agent CLI skill, or send a direct request using the cURL example below. -- **RPC Provider**: Connection to blockchain nodes (Infura, Alchemy, or self-hosted) -- **Web3 Library**: ethers.js, viem, or web3.js for transaction signing -- **Wallet Integration**: WalletConnect, MetaMask, or similar for user signing +### Agent CLI -### Data Flow +If you are using Agent CLI: +```bash +npx skills add uniswap/uniswap-ai --skill swap-integration ``` -User Request - | -Your Application - |-- Check balances (via your RPC) - |-- Request quote (Trading API) - |-- Build transaction (Trading API response) - |-- Check allowances (via your RPC) - |-- Get user signature (Wallet) - |-- Manage nonce (your tracking) - +-- Broadcast transaction (via your RPC) - | - Blockchain -``` - -## Available Endpoints - -| Endpoint | Description | -|----------|-------------| -| [POST /quote](./quote) | Generate a quote for a token swap | -| [POST /swap](./swap) | Convert a quote into an unsigned transaction | -| [POST /check_approval](./check-approval) | Check if token approval is required | -| [POST /swap_5792](./swap-5792) | Generate batch transactions for EIP-5792 | -| [POST /swap_7702](./swap-7702) | Generate transaction with EIP-7702 delegation | -| [Cross-Chain Plans](./cross-chain) | Multi-step cross-chain swap endpoints | -## Routing Types +### cURL Reference -The API returns different quote types based on the optimal routing strategy: +> Never commit real API keys to the repository. Use environment variables or placeholders. -| Value | Type | Description | -|-------|------|-------------| -| 0 | CLASSIC | Standard AMM swap through Uniswap pools | -| 1 | DUTCH_LIMIT | Dutch auction order (UniswapX) | -| 2 | DUTCH_V2 | Dutch auction V2 | -| 3 | LIMIT_ORDER | Limit order | -| 4 | WRAP | ETH to WETH wrap | -| 5 | UNWRAP | WETH to ETH unwrap | -| 6 | BRIDGE | Cross-chain bridge | -| 7 | PRIORITY | MEV-protected priority order | -| 8 | DUTCH_V3 | Dutch auction V3 | -| 9 | QUICKROUTE | Fast approximation quote | -| 10 | CHAINED | Multi-step cross-chain swap | - -## Getting Started - -1. **Get an API Key**: Contact the Uniswap team to request API access -2. **Set Up Infrastructure**: Configure your RPC provider and wallet integration -3. **Implement the Flow**: Follow the [integration guide](./integration-guide) for step-by-step implementation -4. **Test on Testnet**: Validate your integration before going live - -## Next Steps - -- [Integration Guide](./integration-guide) - Complete step-by-step implementation guide -- [Quote Endpoint](./quote) - Detailed quote request/response documentation -- [Permit2 Flow](./permit2) - Gasless approvals via EIP-712 signatures -- [Error Handling](./errors) - Common errors and troubleshooting +```bash +curl --request POST \ + --url https://trade-api.gateway.uniswap.org/v1/quote \ + --header 'Content-Type: application/json' \ + --header 'x-api-key: YOUR_API_KEY' \ + --header 'x-universal-router-version: 2.0' \ + --data '{ + "generatePermitAsTransaction": false, + "autoSlippage": "DEFAULT", + "routingPreference": "BEST_PRICE", + "spreadOptimization": "EXECUTION", + "urgency": "urgent", + "permitAmount": "FULL", + "type": "EXACT_INPUT", + "amount": "1000000000000000000", + "tokenInChainId": "1", + "tokenOutChainId": "1", + "tokenIn": "0x0000000000000000000000000000000000000000", + "tokenOut": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "swapper": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" +}' +``` \ No newline at end of file diff --git a/docs/llms/overview.md b/docs/llms/overview.md index 0ff30075c..7d64c2951 100644 --- a/docs/llms/overview.md +++ b/docs/llms/overview.md @@ -4,52 +4,97 @@ sidebar_position: 1 title: Overview --- -# LLMs and AI Integration +# Uniswap AI Tools for Developers -Large Language Models (LLMs) and AI tools can help developers better understand and work with the Uniswap Protocol. This section provides resources and guidance for leveraging AI assistance when building on Uniswap. +Uniswap provides AI-powered development tools that help you integrate swaps, build v4 hooks, provide liquidity, and interact with the EVM from within your editor. -## AI-Powered Documentation +## Quick links -The Uniswap documentation is designed to work seamlessly with modern AI tools to provide enhanced developer support and assistance. +- [Uniswap AI](#uniswap-ai) +- [LLM Context Files](#llm-context-files) +- [Code Editor Setup](#code-editor-setup) -### Getting AI Help +## Uniswap AI -Use the built-in AI integration features throughout the documentation: +[Uniswap AI](https://github.com/Uniswap/uniswap-ai) is an open-source collection of plugins and skills that gives AI coding agents up-to-date, protocol-specific guidance across Uniswap protocols, APIs, and smart contracts. -- **Copy to AI**: Use the dropdown button on any documentation page to quickly get AI assistance -- **Context-Aware**: AI tools receive relevant documentation context for better responses -- **Multiple Platforms**: Integrate with popular AI assistants like Claude and ChatGPT +### Available plugins -## LLMs.txt +| Plugin | Description | +| --- | --- | +| **uniswap-trading** | Integrate swaps via [Trading API](https://developers.uniswap.org/dashboard), Universal Router SDK, or direct contract calls. | +| **uniswap-hooks** | Security-first guidance for building Uniswap v4 hooks. | +| **uniswap-viem** | EVM integration with viem and wagmi. | +| **uniswap-driver** | Token discovery and swap/liquidity planning with deep links. | +| **uniswap-cca** | Configure and deploy CCA contracts for token distribution. | -This documentation includes an [LLMs.txt file](/llms.txt) that provides comprehensive context about Uniswap for AI systems. This file helps ensure AI assistants can provide accurate and up-to-date information about: +### Install with the Skills CLI -- Protocol architecture and concepts -- Smart contract interfaces and functionality -- SDK usage and integration patterns -- Development best practices +Uniswap AI is available through [skills.sh](https://github.com/Uniswap/uniswap-ai). This works with any AI coding agent that supports skill files: -## Best Practices for AI-Assisted Development +```bash +npx skills add uniswap/uniswap-ai +``` -When using AI tools for Uniswap development: +### Install as a Claude Code plugin -1. **Provide Context**: Always include relevant protocol version (v2, v3, v4) in your queries -2. **Verify Code**: Always test and verify AI-generated code before deployment -3. **Reference Documentation**: Cross-check AI responses against official documentation -4. **Security First**: Have AI-generated smart contracts audited before production use +If you use [Claude Code](https://claude.ai/code), first add the Uniswap marketplace, then install individual plugins: -## Supported AI Platforms +```bash +# Add the Uniswap marketplace +/plugin marketplace add uniswap/uniswap-ai -The documentation provides optimized integration with: +# Install individual plugins +claude plugin add uniswap-hooks # v4 hook development +claude plugin add uniswap-trading # Swap integration +claude plugin add uniswap-viem # EVM / viem / wagmi +claude plugin add uniswap-driver # Token discovery & deep links +claude plugin add uniswap-cca # CCA auction configuration +``` -- **Claude**: Advanced reasoning for complex DeFi concepts -- **ChatGPT**: Code generation and debugging assistance -- **Other LLMs**: Compatible with any AI tool that supports context injection +Once installed, the plugins activate automatically when relevant to your task. You can also invoke specific skills directly. For example, **/uniswap-hooks:v4-security-foundations** for a security-first walkthrough of hook development. -## Contributing AI Resources +## LLM Context Files -Help improve AI assistance for the Uniswap community by: +If you prefer to give your AI agent raw documentation context rather than structured skills, Uniswap publishes LLM-optimized text files that summarize the protocol documentation. -- Reporting AI-generated errors or inaccuracies -- Suggesting improvements to the LLMs.txt context file -- Contributing examples of effective AI prompts for Uniswap development \ No newline at end of file +### llms.txt and llms-full.txt + +AI models have a context window (the amount of text they can process at once). Providing relevant documentation upfront helps the model give better answers without hallucinating. + +Uniswap offers two context files: +- **[llms.txt](https://docs.uniswap.org/v4-llms.txt)**: A compact summary with links to documentation sections. Works well with most models (100K+ token context windows). +- **[llms-full.txt](https://docs.uniswap.org/v4-llms-full.txt)**: A verbose version with more inline content. Use this if your model has a larger context window or you want more detail without following links. + +## Code Editor Setup + +### Cursor + +1. Navigate to **Cursor Settings > Features > Docs** +2. Select **Add new doc** and paste one of the following URLs: + +``` +https://docs.uniswap.org/v4-llms.txt +``` + +``` +https://docs.uniswap.org/v4-llms-full.txt +``` + +3. Use `@docs` → **Uniswap** to reference the documentation in your chat. + +### Windsurf + +Windsurf requires referencing documentation in each conversation. Add it to the Cascade window (`CMD+L`): + +``` +@docs:https://docs.uniswap.org/v4-llms.txt +``` + +``` +@docs:https://docs.uniswap.org/v4-llms-full.txt +``` + +### Claude Code + +Install the Uniswap AI plugins (see [above](#install-as-a-claude-code-plugin)) for the richest integration. The plugins provide structured skills, expert agents, and protocol-specific tools that go beyond static documentation context. \ No newline at end of file diff --git a/docusaurus.config.ts b/docusaurus.config.ts index c7edfcf9e..253b9bff1 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -88,13 +88,13 @@ const config: Config = { }, { label: "Give Feedback", - to: 'https://share.hsforms.com/14XvN41xQTyC8KPamgaM8Jwsdca9', + to: 'https://share.hsforms.com/1gMemq5uWQS-UaaorwU-qGAs8pgg', target: '_blank', rel: 'noreferrer', }, { - label: "Uniswap Foundation", - to: 'https://www.uniswapfoundation.org/', + label: "Uniswap Labs", + to: 'https://app.uniswap.org/', target: '_blank', rel: 'noreferrer', }, diff --git a/src/components/FeedbackForm.tsx b/src/components/FeedbackForm.tsx new file mode 100644 index 000000000..a299ca749 --- /dev/null +++ b/src/components/FeedbackForm.tsx @@ -0,0 +1,247 @@ +import React, { useState } from 'react' + +const FEEDBACK_TYPES = ['Bug', 'Feature request', 'Question', 'Other'] as const + +export default function FeedbackForm() { + const [email, setEmail] = useState('') + const [feedbackType, setFeedbackType] = useState<(typeof FEEDBACK_TYPES)[number] | ''>('') + const [followUp, setFollowUp] = useState(null) + const [issue, setIssue] = useState('') + const [challenges, setChallenges] = useState('') + const [docsUsefulness, setDocsUsefulness] = useState('') + const [loading, setLoading] = useState(false) + const [status, setStatus] = useState<'idle' | 'success' | 'error'>('idle') + const [errorMsg, setErrorMsg] = useState('') + const [acceptedTerms, setAcceptedTerms] = useState(false) + + async function onSubmit(e: React.FormEvent) { + e.preventDefault() + setLoading(true) + setStatus('idle') + setErrorMsg('') + + try { + const res = await fetch('/api/feedback', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + email, + feedbackType, + issue, + followUp, + challenges, + docsUsefulness, + pageUrl: typeof window !== 'undefined' ? window.location.href : '', + website: '', + }), + }) + + const data = await res.json().catch(() => ({})) + if (!res.ok || !data.success) { + setStatus('error') + setErrorMsg(data?.details || data?.error || 'Failed to submit feedback.') + return + } + + setStatus('success') + setEmail('') + setFeedbackType('Bug') + setIssue('') + setFollowUp(true) + setChallenges('') + setDocsUsefulness('') + } catch { + setStatus('error') + setErrorMsg('Network error. Please try again.') + } finally { + setLoading(false) + } + } + + if (status === 'success') { + return ( +
+ +

+ Thanks for submitting your feedback +

+

+ We appreciate it. Your input helps us improve Uniswap Docs. +

+ + +
+ ) + } + + return ( +
+ + +
+ + setEmail(e.target.value)} + className="mt-2 w-full rounded-large bg-light-surface-2 dark:bg-dark-surface-2 border border-light-surface-3 dark:border-dark-surface-3 p-3 text-light-neutral-1 dark:text-dark-neutral-1" + /> +
+ +
+ +
+ {FEEDBACK_TYPES.map((type) => ( + + ))} +
+
+ +
+ +