Skip to content

Added skill doc to help agents create orders#594

Open
data-cowwboy wants to merge 2 commits intomainfrom
agent-skills
Open

Added skill doc to help agents create orders#594
data-cowwboy wants to merge 2 commits intomainfrom
agent-skills

Conversation

@data-cowwboy
Copy link
Contributor

@data-cowwboy data-cowwboy commented Feb 11, 2026

Description

We want to make it easier for AI agents to submit orders using our api. Added this md doc which contains details of how order can be created. We can decide to add other features our api offers as we progress.

Changes

  • Added skill for order creation

Summary by CodeRabbit

  • Documentation
    • Added a comprehensive guide for creating and managing CoW Swap orders via the Order Book API, including environment setup, API endpoints, token/decimals handling, appData and fee guidance (fees, slippage, partner fees), EIP-712 signing schemes and examples, end-to-end workflow (approval, quoting, signing, submission, monitoring), troubleshooting, tooling examples (Python/TypeScript/Shell), and public RPC references.

@data-cowwboy data-cowwboy requested a review from a team as a code owner February 11, 2026 19:42
@vercel
Copy link

vercel bot commented Feb 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview Feb 17, 2026 7:45am

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

Adds a new comprehensive Markdown guide describing how to create, sign, submit, and monitor CoW Protocol orders via the Order Book API, including environment setup, endpoints, contract addresses, EIP-712 signing, fee/slippage guidance, tooling examples, and troubleshooting (documentation only).

Changes

Cohort / File(s) Summary
CoW Swap Order Creator Documentation
skills/cow-swap-order-creator.md
New detailed 600+ line technical guide covering end-to-end CoW Swap order workflows: environment & endpoints, contract addresses, token decimals, appData, pre-approval/allowance flows, quoting and amount adjustments, EIP-712 signing schemes and domain/types, submission, status monitoring, fees/slippage/partner fees guidance, examples (Python/TypeScript/Shell), troubleshooting, safety rules, and RPC/approval calldata references.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Suggested reviewers

  • alfetopito
  • pretf00d
  • shoom3301

Poem

