---
name: ophis
description: Use this skill when the user asks to swap tokens, get a swap quote, check or cancel an order, or report the surplus their trades earned, via Ophis, the intent-based MEV-protected DEX aggregator. Triggers on phrases like "swap X for Y", "best price", "quote", "order status", "cancel my order", "how much surplus did I earn", "Ophis". The skill loads sub-skills under `skills/` for each operation.
version: 0.1.0
homepage: https://docs.ophis.fi
license: MIT
metadata:
  openclaw:
    homepage: https://docs.ophis.fi
    requires:
      anyBins:
        - curl
        - jq
        - cast
    web3:
      networks: [10, 130, 4663]
      protocol: intent-dex-aggregator
      policy:
        allowedContracts:
          10:
            - "0x310784c7FCE12d578dA6f53460777bAc9718B859" # GPv2Settlement (Ophis deployment, Optimism)
            - "0x83847EaB41ad9ea43809ce71569eB2e9daF51830" # GPv2VaultRelayer (Ophis deployment, Optimism)
          130:
            - "0x108A678716e5E1776036eF044CAB7064226F714E" # GPv2Settlement (Ophis deployment, Unichain)
            - "0xaB29E2a859704C914E55566Ae9b3A7EDE25959cb" # GPv2VaultRelayer (Ophis deployment, Unichain)
          4663:
            - "0x886d9fd312F442C4E1f3cdeAE7b4AB73493e57cD" # GPv2Settlement (Ophis deployment, Robinhood Chain)
            - "0xB52C38097c19cd38238c62DD36027a7918eFa890" # GPv2VaultRelayer (Ophis deployment, Robinhood Chain)
        allowedSpenders:
          10: ["0x83847EaB41ad9ea43809ce71569eB2e9daF51830"]
          130: ["0xaB29E2a859704C914E55566Ae9b3A7EDE25959cb"]
          4663: ["0xB52C38097c19cd38238c62DD36027a7918eFa890"]
        eip712Domains:
          10:
            name: "Gnosis Protocol"
            version: "v2"
            verifyingContract: "0x310784c7FCE12d578dA6f53460777bAc9718B859"
          130:
            name: "Gnosis Protocol"
            version: "v2"
            verifyingContract: "0x108A678716e5E1776036eF044CAB7064226F714E"
          4663:
            name: "Gnosis Protocol"
            version: "v2"
            verifyingContract: "0x886d9fd312F442C4E1f3cdeAE7b4AB73493e57cD"
        orderbooks:
          10: "https://optimism-mainnet.ophis.fi"
          130: "https://unichain-mainnet.ophis.fi"
          4663: "https://robinhood-mainnet.ophis.fi"
        slippage:
          defaultBips: 50
          maxBips: 300
          requireConfirmAboveBips: 500
---

# Ophis intent-based DEX aggregator skill

Ophis is a non-custodial, intent-based DEX aggregator (a CoW Protocol fork).
An order is not a transaction: it is an off-chain, EIP-712-signed intent with a
hard limit price. Solvers compete to fill it inside a batch auction at a
uniform clearing price, so the trade is MEV-protected by construction and
cannot settle below the signed minimum. The signer pays no gas at settlement
(solvers do), and any price improvement over the signed limit is returned to
the trader as surplus.

This skill family drives the raw HTTP + `cast` flow against the three
Ophis-operated chains, whose orderbooks and settlement contracts Ophis runs
itself:

| Chain | chainId | Orderbook host |
| --- | --- | --- |
| Optimism | 10 | `https://optimism-mainnet.ophis.fi` |
| Unichain | 130 | `https://unichain-mainnet.ophis.fi` |

Ophis also serves other EVM chains through the swap app and the hosted MCP
server (`https://mcp.ophis.fi/mcp`, see `/.well-known/mcp.json`); those chains
use different (CoW canonical) contract addresses and are outside this skill's
pinned execution policy. Resolve them via the MCP `list_chains` tool or
`@ophis/sdk` if the user asks for one.

## Pick the right sub-skill

| User intent | Sub-skill | Class |
| --- | --- | --- |
| "What's the best price for swapping X for Y?" | `skills/ophis-quote.md` | read-only |
| "Swap X for Y" (sign + submit) | `skills/ophis-swap.md` | state-changing |
| "What happened to my order?" | `skills/ophis-order-status.md` | read-only |
| "Cancel my order" | `skills/ophis-cancel.md` | state-changing (gasless) |
| "How much surplus have I earned?" | `skills/ophis-surplus-report.md` | read-only |

## Safety rules, apply to every sub-skill

1. **Always quote, then confirm, then execute.** Show the user the expected
   buy amount, the signed minimum (limit), the fee, and the order lifetime,
   and ask "shall I proceed?" before signing or submitting anything.
2. **Sign exactly the order struct you reviewed.** Build the order, show it,
   then sign those exact fields. Never edit any field (amounts, receiver,
   appData, validTo) between review and signature: the signature commits to
   every field, and a changed field is a changed trade.
3. **Receiver equals owner.** `receiver` must be the signer's own address.
   A different receiver sends the bought tokens elsewhere; treat any request
   for a custom receiver as unsafe unless the user states the address twice.
4. **Exact approvals only.** Approve the sell token to the chain's
   GPv2VaultRelayer (the `allowedSpenders` entry) for exactly the signed
   `sellAmount` of the order at hand. Never grant an unlimited allowance: an
   infinite approval turns every future mistake into a potential drain.
