Install
openclaw skills install @agora0x/mentatUse when an agent needs to quote a token swap, read the MENTAT open order book, check or prepare an OTC escrow deal, or verify a token by its address. MENTAT is the agent-native OTC venue. Escrow settlement on Base (atomic, no admin keys), CoW Protocol relay (agent-signed, MEV-protected), and an XMR route via Wagyu (XMR/USDC both directions, three legs, one proof per leg). Every quote and order response carries settlement_path, route_legs, and proofs[]. FloorEscrow V3 deals are read with get_book and get_deal and prepared unsigned with create_deal; tokens are judged by address (verified, unverified, lookalike), never by ticker. Quotes are free. MENTAT signs nothing for you, so get the human's explicit OK before any step is signed or sent.
openclaw skills install @agora0x/mentatDisciplined intelligence. MENTAT settles token swaps for agents three ways, and every fill carries whatever its chain can prove.
| settlement_path | What settles | Trust model | Proofs |
|---|---|---|---|
escrow | 1:1 swap through a FloorEscrow contract on Base, 25 bps fee. V2 is behind execute_trade: both sides deposit, one atomic settlement. V3 is behind web deals (mentat.market/new and deal links): the maker funds the deal, the taker's fill settles it in one transaction. | Trustless: no owner, no pause, no upgrade. | V2: escrow_create_tx, then deposits and settlement on the contract page. V3: the fill transaction (DealSettled). |
cow_relay | The calling agent signs an EIP-712 order; MENTAT relays it to CoW Protocol's batch auction on Base. 25 bps partner fee inside the signed appData. | Non-custodial: MENTAT never holds funds. | cow_order_uid, then one settlement_tx per batch that filled it. |
wagyu_route | XMR in or out. XMR is routed through XMR1 on Hyperliquid by Wagyu, the settlement counterparty. MENTAT never holds XMR. 25 bps integrator fee applied inside Wagyu's quoted amount. | Not trustless. You rely on Wagyu to deliver; the route gives you a proof per leg instead. | deposit_tx (the XMR transaction hash), one fill_tx per Hyperliquid fill hash, payout_tx. |
ERC-8004 Agent #31596 on Base Mainnet. Contact: trade@mentat.market.
This applies to every agent, on every surface (MCP, A2A, REST). Some agents, Hermes among them, call MCP tools without asking the human first, so this skill is where the check happens.
to, data, value) or EIP-712 data to sign. Nothing moves until a wallet signs and sends.intent line is written for this. Then wait for an explicit yes to that exact deal.to must be on the official list at https://api.mentat.market/.well-known/contracts.json (an escrow, the token you are approving, or the CoW contracts it names).Anyone can deploy a token called USDC. Its symbol, its name and the balance it reports all come from its own code. MENTAT decides what a token is the only way that cannot be faked: by its ADDRESS, against MENTAT's verified list (the supported tokens below). Every side of every deal carries one of three verdicts, the same ones mentat.market shows people:
trust | Meaning | What MENTAT does |
|---|---|---|
verified | The address is on the list. | Named by the list's symbol. |
unverified | Not on the list, and not imitating a listed token. | Allowed, with a warnings[] entry and a warning on the fill steps: check the contract address yourself. Hidden on the book unless include_unverified=true; create_deal needs allow_unverified: true. |
lookalike | Not on the list, but its symbol or name reads as a listed or protected one (Unicode lookalikes from other scripts, digits for letters, invisible and bidi characters, ETH). A fake. | Never listed on the book, always refused by create_deal, and get_deal answers blocked: true with a DO NOT DEPOSIT warning and no fill steps. |
unknown | Not on the list, and its symbol or name could not be read (not yet, or the chain did not answer just now), so it could not be checked for imitation. | No fill steps (blocked: true on get_deal, fill: null on the book), a token_unchecked warning; create_deal refuses it with chain_unavailable. Don't deposit until it can be checked. |
warnings[] entries are { code, side, token, message } with code unverified_token, lookalike_token (it also names what it imitates and the real_address) or token_unchecked. A chain that does not answer never weakens a verdict: "the RPC timed out" is unknown, never "this token has no name". Compare addresses, not tickers, before any approval.
MENTAT never DMs deal links. An id the contract says does not exist answers found: false, status: "not_found": do not send funds to anyone claiming otherwise. When a deal cannot be checked right now, the answer is status: "unknown" with code: "chain_unavailable", never not_found: don't deposit until it can be checked. The Telegram bot checks a https://mentat.market/deal/0x… link sent to it the same way (in groups when mentioned or replied to), and warns about /deal/ links on any other host.
Every quote and order response carries:
settlement_path: escrow, cow_relay, wagyu_route, or quote_only (a price with no executable path).route_legs[]: the legs of that path, each { leg, name, chain, chain_id, proof_kind, proofs } where proofs says how many proofs the leg can carry (one, one or many, zero, one, or many).proofs[]: { chain, kind, hash, explorer_url }, one entry per hash the chain has exposed so far. Empty on a quote. Nothing is inferred: a leg without a hash yet has no entry.Amounts on the XMR route are decimal strings. Never send a float.
curl -s "https://api.mentat.market/api/quote?from=USDC&to=WETH&amount=1000" | jq
curl -s -X POST https://api.mentat.market/a2a \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tasks/send","params":{"skill_id":"get_quote","arguments":{"from_token":"USDC","to_token":"WETH","amount":1000}},"id":1}'
Quotes are free on every surface (REST, A2A and MCP). MENTAT earns its 25 bps fee on settlement, never on a quote.
curl -s "https://api.mentat.market/api/prices" | jq
Each price carries its source and observed_at (the time of the read it came from); stale: true means a refresh failed and the last read is older than 60 seconds. fallback: true marks a fixed guess used only while CoinGecko has never answered: never trade on it.
Hermes Agent reads MCP servers from ~/.hermes/config.yaml. Add MENTAT under the name mentat:
mcp_servers:
mentat:
url: "https://api.mentat.market/mcp"
Hermes names each tool mcp_<server>_<tool>, so with the name mentat the tools are mcp_mentat_get_book, mcp_mentat_get_deal, mcp_mentat_create_deal, mcp_mentat_get_quote and the rest of the skills table below, in the toolset mcp-mentat. No API key is needed.
A first test prompt: "Using MENTAT, show me the open order book on Base and explain what you see".
This skill also works without the MCP server, over the REST and A2A calls in this file. Hermes does not ask before MCP tool calls: follow "Before anything is signed" above.
wagyu_route)Pairs: XMR ↔ USDC on Base, both directions. Minimums are direction-aware: sell XMR from $25, buy XMR from $500 (below $500 a buy loses most of its value to Hyperliquid size rounding). Maximum $5,000 per order. MENTAT earns a 25 bps integrator fee on every order, applied inside Wagyu's quoted amount: it is already included in to_amount, reported by Wagyu as integrator_fee, and counted in effective_cost_bps, the all-in number.
curl -s -X POST https://api.mentat.market/a2a \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tasks/send","params":{"skill_id":"get_quote","arguments":{"from_token":"XMR","to_token":"USDC","amount":"0.1","to_address":"0xYourBaseAddress"}},"id":1}'
The response's quote carries:
settlement_path: "wagyu_route", venue: "wagyu", the counterparty and a custody line (MENTAT never holds XMR; not trustless).from_amount, to_amount) and atomic units (from_amount_atomic, to_amount_atomic), plus from_amount_usd.effective_cost_bps (the all-in number), mentat_fee_bps: 25 and Wagyu's integrator_fee (already included in to_amount).direction, min_notional_usd, max_notional_usd, tier_max_notional_usd, depth_cap_usd, and valid_for_seconds.reference: the Hyperliquid price check (below).route_legs[]: three legs. source (the XMR network, chain_id 0, proof_kind: deposit_tx), hyperliquid_swap (chain_id 1337, fill_tx, zero or more), destination (Base, chain_id 8453, payout_tx).proofs: [] (a quote has none) and a note that it is not a funding instruction.Full example responses for the XMR route, with every field, are on https://mentat.market/agents#quote and in https://mentat.market/llms.txt.
A quote is an estimate. Only the created order's deposit address, amount, and expiry are the funding instruction.
Every XMR quote is checked against Hyperliquid before Wagyu is asked, and every XMR quote and order response carries the reading in reference:
"reference": { "source": "hyperliquid", "perp_mark": 551.08, "spot_mid": 550.98, "depth_1pct_usd": 369032, "depth_side": "asks", "divergence_pct": 0.02, "observed_at": "2026-09-23T20:30:00.000Z" }
perp_mark is the XMR perpetual mark price; spot_mid is the XMR1/USDC spot mid (pair @260). A quote is refused with code price_divergence when they disagree by more than 1%, price_stale when either reading is older than 60 seconds, and price_unavailable when Hyperliquid cannot be read. Nothing is quoted from a single or stale source.depth_1pct_usd is the resting size within 1% of mid on the side the order hits (depth_side: bids when selling XMR, asks when buying). The per-order cap is the smaller of the tier cap and half of that depth; quotes report both as tier_max_notional_usd and depth_cap_usd, and above_maximum says which one bound.reference. check_xmr_order re-reads the live one and, if the live check would refuse, adds reference_warning to the response; an existing order is never failed by it.to_address is required (Base address for USDC, XMR address for XMR). refund_address on the source chain is optional.
curl -s -X POST https://api.mentat.market/a2a \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tasks/send","params":{"skill_id":"create_xmr_order","arguments":{"from_token":"XMR","to_token":"USDC","amount":"0.1","to_address":"0xYourBaseAddress"}},"id":1}'
REST equivalent: POST /api/xmr/orders with the same JSON body. The response's order.deposit is the funding instruction and order.next_steps spells it out. Send exactly that amount to that address before order.expires_at.
The destination address is the read proof. An order_id with the wrong to_address is simply not found.
curl -s -X POST https://api.mentat.market/a2a \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tasks/send","params":{"skill_id":"check_xmr_order","arguments":{"order_id":"ord_1","to_address":"0xYourBaseAddress"}},"id":1}'
REST equivalent: GET /api/xmr/orders/{order_id}?to_address=0x.... The response's order carries:
order_id, settlement_path: "wagyu_route", status (for example completed), the tokens, and to_address.deposit: the funding instruction (address, chain, token, amount).expected_output and actual_output, mentat_fee_bps and mentat_fee_usd.legs: source (state, confirmations of required_confirmations, tx_hash), hyperliquid (state, fill_count, receipts_status, tx_hashes), destination (state, tx_hash).proofs[]: one { chain, kind, hash, explorer_url } per hash so far. The XMR deposit (deposit_tx, explorer xmrchain.net), each Hyperliquid fill (fill_tx, app.hyperliquid.xyz), and the Base payout (payout_tx, basescan.org).status_check: the exact check_xmr_order call to poll with.Several Hyperliquid fills can share one transaction hash; proofs[] lists each distinct hash once. Fill hashes are appended as Wagyu saves them, including after completed, so poll until legs.hyperliquid.receipts_status is complete.
When Wagyu answers 503 unavailable, MENTAT pauses the route instead of retrying. Quotes and order creation answer:
{ "error": "XMR routing paused", "code": "routing_paused", "routing_paused": true, "until": "2026-09-14T12:05:00.000Z", "retry_after_seconds": 300 }
REST returns HTTP 503 for it. get_route_status (or free GET /api/routes) reports paused, until, and reason. Other machine codes: below_minimum / above_maximum (MENTAT's floor and cap, with the numbers), no_route (nearby sizes were tried; change the amount), amount_below_minimum (venue minimum), invalid_request (never retried), rate_limited, create_uncertain (creation got no answer and could not be verified: nothing was retried, do not send funds), price_unavailable / price_stale / price_divergence (the Hyperliquid reference refused the quote; see above).
cow_relay)Step 1, ask MENTAT to build the order:
curl -s -X POST https://api.mentat.market/a2a \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tasks/send","params":{"skill_id":"prepare_trade","arguments":{"sell_token":"USDC","buy_token":"WETH","sell_amount":1000,"trader_address":"0xYourAgentWallet"}},"id":1}'
You get back typed_data (EIP-712 domain, types, message), app_data (MENTAT's partnerFee document and hash), quote_summary, preflight (balance and relayer allowance), and relayer.approve_calldata if the GPv2VaultRelayer still needs an approval.
Step 2, sign locally:
const signature = await wallet.signTypedData(typed_data.domain, typed_data.types, typed_data.message);
Step 3, submit through MENTAT:
curl -s -X POST https://api.mentat.market/a2a \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tasks/send","params":{"skill_id":"submit_signed_trade","arguments":{"order":{...},"signature":"0x...","trader_address":"0xYourAgentWallet"}},"id":1}'
Returns order_uid (the first proof) and explorer_url. check_cow_order adds one settlement_tx proof per batch that filled the order.
escrow)Two FloorEscrow contracts are live on Base. execute_trade and check_trade use V2; web deals use V3.
execute_tradecurl -s -X POST https://api.mentat.market/a2a \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tasks/send","params":{"skill_id":"execute_trade","arguments":{"from_token":"USDC","to_token":"DAI","amount":1000}},"id":1}'
Returns the deal, proofs[0] as the escrow_create_tx, and next steps. check_trade reads the deal state from the V2 contract; an id the contract has never seen answers found: false, status: "not_found" (never a default status), and a FloorEscrow V3 deal id says so and points to get_deal. A deployment can keep A2A execution switched off; then execute_trade answers code execution_disabled with the V2 escrow address, and the Telegram bot or the contract itself is the way to open a deal.
FloorEscrow V3 (0x83818036539737a813D1D99f1c6Cd2867c7D6a6d, live on Base since block 52006064) settles the deals people post on the web. No skill signs for it: get_book and get_deal read deals, create_deal prepares a new one unsigned, and maker and taker send every transaction from their own wallets.
https://mentat.market/new. The maker approves exactly the amount plus the 25 bps fee and calls createDeal; the deal is funded in that same transaction and gets a link, https://mentat.market/deal/{id}.deals(dealId)). fill(dealId) pulls the taker's side and pays out every leg in the same transaction: the taker receives the full stated amount, the maker receives the ask, the fee goes to the treasury.https://mentat.market/book, GET /api/book, the get_book skill) for anyone, or any agent, to fill at exactly its amounts; the maker can opt out with a private link, which is not listed on MENTAT. Either way every deal is public on-chain: fill() has no taker restriction, so anyone who finds a deal can fill it at the stated price, and a maker can fill their own (the tape labels that a self-trade). Book rows carry maker_fills ("new maker" or "N fills": the maker's V3 deals that settled on MENTAT before that deal was created; self-trades and MENTAT's own wallets don't count).get_deal: check one deal before you touch itcurl -s "https://api.mentat.market/api/deals/0xYourDealId" | jq
Or {"skill_id":"get_deal","arguments":{"deal_id":"0x…"}} over A2A, or the get_deal MCP tool. The answer reads the contract's deals(dealId) when the chain answers (source: "chain"), else MENTAT's index (source: "indexer", with synced and a warning when it lags). It carries status (open, expired, settled, cancelled, not_found, unknown), the terms in the book's row shape (taker_pays and taker_receives with trust, price, reference, distance, fee, deal_url), listing (open or private), warnings[], and fill.steps only from a chain read just now of an open deal whose tokens are neither fake nor unchecked (blocked: true otherwise; an index-only answer never has steps and says status_unconfirmed). An id the chain says does not exist is not an error:
{ "found": false, "status": "not_found", "message": "No deal with this id on FloorEscrowV3. Do not send funds to anyone claiming otherwise." }
When neither the chain nor the index can confirm the id (REST answers 503):
{ "found": null, "status": "unknown", "code": "chain_unavailable", "retryable": true, "retry_after_seconds": 10, "message": "Couldn't confirm this deal right now. Don't deposit until it can be checked; open it on mentat.market/deal/0x…." }
create_deal: prepare a deal, sign it yourselfcurl -s -X POST "https://api.mentat.market/api/deals/prepare" -H "Content-Type: application/json" \
-d '{"give_token":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","give_amount":"1000","get_token":"0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb","get_amount":"1001","expiry_seconds":3600,"listing":"open"}'
(1,000 USDC for 1,001 DAI: the maker asks slightly above market, so the price check passes. A deal that gives the taker more than 5% over market, for instance WETH offered well under its current price, is refused with price_generous unless acknowledge_price: true.)
Input: give_token and get_token (addresses), give_amount and get_amount (decimal strings, parsed with each token's decimals; amounts the contract's uint256 arithmetic could not hold are refused), expiry_seconds (600 to 2592000, default 3600), listing (open, the default, or private), and optionally maker (its balance and allowance are read and reported as warnings), allow_unverified (needed for a token off the list; its decimals are then read from the chain), acknowledge_price (needed when the deal hands the taker more than 5% over market, the same check as mentat.market/new). Output: chain_id, contract, fee_bps, fee_atomic, total_debit_atomic (the amount plus the 25 bps fee, which createDeal pulls), two steps with to, data and value (an exact approve to the escrow, never unlimited, then createDeal), price_check (within, generous, above or no_reference, with distance_bps), warnings[] and a listing_note: a private link is not enforced on-chain, so anyone who finds the deal can fill it. Send step 1, then step 2, from the maker wallet; the deal id is topics[1] of the DealCreated log in the step 2 receipt. Share the link only after it confirms. While the deal is unfilled the maker can cancel it at any time, before or after expiry, and gets back the amount plus the fee.
An unlisted token whose symbol and name the chain could not be asked about is refused with chain_unavailable, allow_unverified or not: the lookalike check could not run. Refusals carry { error, code, retryable } (retryable is true only for chain_unavailable, with retry_after_seconds): invalid_request, invalid_address, same_token, invalid_amount, invalid_expiry, invalid_listing, unverified_token, lookalike_token, chain_unavailable, token_unreadable, price_generous. REST answers 400 for malformed input, 422 for a refusal on the merits, 503 when an unlisted token needs a chain read that could not be made, and 429 (rate_limited, with retry_after_seconds) past 30 requests a minute per client on /api/deals.
FloorEscrowV31 is the escrow MENTAT launches on Ethereum mainnet (chain 1). It is switched off until MENTAT deploys it: the official list GET /.well-known/contracts.json names every enabled deployment in deployments[] (CAIP-2 eip155:1, version v3.1, address, deploy block, selectors). Until an Ethereum entry is there, chain_id: 1 answers invalid_request. Base keeps trading on FloorEscrow V3, unchanged.
The same three skills take an optional chain_id (8453 Base, the default for create_deal; 1 Ethereum). Without it, get_book and get_deal cover every enabled deployment, and every row says where it lives: chain_id, chain, escrow, escrow_version. An Ethereum deal link is https://mentat.market/deal/{id}?chain=1.
What differs from V3:
fee.bps, fee.leg). When the fee is on the token the taker pays, they pay amountB + fee; when it is on the token they receive, they get amountA − fee. Either way the maker gets exactly their price. A pair with no quote asset is refused (no_quote_asset).create_deal with chain_id: 1): approve exactly give_amount, with no fee on top, then createDeal(CreateParams), which carries a 20-minute deadline. lock_eth: true (give WETH) makes it one step: createDeal with value, no approve. receive_eth: true (get WETH) pays you in ETH. taker names the only address that may fill, and recipient receives get_amount. Neither may be WETH or the escrow (invalid_recipient).get_book / get_deal): the steps come from a fresh previewFill(dealId, 0). Approve exactly pay of the token you pay, then fill(FillParams) with maxPay = pay, minReceive = receive, a 10-minute deadline and no integrator. When you pay WETH, fill.pay_with_eth is the one-step alternative: value = pay, no approve. fill.valid_until is that deadline (or the expiry, if sooner).gas_hint.add_gas (200000). Set the gas limit to your wallet's estimate plus that.approve(escrow, 0) (approve_reset), or say so in fill.reset_first. If Tether turns on USDT's transfer fee, USDT deals show no steps and create_deal refuses USDT (token_fee_on).create_deal refuses a bigger deal (over_launch_cap), and refuses one it can't value (cannot_value, retryable). A bigger deal made on the contract directly is still listed, with over_cap: true and no fill steps. Ethereum deals last 24 hours at most (create_deal with chain_id: 1).submit_via (Flashbots Protect, https://rpc.flashbots.net/fast). Send creates, fills and cancels through it: it keeps them out of the public mempool, so bots can't front-run them.refunded: after expiry anyone may call refundExpired(dealId), which returns exactly the locked amount to the maker. cancel also returns exactly the locked amount.Integrators (wallets, agent apps) may pass their own integrator and integratorBps (at most 50) in previewFill and fill, and collect with withdrawIntegratorFees. MENTAT's own surfaces always send 0.
get_book, get_deal, create_deal)to, data, value (always "0"), on Base (chain 8453), from your own wallet, in order. function and args say what the calldata is.intent: one plain line, for example "Approve exactly 1002.5 of USDC to MENTAT's escrow 0x8381…6a6d for a new deal: 1000 plus the 2.5 fee". Pass it to your wallet's approval screen (MetaMask Agent Wallet: --intent). A token MENTAT does not list is named unverified token 0x1234…abcd, its own symbol added only when it is a plain ticker of at most 12 characters; an amount whose decimals are unknown is given in base units.if_abandoned is approve(escrow, 0) on the token you approved. Send it only if step 1 went through and you will not send step 2: the escrow can only pull from whoever sends step 2, but resetting leaves nothing approved. Some tokens refuse to change one non-zero allowance to another: if step 1 reverts while an old allowance is set, send if_abandoned first, then step 1 (create_deal warns existing_allowance when it sees one).create_deal answers prepared_at and valid_until (10 minutes later). The transactions don't expire, but the price check does: after valid_until, call create_deal again before sending step 2. Fill steps carry fill.valid_until, the deal's expiry (exclusive: the fill must be mined before it).retryable. It is true only when the same call can succeed later unchanged (chain_unavailable, a rate_limited 429, a 500), with retry_after_seconds; everything else needs different input, so don't retry it as is.MENTAT's servers hold one Ethereum key, the operator key: the agent wallet 0xE6f26F05C956D1833A1DAaaAD57CD0a1E626bfd3. On-chain it is the owner of ERC-8004 Agent #31596 (ownerOf(31596) on the Identity Registry) and the immutable treasury of the FloorEscrow V2 contract (treasury(); feeBps() = 25). It signs three kinds of transaction and nothing else:
| Contract | Function | Gate in the contract | Effect |
|---|---|---|---|
FloorEscrow V2 0x9EC9…3AA7 | createEscrow(tradeId, buyer, seller, tokenBuy, tokenSell, amountBuy, amountSell, agentId, duration) | identityRegistry.ownerOf(agentId) == msg.sender | Records the terms of a deal (execute_trade, and the Telegram bot's escrow flow). Moves no tokens. Terms are final once written, duration is capped at 7 days, a tradeId cannot be reused. |
| FloorEscrow V2 | settle(tradeId) | none: anyone may call; status must be BothDeposited | Pays 25 bps of each side to the immutable treasury and the rest to the counterparty named in the deal. The caller chooses nothing. The Telegram bot calls it as soon as both deposits land. |
Identity Registry 0x8004…a432 | setAgentURI(31596, uri) | ERC-721 owner of the agent NFT | Updates Agent #31596's metadata (the same operator script minted the agent once with register). No skill triggers it. |
Like any address it can also call refund(tradeId) (after expiry only; full refund to whoever deposited, no fee) and getTrade(tradeId) (read). depositBuyer / depositSeller accept only the buyer or seller named in the deal and pull tokens from msg.sender alone, so the key can deposit only its own tokens, and only where it is itself the named party (the Telegram bot names it as buyer of the deals it opens).
The key cannot withdraw or sweep anything from the escrow (no such function exists), change the fee or the treasury (both immutable), pause, upgrade, or kill the contract (no owner, no proxy, no admin role), amend or cancel a deal before it expires, deposit or move a counterparty's tokens (approvals go to the contract, never to the key), settle early, or send a settlement anywhere but the addresses stored in the deal. A leaked key could open junk deals, rewrite the agent's metadata or transfer the agent NFT, and spend the wallet's own balance (gas, collected fees, x402 receipts); tokens sitting in escrow would still move only where the contract rules send them.
FloorEscrow V3 (0x8381…6a6d, web deals) gives the key no special role. It has no operator-gated function: the maker alone creates, funds and cancels a deal, any taker fills it, and its fee recipient is a separate immutable address (treasury()), not this key.
No other server code uses the key. get_quote, check_trade, get_prices, get_route_status, get_book and get_deal only read; create_deal returns unsigned calldata for the maker's own wallet and signs nothing. The CoW relay never signs: the trader signs, MENTAT forwards the signed order to api.cow.fi. The XMR route talks to Wagyu over HTTPS with an API credential (WAGYU_INTEGRATOR_KEY), signs no transaction, and never holds XMR. The MCP endpoint exposes the same skills through the same handlers and holds no key of its own. Without PRIVATE_KEY, or with A2A execution switched off, execute_trade answers with an error (execution_disabled) and the V2 escrow address to call directly; everything else is unaffected.
| Skill | Path | What it does |
|---|---|---|
get_quote | any | Live quote. XMR pairs answer through the Wagyu route with settlement_path: "wagyu_route" and the Hyperliquid reference. |
get_prices | — | CoinGecko prices for the escrow and CoW pairs, each with source and observed_at; guesses flagged fallback: true. XMR is priced live at quote time. |
create_xmr_order | wagyu_route | Durable XMR order; returns the funding instruction. |
check_xmr_order | wagyu_route | Three legs and proofs[]; needs order_id + to_address. |
get_route_status | wagyu_route | Live or paused, with the pause deadline and reason. |
prepare_trade | cow_relay | Build the EIP-712 order for the agent to sign. |
submit_signed_trade | cow_relay | Relay the signed order to the CoW order book. |
check_cow_order | cow_relay | Order state plus settlement transaction proofs. |
execute_trade | escrow | Create an escrow deal on Base (FloorEscrow V2). |
check_trade | escrow | Read a FloorEscrow V2 deal by id; not_found for an unknown id, with a pointer to get_deal for a V3 deal id. |
get_book | escrow | The open book of FloorEscrow V3 deals in the taker frame, with trust per side, reference, distance and fill steps. |
get_deal | escrow | One FloorEscrow V3 deal by id: status, terms, trust, warnings[], fill steps only from a chain read of an open deal; not_found only when the chain says so, else unknown. |
create_deal | escrow | Prepare a FloorEscrow V3 deal: an exact approve and createDeal as unsigned calldata, with the price check. |
| Token | Address |
|---|---|
| USDC | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
| USDT | 0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2 |
| USDbC | 0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA |
| DAI | 0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb |
| WETH | 0x4200000000000000000000000000000000000006 |
| XMR | native XMR, routed via Wagyu (XMR1 0xbB2057659bD378D17E2E151BB04bdcaA00000000 on Hyperliquid spot) |
Quotes only: ETH, BTC, WBTC, UNI, LINK, AAVE.
The Base rows above are MENTAT's verified list: the only tokens trust: "verified" is ever given to, by address.
Quotes are at real market rates. MENTAT charges 25 bps on every fill, on every path: escrow (immutable in the contract; V2 deducts it from each side on settlement, V3 charges it on top of the maker's side), CoW relay (partnerFee inside the appData the trader signs), and the XMR route (a 0.25% integrator fee applied inside Wagyu's quoted amount, so it is already in to_amount and reported by Wagyu as integrator_fee). On the XMR route effective_cost_bps is the all-in number: Wagyu's own costs plus MENTAT's fee.
https://api.mentat.market/.well-known/agent.json (also at /.well-known/agent-card.json)https://mentat.market/.well-known/skills/mentat/SKILL.md, listed in https://mentat.market/.well-known/skills/index.json and https://mentat.market/.well-known/agent-skills/index.jsonPOST https://api.mentat.market/a2aGET https://api.mentat.market/api/quote?from=XMR&to=USDC&amount=0.1&to_address=0x...POST /api/xmr/orders, GET /api/xmr/orders/{id}?to_address=0x...GET https://api.mentat.market/api/routesGET https://api.mentat.market/api/priceshttps://api.mentat.market/mcp (legacy SSE at /sse + /messages)GET https://api.mentat.market/api/tape?limit=50 (every fill on every path, newest first, keyset cursor), GET https://api.mentat.market/api/tape/{id} (one receipt), GET https://api.mentat.market/api/stats (24h and all-time per path; a stable leg adds volume only by its token address)GET https://api.mentat.market/api/book, GET https://api.mentat.market/api/book/reference, GET https://api.mentat.market/api/makers/{address}GET https://api.mentat.market/api/deals/{id} (get_deal), POST https://api.mentat.market/api/deals/prepare (create_deal, unsigned)GET https://api.mentat.market/healthhttps://t.me/FloorOTCbothttps://mentat.market · public tape and receipts: https://mentat.market/ledger (one receipt per fill at /ledger/{id})https://mentat.market/new; a deal lives at https://mentat.market/deal/{id}The official list, machine-readable: https://api.mentat.market/.well-known/contracts.json (the escrow with its function selectors, the other contracts, the verified tokens, and the addresses an agent wallet needs to allowlist). Match on these addresses, never on a name or ticker.
get_book, get_deal, create_deal, web deals at mentat.market/new and deal links): 0x83818036539737a813D1D99f1c6Cd2867c7D6a6d (verified on Basescan, deployed at block 52006064)execute_trade, check_trade, the Telegram bot): 0x9EC9d882C93F52621CBD0d146D3F2e0929E53AA7 (verified on Basescan)0x31Acb13BA6F76e2fc79cD301578538EEcde194dB is retired: do not approve tokens to it or send it funds.0x9008D19f58AAbD9eD0D60971565AA8510560ab410xC92E8bdf79f0507f65a392b0ab4667716BFE01100x8004A169FB4a3325136EB29fA0ceB6D2e539a432, Agent ID 31596No authentication, and every call is free: quotes, order status, route status, prices, the book and the deal tools.
This skill was published as floor-otc. ClawHub supports renaming an owned skill (clawhub skill rename floor-otc mentat, or POST /api/v1/skills/floor-otc/rename with {"newSlug":"mentat"}); the previous slug is kept as a redirect alias, so floor-otc keeps resolving here.