🐰 I hopped through docs, pen in paw,
EIP-712 dreams I gently draw,
Orders signed, and quotes align,
Swap paths clear in every line,
Hooray—automation's carrot shine! 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely summarizes the main change: adding a skill documentation file to help agents create orders via the CoW Swap API.
Description check ✅ Passed The PR description covers the required sections with a clear rationale for the change (making order submission easier for AI agents) and mentions the added skill documentation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch agent-skills

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@skills/cow-swap-order-creator.md`:
- Around line 288-292: Add a guard that validates required environment variables
(at minimum $PRIVATE_KEY) before the Python block that computes FROM (the block
containing Account.from_key('$PRIVATE_KEY')). If a variable is missing or empty,
print a clear error message and exit non‑zero; do this validation after the
config section and before any use of $PRIVATE_KEY (and replicate for any other
env vars used later in the script).
- Around line 310-345: The script writes the signed raw transaction to
/tmp/cow_raw_tx.txt which is insecure; change the flow so the Python snippet
that signs the transaction (symbols: Account.from_key, rpc_call,
signed.raw_transaction.hex) feeds its hex output directly into the curl
eth_sendRawTransaction call instead of creating a world-readable temp
file—either by piping the Python stdout into curl (or command substitution) or
by using a secure temporary file created with mktemp and chmod 600 if a file is
unavoidable; also ensure the shell variable TX_HASH is produced from the curl
response as before without referencing /tmp/cow_raw_tx.txt.
🧹 Nitpick comments (2)
skills/cow-swap-order-creator.md (2)

113-127: Consider using ABI encoding libraries for calldata construction.

The manual calldata construction using printf and tr is technically correct but fragile and difficult to maintain. For production use, recommend using proper ABI encoding tools:

  • Python: web3.py's contract interface or eth_abi.encode
  • JavaScript/TypeScript: ethers.js or viem contract interfaces

This would improve readability and reduce the risk of encoding errors.


349-356: Hardcoded transaction confirmation timeout.

The approval confirmation loop waits a maximum of 120 seconds (30 iterations × 4 seconds). During network congestion, this may be insufficient and cause the script to proceed before the approval is confirmed.

Consider:

  • Increasing the timeout or making it configurable
  • Adding a clear error message if timeout is reached
  • Checking the transaction status more explicitly
♻️ Improved confirmation logic
   # Wait for confirmation
   echo "Waiting for confirmation..."
-  for i in $(seq 1 30); do
+  MAX_WAIT=60  # iterations (4 min total)
+  for i in $(seq 1 $MAX_WAIT); do
     RECEIPT=$(curl -s "$RPC" -X POST \
       -H "Content-Type: application/json" \
       -d "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionReceipt\",\"params\":[\"$TX_HASH\"],\"id\":1}" \
       | python3 -c "import sys,json; r=json.load(sys.stdin)['result']; print(r['status'] if r else 'pending')")
     [ "$RECEIPT" = "0x1" ] && echo "Approved!" && break
+    [ "$i" -eq "$MAX_WAIT" ] && echo "Error: Approval tx timed out" && exit 1
     sleep 4
   done

Comment on lines +288 to +292
FROM=$(python3 -c "
from eth_account import Account
a = Account.from_key('$PRIVATE_KEY')
print(a.address)
")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add validation for required environment variables.

The script uses $PRIVATE_KEY without checking if it's set, which will cause a cryptic Python error. Consider adding validation at the start of the script.

🛡️ Proposed validation check

Add after the config section (after line 285):

+# Validate required environment
+if [ -z "${PRIVATE_KEY:-}" ]; then
+  echo "Error: PRIVATE_KEY environment variable is required"
+  exit 1
+fi
+
 # Derive from address from private key
🤖 Prompt for AI Agents
In `@skills/cow-swap-order-creator.md` around lines 288 - 292, Add a guard that
validates required environment variables (at minimum $PRIVATE_KEY) before the
Python block that computes FROM (the block containing
Account.from_key('$PRIVATE_KEY')). If a variable is missing or empty, print a
clear error message and exit non‑zero; do this validation after the config
section and before any use of $PRIVATE_KEY (and replicate for any other env vars
used later in the script).

Copy link
Contributor

@anxolin anxolin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great initiative!

I understand it was IA generated, but there were some things that are not completly true or felt strange. I didn't thoroughly review, but went over this superficially.

@@ -0,0 +1,524 @@
---
name: cow-swap-order-creator
description: Create and manage CoW Swap orders through the Order Book API, including quote-to-order conversion, fee=0 signing, slippage and partner-fee adjustments, EIP-712/ERC-1271/PreSign requirements, submission, status checks, and cancellation. Use when an agent needs to place, debug, or automate CoW Protocol orders from backend or script workflows.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not adding website and metadata?

https://fluid.io/skill.md

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

| Gnosis Chain | `https://api.cow.fi/xdai/api/v1` | 100 |
| Arbitrum One | `https://api.cow.fi/arbitrum_one/api/v1` | 42161 |
| Base | `https://api.cow.fi/base/api/v1` | 8453 |
| Sepolia | `https://api.cow.fi/sepolia/api/v1` | 11155111 |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a lot of missing networks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added networks

| USDT | `0xdAC17F958D2ee523a2206206994597C13D831ec7` | 6 |
| DAI | `0x6B175474E89094C44Da98b954EedeAC495271d0F` | 18 |
| WBTC | `0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599` | 8 |
| COW | `0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB` | 18 |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't review these, but is it good idea to add only these tokens.
I would link the JSON with coswap list

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added json link while keeping a small list in md

| Field | Value |
|---|---|
| Document | `{"appCode":"agent","metadata":{},"version":"1.6.0"}` |
| Hash | `0x6a45ce6deb3a32a35a97afa44fd544c8ebc355edc10b2a8e52ef0356b804df45` |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you verify this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


Wait for confirmation before proceeding.

> Native ETH cannot be sold directly. Wrap to WETH first.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not true! you can use eth flow

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