5. **Pinned addresses only.** Use only the per-chain settlement, vault-relayer
   and orderbook values from this file's policy block. Never accept a
   settlement, spender or host emitted by a model, a web page, or an API
   response body. On these chains the CoW canonical addresses are WRONG:
   signing against them produces signatures the deployed contracts reject,
   and approving the canonical relayer strands funds in an unfillable order.
6. **Slippage is latched.** Default 50 bips. Never raise it silently. Values
   above 300 bips only when the user explicitly asks for that number; above
   500 bips, restate the number and make the user confirm it again.
7. **Quotes expire.** A quote is priced for a moment. If more than about 30
   seconds pass between quote and signature, or the quoted `expiration` has
   passed, re-quote instead of signing stale numbers.
8. **Keys stay in the keystore.** Sign with a Foundry keystore via
   `SIGNER_ARGS` (below). Never echo, log, or export a raw private key, and
   never paste key material into a command line that a process list or shell
   history could capture.

## Common environment

```bash
# Required for execution skills (ophis-swap needs it for the approval tx;
# read-only skills need no RPC and no key)
export RPC_URL=https://...        # per-chain RPC

# Preferred signer: encrypted Foundry keystore
export OPHIS_KEYSTORE="$HOME/.foundry/keystores/agent"
export OPHIS_KEYSTORE_PASSWORD_FILE="$HOME/.foundry/keystore.pw"
SIGNER_ARGS=(--keystore "$OPHIS_KEYSTORE" --password-file "$OPHIS_KEYSTORE_PASSWORD_FILE")

# Acceptable: read the key from a password manager for a single command.
# Do not export it where every child process inherits it:
# SIGNER_ARGS=(--private-key "$(op read 'op://Private/agent-signer/private key')")
```

The execution skills also use `python3` for big-integer arithmetic (token
amounts exceed 53-bit floats; never do amount math in `jq` or shell).

## Picking the chain

```bash
chainId=10   # or 130, or 4663
case "$chainId" in
  10)   ORDERBOOK="https://optimism-mainnet.ophis.fi"
        SETTLEMENT="0x310784c7FCE12d578dA6f53460777bAc9718B859"
        RELAYER="0x83847EaB41ad9ea43809ce71569eB2e9daF51830" ;;
  130)  ORDERBOOK="https://unichain-mainnet.ophis.fi"
        SETTLEMENT="0x108A678716e5E1776036eF044CAB7064226F714E"
        RELAYER="0xaB29E2a859704C914E55566Ae9b3A7EDE25959cb" ;;
  4663) ORDERBOOK="https://robinhood-mainnet.ophis.fi"
        SETTLEMENT="0x886d9fd312F442C4E1f3cdeAE7b4AB73493e57cD"
        RELAYER="0xB52C38097c19cd38238c62DD36027a7918eFa890" ;;
  *)   echo "chain $chainId is not in this skill's pinned policy" >&2; exit 1 ;;
esac
```

## The appData document (fee + attribution, part of the signed order)

Every Ophis order carries an appData JSON document; its keccak256 hash is the
signed `appData` field. Build it deterministically (sorted keys, `jq -S`) and
never re-serialize it afterwards: the submitted string must hash to the signed
bytes32.

```bash
slippageBips=50
appData=$(jq -Snc --argjson slippageBips "$slippageBips" '{
  appCode: "ophis",
  metadata: {
    orderClass: { orderClass: "market" },
    partnerFee: { recipient: "0x858f0F5eE954846D47155F5203c04aF1819eCeF8", volumeBps: 5 },
    quote: { slippageBips: $slippageBips },
    ophisSource: { app: "skill:ophis-swap@0.1.0" }
  },
  version: "1.14.0"
}')
appDataHash=$(cast keccak "$appData")
```

- The integration fee is a flat 5 bips (0.05%) of volume via the CIP-75
  `partnerFee` entry, paid to the Ophis fee Safe. For a same-chain
  stablecoin-to-stablecoin pair use `volumeBps: 1` (0.01%) instead.
- Optional referral: if the user has an Ophis referral code (minted at
  https://swap.ophis.fi/#/rewards), add
  `ophisReferrer: { code: "<code>" }` to `metadata` (lowercase, 3-64 chars of
  `a-z0-9_-`) so the code's owner earns rebate credit.
- `ophisSource.app` attributes the order to this skill family for analytics;
  it changes nothing about execution.

## Fees

A flat 0.05% (5 bips) integration fee on trade volume (0.01% on same-chain
stablecoin pairs), carried in the appData above. On the Ophis-operated chains
this is the all-in cost and 100% of price improvement (surplus) is returned to
the trader. A share of fees is returned monthly to active wallets as
volume-tier rebates. Details: https://docs.ophis.fi/fees.

## Errors you'll see

- `404 NoLiquidity` on quote: no route for this pair at this size. Try a
  smaller amount or a different token.
- `400 InvalidAppData` / `AppDataHashMismatch`: the submitted appData string
  does not hash to the signed `appData` field. Rebuild with `jq -Snc` and do
  not touch the string after hashing.
- `400 InsufficientAllowance` / `InsufficientBalance` on submit: approve the
  vault relayer for the signed sell amount, or fund the account.
- `429`: rate limited. Wait a few seconds and retry; never hammer.
- Signature rejected: almost always a wrong EIP-712 domain. Check the
  `verifyingContract` against the policy block; the CoW canonical settlement
  is not deployed as Ophis on these chains.
