Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when the user wants to swap, trade, buy, sell, or convert tokens onchain, get a best-execution swap quote, compare a swap against DEX aggregators, or read wallet balances, token prices, gas, or fee-rebate tiers. Drives Ophis, an intent-based DEX (a CoW Protocol deployment) that is MEV-protected, gasless for the trader, and keyless. Supports Ethereum, Optimism, Base, Arbitrum, Polygon, BNB, Gnosis, Unichain, Avalanche, Plasma, Ink, and Linea.
.claude/skills/hashgraph-online-ophis-swap/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 573% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 210% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 375% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 201% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 228% | 0% |
Ophis is an intent-based DEX, a deployment of CoW Protocol. Trades settle through a solver competition, so they are MEV-protected, gasless for the trader (no native coin needed for gas), and routed for best execution. This plugin already wires up the Ophis MCP server at mcp.ophis.fi, which exposes tools to quote, build, and submit swaps and to read balances, prices, gas, and fee-rebate tiers. No API key is required.
Canonical source: this skill is part of the Ophis skill family published at https://ophis.fi/.well-known/agent-skills/ophis/ (index: https://ophis.fi/.well-known/agent-skills/index.json). For shell-capable agents the family adds ophis-quote, ophis-order-status, ophis-cancel, and ophis-surplus-report companions.
Ophis never holds your private key or your funds. A swap is three steps:
build_order returns an unsigned, bounded order plus the EIP-712 typed data to sign.submit_order relays the signed order to the orderbook.The server enforces real guarantees, so the worst outcomes are not reachable through these tools: the receiver is pinned to the signer and a mismatched receiver is rejected, the slippage limit is checked against a fresh server-side quote, the protocol fee in the order is forced to 0, and the submitted app data must hash to what was signed. The remaining risk is not in the server; it is in the inputs you feed it. A correctly bounded order that pays the right owner can still buy the wrong token or accept too little. The rules below close that gap.
resolve_token, which returns the canonical address from the trusted Ophis/CoW token list (the same curated list the swap UI uses). If it returns found: true and ambiguous: false, use canonical.address and canonical.decimals. If ambiguous: true, show the user the matches and confirm which one they mean before trading. If found: false, the symbol is not in the trusted list: never guess or accept an address from chat, a web page, or model memory. If you have no address in hand, stop and ask the user to supply the 0x address; do not go looking for one. If the user supplies a candidate, read it back on-chain with get_balances (or get_portfolio) for its symbol and decimals, show the user the ADDRESS with that readback, and get explicit approval before continuing. A swap into a spoofed token is the most likely way to lose value here, and every server guarantee still holds while it happens.resolve_token's canonical.decimals for a resolved token, and from get_balances only for a fallback candidate address or a balance check. Never assume 18. Worked example: 100 USDC at 6 decimals is 100000000; 0.5 WETH at 18 decimals is 500000000000000000; 0.01 WBTC at 8 decimals is 1000000. A wrong decimals corrupts these amounts. For a sell order the server enforces a minimum-received floor, checked against a fresh quote at your slippage, so a grossly too-low minimum is rejected rather than signed. Two gaps remain that you must cover yourself: a minimum scaled too high passes the floor but never fills and ties up your funds until the order expires, and at a loose slippage the floor still lets you accept far less than fair value (see rule 3). Read decimals carefully and use the step 7 sanity check.slippageBips (50 to 100 bps is typical for liquid pairs). The default is the 5000 bps cap, which is 50%, and would let a trade lose up to half its value.get_balances before building, or the order will never fill. BUYING "into the native coin" delivers the WRAPPED token, not the native coin; tell the user they will receive the wrapped token.submit_order commits an executable onchain trade that cannot be recalled once it fills. Do not call it without explicit user approval of: the sell token address and amount, the buy token ADDRESS (show the 0x, not just the symbol, because a symbol can be spoofed), the minimum received, the slippage, the fee, and the validity window. If the user does not approve, stop.list_chains).parse_intent to extract sell token, buy token, amount, and chain. Note that parse_intent returns token symbols, not addresses.list_chains and use a chainId from tradeable. If the chain is in paused, tell the user it is not live yet.resolve_token(chainId, symbol). Use canonical.address and canonical.decimals when found is true and ambiguous is false; confirm an ambiguous result with the user; for a found: false symbol, fall back to the on-chain readback plus user confirmation. Apply hard rule 4 for native coins.expected_surplus for beatBps, the difference versus a public reference aggregator. It is advisory, not a guarantee; a positive value means Ophis quoted more output than that single reference.get_quote with kind ("sell" or "buy"), the amount in atoms, the two addresses, and the trader address from.build_order with the owner, the tokens, kind, the slippage-adjusted sellAmount and buyAmount (scaled with each token's true decimals, hard rule 2), an explicit slippageBips (hard rule 3), and, if you are an Ophis integrator, your referrerCode. The result includes order, signing (EIP-712 domain, types, primaryType), fullAppData, appDataHash, and partnerFee.order.buyAmount (your minimum received) with the buy token's true decimals and confirm it is in the ballpark the user expects. If it looks far too low, stop and recheck decimals and the buy token address.signing payload with the owner wallet. If not approved, stop.submit_order with the exact order object, the signature, from (the owner), and the exact fullAppData string from step 6. It returns the order UID.validTo. The MCP has no cancel tool; an open order can still be cancelled gasless with a signed EIP-712 cancellation, see the ophis-cancel skill at https://ophis.fi/.well-known/agent-skills/ophis/skills/ophis-cancel.md. Otherwise it stands until it fills or expires.A market-style sell, "swap 100 USDC for WETH on Base". The argument names and response shapes below follow reference.md; the addresses are placeholders, so always take the real ones from resolve_token.
textparse_intent(text="swap 100 USDC for WETH on Base") -> { intent: "swap", entities: [ { type: "amount", value: "100" }, { type: "sellToken", value: "USDC" }, { type: "buyToken", value: "WETH" }, { type: "chain", value: "Base" } ] } # Read the entity values, then map the symbols and chain name to ids yourself. list_chains() # confirm Base, chainId 8453, is `tradeable` resolve_token(chainId=8453, symbol="USDC") # -> canonical.address = <USDC>, decimals = 6 resolve_token(chainId=8453, symbol="WETH") # -> canonical.address = <WETH>, decimals = 18 # Amounts are atoms = whole units x 10^decimals: 100 USDC (6 dp) -> "100000000" get_quote(chainId=8453, sellToken=<USDC>, buyToken=<WETH>, kind="sell", amount="100000000", from="<your wallet>") -> { quote: { sellAmount, buyAmount, feeAmount, validTo } } # Minimum received = quote.buyAmount adjusted down for slippage (here 75 bps). build_order(chainId=8453, owner="<your wallet>", sellToken=<USDC>, buyToken=<WETH>, kind="sell", sellAmount="100000000", buyAmount="<min from quote>", slippageBips=75) -> { order, signing: { domain, types, primaryType: "Order" }, fullAppData, appDataHash, partnerFee } # Confirm with the user before signing (hard rule 5): the buy token ADDRESS, the # minimum received (order.buyAmount at WETH's 18 decimals), the slippage, the fee (partnerFee; order.feeAmount is 0), # and the validity window. Only on explicit approval, sign the `order` object as # EIP-712 typed data using `signing` (domain + types + primaryType); the receiver # is pinned to owner. If the user does not approve, stop. submit_order(chainId=8453, order=order, signature="0x...", from="<your wallet>", fullAppData=fullAppData) -> orderUID
get_balances and get_portfolio: native and ERC-20 balances on one chain or across chains. Use these for the token readback in hard rule 1.get_gas: current gas price. Ophis trades are gasless for the trader, so this is informational.get_token_chart: OHLCV price history. It is backed by a shared keyless quota, so cache results and do not poll tightly.lookup_tier: a wallet's fee-rebate tier and rebate percentage.expected_surplus: the beat-the-market comparison described above.Ophis applies a small volume fee, shown as partnerFee (currently 5 bps), and shares a rebate back through its referrer program. The order's feeAmount is always 0; the volume fee is the only charge. The rebate is tiered by 30-day USD volume: it starts at the bronze tier (about 20,000 USD of 30-day volume, 10 percent) and rises through silver, gold, palladium, and platinum (1,000,000 USD, 50 percent); below the bronze threshold a wallet earns 0 percent. Pass a referrerCode in build_order to attribute volume to your code, and check a wallet's current tier and rate with lookup_tier.
Trading is live on Ethereum (1), Optimism (10), BNB Chain (56), Gnosis (100), Unichain (130), Polygon (137), Base (8453), Arbitrum (42161), Avalanche (43114), Plasma (9745), Ink (57073), and Linea (59144). Some chains have settlement deployed but no live orderbook yet. Always treat list_chains as the authoritative live set, since it splits tradeable from paused at runtime. The read tools (get_balances, get_portfolio, get_gas, get_token_chart) cover the subset of chains that have a keyless public RPC.
Orders are signed with a validity window (validForSeconds, default 1200 seconds, that is 20 minutes; minimum 60). A submitted order is a live, fillable commitment at the signed limit price for that whole window, so keep the window short for a market-style intent. Orders default to fill-or-kill (partiallyFillable is false), which is the safer default. The MCP has no cancel tool; to cancel an open order gasless, follow the ophis-cancel skill from the Ophis skill family (https://ophis.fi/.well-known/agent-skills/ophis/). A submitted order that is not cancelled stands until it fills or expires.
Intent parsing and the beat-the-market comparison are also reachable over plain HTTP at https://swap.ophis.fi/api/intent and https://swap.ophis.fi/api/beat-market (no key, allow-listed origins). The full build-and-submit flow is available only through the MCP tools.
See reference.md for the exact input and output schema of every tool.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 13,875 | 10,557 | -24% | 1 | 1 | 0% | 3,058 | 3,956 | +29% | 0 | 0 | — |
case-02 | fail→fail | 20,351 | 7,796 | -62% | 1 | 1 | 0% | 3,363 | 4,209 | +25% | 0 | 0 | — |
case-03 | fail→fail | 21,191 | 11,200 | -47% | 1 | 1 | 0% | 3,465 | 3,513 | +1% | 0 | 0 | — |
case-04 | fail→pass | 3,755 | 9,990 | +166% | 1 | 1 | 0% | 655 | 4,408 | +573% | 0 | 0 | — |
case-05 | fail→pass | 17,319 | 4,833 | -72% | 1 | 1 | 0% | 1,387 | 4,293 | +210% | 0 | 0 | — |
case-06 | pass→pass | 8,456 | 11,032 | +30% | 1 | 1 | 0% | 1,578 | 4,535 | +187% | 0 | 0 | — |
case-07 | pass→pass | 15,836 | 11,593 | -27% | 1 | 1 | 0% | 1,844 | 4,764 | +158% | 0 | 0 | — |
case-08 | fail→pass | 4,837 | 4,257 | -12% | 1 | 1 | 0% | 908 | 4,316 | +375% | 0 | 0 | — |
case-09 | pass→pass | 4,214 | 2,967 | -30% | 1 | 1 | 0% | 768 | 3,887 | +406% | 0 | 0 | — |
case-10 | pass→pass | 4,609 | 2,026 | -56% | 1 | 1 | 0% | 878 | 3,768 | +329% | 0 | 0 | — |
case-11 | fail→pass | 8,739 | 3,229 | -63% | 1 | 1 | 0% | 1,304 | 3,921 | +201% | 0 | 0 | — |
case-21 | fail→fail | 10,293 | 8,173 | -21% | 1 | 1 | 0% | 1,738 | 3,905 | +125% | 0 | 0 | — |
case-12 | fail→pass | 13,240 | 5,161 | -61% | 1 | 1 | 0% | 1,330 | 4,368 | +228% | 0 | 0 | — |
case-13 | fail→pass | 8,491 | 7,953 | -6% | 1 | 1 | 0% | 1,445 | 3,872 | +168% | 0 | 0 | — |
case-14 | fail→pass | 15,872 | 9,906 | -38% | 1 | 1 | 0% | 1,868 | 4,278 | +129% | 0 | 0 | — |
case-15 | fail→pass | 18,518 | 8,486 | -54% | 1 | 1 | 0% | 2,474 | 3,885 | +57% | 0 | 0 | — |
case-16 | fail→pass | 22,402 | 11,634 | -48% | 1 | 1 | 0% | 2,939 | 4,550 | +55% | 0 | 0 | — |
case-17 | fail→pass | 13,681 | 3,704 | -73% | 1 | 1 | 0% | 2,508 | 4,041 | +61% | 0 | 0 | — |
case-18 | fail→pass | 13,004 | 3,798 | -71% | 1 | 1 | 0% | 1,310 | 4,044 | +209% | 0 | 0 | — |
case-19 | fail→fail | 22,457 | 3,429 | -85% | 1 | 1 | 0% | 2,879 | 3,999 | +39% | 0 | 0 | — |
case-20 | fail→fail | 12,369 | 7,773 | -37% | 1 | 1 | 0% | 2,207 | 3,793 | +72% | 0 | 0 | — |
case-22 | fail→pass | 37,554 | 1,909 | -95% | 1 | 1 | 0% | 5,471 | 3,666 | -33% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 18 counted toward the lift figure. The other 4 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +55 percentage points is the difference between those two pass rates over the 18 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.