"kind": "sell",
"from": "0xYOUR_ADDRESS",
"receiver": "0xYOUR_ADDRESS",
"appData": "0x6a45ce6deb3a32a35a97afa44fd544c8ebc355edc10b2a8e52ef0356b804df45",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would swear appData needs to be the JSON and appDataHash the hash, but for sure there's sth weird here having the field 2 times with the same value

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

### Step 1 — Request a quote

```bash
curl -s -X POST "$API_BASE/quote" \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think an AI is good at reading swagger/open API yaml too. No? shouldt we point to it?


1. `signingSellAmount = quote.sellAmount + quote.feeAmount`
2. `signingBuyAmount = quote.buyAmount × (10000 − slippageBps) / 10000`
3. Optional partner fee: `signingBuyAmount = signingBuyAmount × (10000 − partnerFeeBps) / 10000`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weird formulat, I get what aims to say, but mathematically wrong unless partnerFeeBps=0

Proof:

signingBuyAmount = signingBuyAmount × (10000 − partnerFeeBps) / 10000

1 = (10000 − partnerFeeBps) / 10000

10000 = 10000 − partnerFeeBps

0 =  partnerFeeBps


## appData

All agent-created orders must include `"appCode": "agent"` in their appData document.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want appCode to be an agent? I would use a name more meaningful. If we want to know it was IA generated for some reason, we can do a metadata for that, but not sure we care

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anxolin can you please suggest what should we keep in appCode.

If we want to know it was IA generated for some reason, we can do a metadata for that, but not sure we care
It would be great if we could capture this, this can then become a metric in understand user category in future

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
skills/cow-swap-order-creator.md (1)

424-456: Consider using secure temporary files.

The script writes sensitive trading data (token pairs, amounts, timing) to world-readable /tmp files. While this avoids shell quoting issues, it exposes trading strategy in multi-user environments.

🔒 Proposed improvement using mktemp
-# Write quote request to temp file (avoids shell-quoting issues with JSON)
+# Write quote request to secure temp file
+QUOTE_REQ=$(mktemp)
+QUOTE_RESP=$(mktemp)
+trap "rm -f $QUOTE_REQ $QUOTE_RESP" EXIT
+
 python3 -c "
 import json
 payload = {
@@ -440,16 +443,16 @@
     'signingScheme': 'eip712'
 }
-with open('/tmp/cow_quote_req.json', 'w') as f:
+with open('$QUOTE_REQ', 'w') as f:
     json.dump(payload, f)
 "
 
 curl -s -X POST "$API/quote" \
   -H "Content-Type: application/json" \
-  -d `@/tmp/cow_quote_req.json` > /tmp/cow_quote_resp.json
+  -d @$QUOTE_REQ > $QUOTE_RESP
 
 echo "Quote response:"
 python3 -c "
 import json
-with open('/tmp/cow_quote_resp.json') as f:
+with open('$QUOTE_RESP') as f:
     data = json.load(f)

Apply similar pattern to other temp files.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/cow-swap-order-creator.md` around lines 424 - 456, The script writes
sensitive data to world-readable static files (/tmp/cow_quote_req.json and
/tmp/cow_quote_resp.json); change this to create secure temp files (e.g., use
mktemp or Python's tempfile.NamedTemporaryFile with delete=False) and set
restrictive permissions (600) before writing/reading, then pass that secure
filename into the python3 payload dump and the curl -d/@... call (and remove the
temp files after use); update both the python3 snippet that writes the quote
payload and the code that reads the curl response to use the secure temp path
instead of the hard-coded /tmp filenames.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@skills/cow-swap-order-creator.md`:
- Around line 492-537: The Order struct hash builds struct_hash using
ORDER_TYPE_HASH and several encode_abi calls; the encoding of the validTo field
currently uses encode_abi(['uint256'], [$VALID_TO]) which mismatches the
declared ORDER_TYPE_HASH type of uint32 — change that encode_abi call to
encode_abi(['uint32'], [$VALID_TO]) so the encoded type matches ORDER_TYPE_HASH
and CoW Protocol's reference implementation (look for ORDER_TYPE_HASH,
struct_hash, and the encode_abi for validTo to locate the code).

---

Duplicate comments:
In `@skills/cow-swap-order-creator.md`:
- Around line 388-411: The current flow writes the signed transaction to
/tmp/cow_raw_tx.txt and interpolates $PRIVATE_KEY into a shell-invoked Python
command, exposing secrets in multi-user environments; instead, stop using the
temp file and avoid command-line key exposure by running the Python snippet via
a here-document or command substitution that reads the private key from an
environment variable (e.g., os.environ['PRIVATE_KEY']) and prints the signed raw
transaction to stdout, capture that stdout into the TX variable (replace
/tmp/cow_raw_tx.txt and the inline Python invocation), then use TX in the
eth_sendRawTransaction curl payload to set TX_HASH; ensure no secrets appear in
the process list and remove any writes to /tmp/cow_raw_tx.txt.
- Around line 353-359: Validate that the PRIVATE_KEY env var is set before
deriving FROM and fail fast with a clear error; replace the current python3
invocation that interpolates '$PRIVATE_KEY' (the multi-line block that sets
FROM) with a secure here-document or stdin-based invocation so the private key
is not visible in process arguments—keep the logic that uses
eth_account.Account.from_key inside the python block but read the key from stdin
or os.environ within that block, and ensure FROM is assigned only after
successful validation.

---

Nitpick comments:
In `@skills/cow-swap-order-creator.md`:
- Around line 424-456: The script writes sensitive data to world-readable static
files (/tmp/cow_quote_req.json and /tmp/cow_quote_resp.json); change this to
create secure temp files (e.g., use mktemp or Python's
tempfile.NamedTemporaryFile with delete=False) and set restrictive permissions
(600) before writing/reading, then pass that secure filename into the python3
payload dump and the curl -d/@... call (and remove the temp files after use);
update both the python3 snippet that writes the quote payload and the code that
reads the curl response to use the secure temp path instead of the hard-coded
/tmp filenames.

Comment on lines +492 to +537
# --- Step 3: Sign (EIP-712) ---
# Manual EIP-712 hashing — works with any eth_account version.
# Only requires eth_account, eth_utils, and eth_abi (no minimum version).
SIGNATURE=$(python3 -c "
from eth_account import Account
from eth_utils import keccak
from eth_abi import encode_abi

# Domain separator
DOMAIN_TYPE_HASH = keccak(b'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)')
domain_separator = keccak(
DOMAIN_TYPE_HASH
+ keccak(b'Gnosis Protocol')
+ keccak(b'v2')
+ encode_abi(['uint256'], [1])
+ encode_abi(['address'], ['$SETTLEMENT'])
)

# Order struct hash
ORDER_TYPE_HASH = keccak(b'Order(address sellToken,address buyToken,address receiver,uint256 sellAmount,uint256 buyAmount,uint32 validTo,bytes32 appData,uint256 feeAmount,string kind,bool partiallyFillable,string sellTokenBalance,string buyTokenBalance)')
struct_hash = keccak(
ORDER_TYPE_HASH
+ encode_abi(['address'], ['$SELL_TOKEN'])
+ encode_abi(['address'], ['$BUY_TOKEN'])
+ encode_abi(['address'], ['$RECEIVER'])
+ encode_abi(['uint256'], [int('$SIGNING_SELL')])
+ encode_abi(['uint256'], [int('$SIGNING_BUY')])
+ encode_abi(['uint256'], [$VALID_TO])
+ bytes.fromhex('${APP_DATA_HASH#0x}')
+ encode_abi(['uint256'], [0])
+ keccak(b'sell')
+ encode_abi(['bool'], [False])
+ keccak(b'erc20')
+ keccak(b'erc20')
)

msg_hash = keccak(b'\x19\x01' + domain_separator + struct_hash)

acct = Account.from_key('$PRIVATE_KEY')
signed = acct.signHash(msg_hash)
sig = signed.signature.hex()
# Ensure no double 0x prefix
print(sig if not sig.startswith('0x') else sig[2:])
")

echo "Signature: 0x$SIGNATURE"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and read the file to understand the full context
find . -name "cow-swap-order-creator.md" -type f

Repository: cowprotocol/docs

Length of output: 94


🏁 Script executed:

# Also search for Order struct declarations to understand the type definitions
rg -n "Order\(" --type markdown -A 20 | head -100

Repository: cowprotocol/docs

Length of output: 9212


🌐 Web query:

EIP-712 encode_abi uint32 encoding specification

💡 Result:

In EIP-712, encodeData encodes each atomic field into an exactly 32-byte value, using Solidity ABI-style integer encoding (big-endian, left-padded). (eips.ethereum.org)

So a uint32 is encoded the same way as any uint<M> in the ABI spec: big-endian encoding of the integer, left-padded with 0x00 to 32 bytes. (docs.soliditylang.org)

Example (uint32(1)), the 32-byte encoded word is:

0x0000000000000000000000000000000000000000000000000000000000000001

In EIP-712 hashing, this 32-byte word is what gets concatenated (along with other 32-byte encodings) inside encodeData, typically via keccak256(abi.encode(typeHash, ...)). (eips.ethereum.org)

Citations:


🌐 Web query:

CoW Protocol order struct EIP-712 validTo field encoding reference implementation

💡 Result:

validTo in CoW Protocol (GPv2) EIP-712 Order: how it’s encoded

Canonical on-chain reference implementation: GPv2Order.sol in cowprotocol/contracts (v1.0.0). It defines the signed order struct as:

  • validTo is uint32 in GPv2Order.Data and in the EIP-712 type string ("uint32 validTo"). [1]

EIP-712 encoding rule that determines the actual bytes hashed:

  • In EIP-712 encodeData, every field is encoded as a 32-byte word; integer types are encoded as their value (big-endian) padded to 32 bytes (i.e., effectively like uint256(value) in ABI encoding). [2]
    So validTo (a uint32) is hashed as a 32-byte left-padded integer word containing that 32-bit value.

Order UID packing (not the EIP-712 hash, but often confused with “encoding”):

  • CoW’s orderUid is abi.encodePacked(orderDigest, owner, validTo) where validTo occupies the final 4 bytes (since it’s a packed uint32). This is implemented in packOrderUidParams / extractOrderUidParams. [1]

Practical takeaway (off-chain signing)

When producing the EIP-712 typed data, define validTo as type uint32 and pass a Unix timestamp (seconds) that fits in 32 bits; the EIP-712 hash will include it as a 32-byte ABI word per spec. [1][2]

Sources:
[1] CoW Protocol contracts GPv2Order.sol (v1.0.0) reference implementation (struct, TYPE_HASH string includes uint32 validTo, hashing & UID packing). (raw.githubusercontent.com)
[2] EIP-712 spec: encodeData uses 32-byte encoding per member; integers encoded as 256-bit words. (eips.ethereum.org)

Citations:


Fix EIP-712 type encoding for validTo field.

The Order type string at line 511 correctly declares validTo as uint32, but line 519 encodes it as uint256. For semantic correctness and to match CoW Protocol's reference implementation, change:

+ encode_abi(['uint256'], [$VALID_TO])

to:

+ encode_abi(['uint32'], [$VALID_TO])

While EIP-712 encodes all atomic types as 32-byte values (so both would produce the same bits for valid values), the type argument must match the declared type string for proper alignment with the spec and reference implementations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/cow-swap-order-creator.md` around lines 492 - 537, The Order struct
hash builds struct_hash using ORDER_TYPE_HASH and several encode_abi calls; the
encoding of the validTo field currently uses encode_abi(['uint256'],
[$VALID_TO]) which mismatches the declared ORDER_TYPE_HASH type of uint32 —
change that encode_abi call to encode_abi(['uint32'], [$VALID_TO]) so the
encoded type matches ORDER_TYPE_HASH and CoW Protocol's reference implementation
(look for ORDER_TYPE_HASH, struct_hash, and the encode_abi for validTo to locate
the code).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants