diff --git a/docs/README.md b/docs/README.md index 64d427bde..3f998d6f8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -24,3 +24,8 @@ CoW DAO's documentation follows a "Concepts", "Tutorials", "Technical Reference" - **_What_** something is (example: batch auctions) → see [Concepts](/category/concepts) - **_How_** to do something (example: create an order) → see [Tutorials](/category/tutorials) - **_Technical_** information (example: such as SDKs, APIs) - see [Technical Reference](/category/technical-reference) + +## Documentation for AI agents + +- /llms.txt: Find the relevant guide in the documentation index. +- /llms-full.txt: Read all guides in one file. diff --git a/docs/cow-protocol/tutorials/cow-swap/custom-links.md b/docs/cow-protocol/tutorials/cow-swap/custom-links.md index d746d47e8..4d1f4275e 100644 --- a/docs/cow-protocol/tutorials/cow-swap/custom-links.md +++ b/docs/cow-protocol/tutorials/cow-swap/custom-links.md @@ -1,12 +1,15 @@ --- -title: Make custom links +title: 'CoW Swap deeplinks: swaps and limit orders' +sidebar_label: Create trade deeplinks +description: Create CoW Swap limit-order deeplinks from natural-language requests, with price conversion, exact-buy and exact-sell examples, token units, and supported settings. --- -# Custom Links +# CoW Swap deeplinks: swaps and limit orders {#custom-links} -[CoW Swap](https://swap.cow.fi) supports URL query parameters to allow anyone to create a custom link for sharing a specific trade with a friend or on social media, or for creating a custom link to a specific trade for your own use. +Create prefilled [CoW Swap](https://swap.cow.fi) trade links for swaps and limit orders. +A link opens a form. It does not sign or place an order. -### Examples +## More link examples {#examples} - [`https://swap.cow.fi`](https://swap.cow.fi) - CoW Swap main page (`swap` is default page) - [`https://swap.cow.fi/#/swap`](https://swap.cow.fi/#/swap) - `swap` page @@ -19,9 +22,9 @@ title: Make custom links ## Query Parameters -Even though the main page is `swap`, each page has it's own specific URL parameters that can be set, in addition to global parameters that are used on all pages. Parameters not applicable to a page will have no effect on UI settings. - -Fortunately, the `swap` and `limit` pages have the same URL query parameters! +The default page is `swap`. The `swap` and `limit` pages accept amount and recipient parameters. +Limit-order links also use `orderKind` to distinguish exact-sell and exact-buy orders. +Unsupported parameters do not configure form settings. ### URL links format @@ -31,7 +34,7 @@ Fortunately, the `swap` and `limit` pages have the same URL query parameters! #### Limit -`https://swap.cow.fi/#//limit//?recipient=&sellAmount=&buyAmount=` +`https://swap.cow.fi/#//limit//?recipient=&sellAmount=&buyAmount=&orderKind=` ### Global parameters @@ -43,7 +46,8 @@ Fortunately, the `swap` and `limit` pages have the same URL query parameters! :::note -All parameters are optional. If a parameter is not set, the default value will be used. +The app accepts partial links and uses defaults for omitted fields. +For a complete limit-order request, supply the chain, token pair, both amounts, and order kind. ::: @@ -52,9 +56,10 @@ All parameters are optional. If a parameter is not set, the default value will b | `chainId` | Integer | `1` - Ethereum Mainnet | [Supported blockchains](/cow-protocol/reference/contracts/core#deployments) | | `sellTokenSymbolOrAddress` | Token `symbol` or `address` | Wrapped native token symbol for the current network (for Mainnet: `WETH`) | Sell token `symbol` or `address` | | `buyTokenSymbolOrAddress` | Token `symbol` or `address` | | Buy token `symbol` or `address` | -| `recipient` | `address` | `undefined` | Valid Ethereum account address | -| `sellAmount` | Integer or Float | `undefined` | Amount of sell token to sell | -| `buyAmount` | Integer or Float | `undefined` | Amount of buy token to buy | +| `recipient` | `address` | `undefined` | Recipient address or ENS name. URL-encode the value. | +| `sellAmount` | Integer or Float | `undefined` | Sell-token amount in human units | +| `buyAmount` | Integer or Float | `undefined` | Buy-token amount in human units | +| `orderKind` | `sell` or `buy` | `sell` when both amounts are supplied | For limit orders: fixes the sell amount or buy amount, respectively. | :::caution @@ -81,3 +86,71 @@ Native and wrapped tokens per chain: - Sepolia: [`WETH`](https://sepolia.etherscan.io/address/0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14) - wrapped `ETH` ::: + +## Create a limit-order deeplink + +### Build the URL + +The query parameters belong after the hash route. +Ethereum mainnet uses chain ID `1`. Gnosis Chain uses `100`. +Other chain IDs appear in the [supported blockchain deployments](/cow-protocol/reference/contracts/core#deployments). + +`sellAmount` and `buyAmount` use human token units, not integer base units. +For example, `sellAmount=0.125` means 0.125 sell tokens, regardless of the token's decimal count. +Use positive decimal amounts without thousands separators. +For complete limit-order links, supply both amounts and an explicit `orderKind`. + +| Request | `sellAmount` | `buyAmount` | `orderKind` | +| -------------------- | -------------------- | ---------------------- | ----------- | +| Sell an exact amount | Fixed sell amount | Minimum receive amount | `sell` | +| Buy an exact amount | Maximum spend amount | Fixed buy amount | `buy` | + +With both amounts supplied, `orderKind` defaults to `sell`. + +### Convert the price into amounts + +Deeplinks accept token amounts, not a price. +Convert the limit price into the minimum receive or maximum spend amount. + +#### Exact-sell example + +Request: “On Ethereum, sell exactly 4 WETH for USDC at a minimum of 2,500 USDC per WETH.” + +The minimum receive amount is `4 × 2500 = 10000 USDC`. + +[Open the exact-sell limit order](https://swap.cow.fi/#/1/limit/WETH/USDC?sellAmount=4&buyAmount=10000&orderKind=sell) + +#### Exact-buy example + +Request: “On Ethereum, buy exactly 4 WETH with USDC, paying at most 2,500 USDC per WETH.” + +The maximum spend amount is `4 × 2500 = 10000 USDC`. +The sell token is USDC. The buy token is WETH. + +[Open the exact-buy limit order](https://swap.cow.fi/#/1/limit/USDC/WETH?sellAmount=10000&buyAmount=4&orderKind=buy) + +If the price uses reverse units, divide the fixed amount by that price instead of multiplying. +Preserve the requested precision and the minimum receive or maximum spend bound. + +### Expiry and partial fills + +The limit-order deeplink format does not support expiry or partial-fill settings. +Parameters such as `validTo` and `partiallyFillable` do not configure these settings. + +Configure expiry and partial-fill settings manually in CoW Swap. + +### Review and share + +Before signing, review the chain, token addresses, amounts, order kind, recipient, expiry, and partial-fill settings in the form. +Opening a link does not guarantee that the requested fields applied or that the order will fill. + +CoW Swap can remove amount and order-kind parameters from the address bar after it consumes them. +Share the original generated URL to preserve the requested trade fields. + +## Tips for AI agents + +- If the chain or price bound is missing, ask for it. Do not assume Ethereum or substitute a market quote. +- If a token symbol is ambiguous, ask for its address or resolve it from reliable sources for the requested chain. + Preserve supplied addresses and native or wrapped token choices. +- If the request includes expiry or partial fills, explain that those settings require manual setup in CoW Swap. + Do not invent URL parameters or claim the link applies those settings. diff --git a/docs/cow-protocol/tutorials/cow-swap/limit.mdx b/docs/cow-protocol/tutorials/cow-swap/limit.mdx index a262acadc..ec9824ead 100644 --- a/docs/cow-protocol/tutorials/cow-swap/limit.mdx +++ b/docs/cow-protocol/tutorials/cow-swap/limit.mdx @@ -12,6 +12,12 @@ A buy limit order allows a trader to specify the maximum price they are willing CoW Swap provides an intuitive interface in which one may place their limit orders. +## Create a prefilled limit-order link + +To turn a trade request into a URL, use the [CoW Swap limit-order deeplink guide](./custom-links.md#create-a-limit-order-deeplink). +It covers exact-sell and exact-buy orders, price conversion, token units, and supported settings. +A deeplink opens a form. Review the fields and configure expiry and partial fills before signing. + ## Place a limit order For example, let's say we want to sell `COW`. We can set the limit price at which we want to sell, and the duration over which the limit order is valid: diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 4c03c5e2f..83ed9430f 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -103,6 +103,10 @@ const config: Config = { { generateLLMsTxt: true, generateLLMsFullTxt: true, + // vercel.json serves .md as text/plain because some AI readers reject text/markdown. + generateMarkdownFiles: true, + preserveDirectoryStructure: false, + useRelativeUrls: true, excludeImports: true, removeDuplicateHeadings: true, ignoreFiles: ['mevblocker/**'], diff --git a/package.json b/package.json index 1ac9fb925..a52e361b4 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "@docusaurus/tsconfig": "3.9.2", "@docusaurus/types": "3.9.2", "@types/react": "^18.0.0", - "docusaurus-plugin-llms": "^0.3.0", + "docusaurus-plugin-llms": "0.6.1", "docusaurus-plugin-typedoc": "^0.21.0", "dotenv": "^16.3.1", "prettier": "^3.0.3", diff --git a/src/components/AiActionsDropdown/index.tsx b/src/components/AiActionsDropdown/index.tsx index b8942a088..536baa88c 100644 --- a/src/components/AiActionsDropdown/index.tsx +++ b/src/components/AiActionsDropdown/index.tsx @@ -1,5 +1,6 @@ import React, { useState, useRef, useEffect } from 'react' import styles from './styles.module.css' +import { useLocation } from '@docusaurus/router' // Simple inline SVG icons const ChatGPTIcon = () => ( @@ -33,11 +34,8 @@ const ExternalIcon = () => ( ) -interface Props { - editUrl?: string -} - -export default function AiActionsDropdown({ editUrl }: Props) { +export default function AiActionsDropdown() { + const { pathname } = useLocation() const [open, setOpen] = useState(false) const [copied, setCopied] = useState(false) const ref = useRef(null) @@ -54,50 +52,47 @@ export default function AiActionsDropdown({ editUrl }: Props) { const pageUrl = typeof window !== 'undefined' ? window.location.href : '' - // Convert GitHub editUrl to raw content URL - // e.g. https://github.com/cowprotocol/docs/tree/main/docs/foo.md - // -> https://raw.githubusercontent.com/cowprotocol/docs/main/docs/foo.md - const rawUrl = editUrl - ?.replace('github.com', 'raw.githubusercontent.com') - .replace('/tree/', '/') + // Mev Blocker pages are excluded from Markdown exports. + const markdownUrl = pathname === '/mevblocker' || pathname.startsWith('/mevblocker/') + ? undefined + : (pathname.replace(/\/$/, '') || '/index') + '.md' + + const sourceUrl = markdownUrl && pageUrl ? new URL(markdownUrl, pageUrl).href : pageUrl const openInChatGPT = () => { - const q = encodeURIComponent(`Read from ${pageUrl} so I can ask questions about it`) + const q = encodeURIComponent(`Read from ${sourceUrl} so I can ask questions about it`) window.open(`https://chatgpt.com/?q=${q}`, '_blank') setOpen(false) } const openInClaude = () => { - const q = encodeURIComponent(`Read from ${pageUrl} so I can ask questions about it`) + const q = encodeURIComponent(`Read from ${sourceUrl} so I can ask questions about it`) window.open(`https://claude.ai/new?q=${q}`, '_blank') setOpen(false) } const copyPage = async () => { - if (rawUrl) { + let text = document.querySelector('article')?.innerText + if (markdownUrl) { try { - const res = await fetch(rawUrl) - if (!res.ok) throw new Error() - const md = await res.text() - await navigator.clipboard.writeText(md) - setCopied(true) - setTimeout(() => setCopied(false), 2000) + const res = await fetch(markdownUrl) + if (!res.ok || res.headers.get('content-type')?.includes('text/html')) throw new Error() + text = await res.text() } catch { - // Fallback: copy article text if fetch fails - const article = document.querySelector('article') - if (article) { - await navigator.clipboard.writeText(article.innerText) - setCopied(true) - setTimeout(() => setCopied(false), 2000) - } + // Keep article text if the Markdown export is unavailable. } } + if (text) { + await navigator.clipboard.writeText(text) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + } setOpen(false) } const viewMarkdown = () => { - if (rawUrl) { - window.open(rawUrl, '_blank') + if (markdownUrl) { + window.open(markdownUrl, '_blank', 'noopener,noreferrer') } setOpen(false) } @@ -140,7 +135,7 @@ export default function AiActionsDropdown({ editUrl }: Props) { - {editUrl && ( + {markdownUrl && (