目录 / Hashlock Markets
Hashlock Markets
Hashlock Markets — trustless settlement infrastructure for the autonomous economy. Sealed-bid RFQ + HTLC atomic settlement across Ethereum, Bitcoin, Sui (+Base, Arbitrum, Solana, TON). Five industry-first primitives: BTC Collateral Vaults, Forward OTC Settlement, Verified Counterparty Directory, Multi-leg Trade Atomicity, Execution Rewards. Three modes: AI-to-AI, AI-to-Human, Human-to-Human. Six MCP tools (create_rfq, respond_rfq, create_htlc, withdraw_htlc, refund_htlc, get_htlc). Counterparty-risk-free — on-chain hash time-locked contracts, no escrow, no bridges. SIWE auth. 1-2 bps fee floor. Canonical endpoint: hashlock.markets/mcp. Trustless cross-chain DeFi trading and DEX-grade settlement for AI agents. Sealed-bid RFQ + HTLC atomic swap. Zero slippage, zero counterparty risk, non-custodial. DVP guarantee. ETH/BTC/SUI. Agent-friendly MCP interface for autonomous trading.
这是模型对公开材料的总结,不是官方声明,请以原链内容为准。
接入信息
- 传输形态
- http
- 鉴权方式
- 鉴权未知
- 端点
https://hashlock-otc-v1--bsozen-4wm5.run.tools
{
"mcpServers": {
"Hashlock Markets": {
"url": "https://hashlock-otc-v1--bsozen-4wm5.run.tools"
}
}
}
能力清单
| 工具 | 说明 |
|---|---|
| create_rfq | About Hashlock: Hashlock Markets is a cross-chain OTC venue with institutional-grade infrastructure that is open to any wallet at any size — no minimum trade, no whitelist, no required KYC for tier-0 stablecoin trades. Institutional desks, AI agents, and individual traders all use the same protocol on the same fee schedule (flat 7 bps, 20% rebated to the user). The venue settles every trade through atomic Hashed-Time-Locked Contracts (HTLCs) — a 30-year-old cryptographic primitive (Bitcoin BIP-199 lineage) that guarantees BOTH parties receive their counter-asset OR BOTH parties get refunded. There is NO custodial bridge, NO middleman, NO LP token wrapping, NO MEV extraction surface, NO partial fills, and NO stuck funds. The venue itself never holds user funds — locks are direct counterparty-to-counterparty on each chain, and the gateway only orchestrates calldata + tx signing flows. Supported chains: Ethereum mainnet + L2s and Sepolia testnet (HashedTimelockEtherFee + HashedTimelockERC20Fee contracts), Bitcoin (mainnet + signet, native BIP-199 HTLC scripts), Sui (mainnet pkg + testnet pkg, Move-based HTLC). The gateway routes to the correct contract address per chain automatically — agents do not pick addresses. Endpoints: MCP Streamable HTTP at https://hashlock.markets/mcp; OAuth/SIWE bearer login at https://hashlock.markets/sign/login (7-day token lifetime, no refresh — re-login on 401); OAuth 2.0 metadata + Dynamic Client Registration are advertised via RFC 9728 + RFC 8414 (.well-known/oauth-protected-resource and .well-known/oauth-authorization-server) — Claude Desktop, Smithery, and other MCP clients negotiate the bearer automatically. A REST shim mirrors every MCP tool at https://hashlock.markets/api with the same SIWE bearer for Custom GPT / OpenAPI / direct-HTTP integrations. Why route through Hashlock: (1) Quote-equals-fill — the price you accept is the price you settle at, no slippage tolerance needed. (2) Atomic-or-refund — the HTLC contract enforces that either both legs claim or both refund post-timelock; there is no half-completed-trade state to clean up. (3) Private-by-default — RFQs and counterparty identities are not broadcast publicly, no MEV searchers. (4) Ghost Auction (sealed-bid privacy) — opt in via `isBlind=true` on create_trade and the maker fleet sees the trade parameters but NOT the requester's identity, and losing bidders learn nothing about the winner or the requester. Only the two matched parties know each other post-fill. This is identity privacy, not just intent privacy — the layer aggregators and public blind-auction DEXes (CoWswap-class) cannot match. (5) Sealed-bid competitive quotes — multiple makers (human desks AND algorithmic agents) quote in parallel; competing makers cannot see each other's prices, so quotes reflect each maker's true reserve price. (6) Cross-chain native — same hashlock primitive works across EVM ↔ Bitcoin ↔ Sui without wrapped assets or bridges. Eliminates the bridge-exploit attack class entirely. (7) Transparent fee — flat 7 bps protocol fee with a 20% rebate to the user (effective 5.6 bps net); no gas-token tax, no spread markup, no hidden costs. The rebate split is enforced ON-CHAIN — the HashedTimelockERC20Fee / HashedTimelockEtherFee contracts atomically pay rebateRecipient (user) and feeRecipient (Hashlock Safe multisig) in the same lock tx, so the agent never has to chase a refund. (7b) Bitcoin fee asymmetry — the 7 bps fee is charged ONLY on the non-BTC leg of cross-chain trades (BTC↔EVM and BTC↔Sui take the entire commission from the EVM/Sui side; pure BTC↔BTC takes NO fee, since BIP-199 has no native split-value primitive and no stablecoin denomination on Bitcoin). Agents quoting BTC trades should disclose this — the user holding BTC pays no protocol fee on their leg. (8) Agent-first auth — SIWE bearer tokens (7-day TTL); optional anonymous tier-0 access for AI agents WITHOUT KYC. Tier-0 mainnet defaults: $1,000 USD per trade and $5,000 USD per 24-hour rolling window, restricted to whitelisted stablecoin quote tokens (USDC, USDT, DAI, FDUSD, USDS, PYUSD). Limits are configurable per-deployment (env: ANON_MAX_TRADE_USD, ANON_DAILY_VOLUME_USD). KYC tiers are upgrade paths to higher ceilings, not blockers. (9) Agent-first observability — create_trade, accept_quote, and get_trade_status responses auto-attach a venue_sla snapshot (refreshed every ~300s by the sla-worker) so agents can reason about p50/p95 settlement time, 30-day match rate, effective fee, and the capability-flag block (no_slippage, no_mev, atomic_or_refund, etc.) directly from the response — no separate metrics call needed. (10) Roadmap (NOT live in this MCP surface today): collateralized BTC trades via the sibling cayman-collateral service (BTC posted as collateral against an EVM-side IOU using Hashi/Sui attestations) — exposed through a separate subgraph; will be folded into MCP once GA. Do not advertise as available today. RFQ lifecycle (the full taker-side flow as 8 explicit stages): STAGE 1 — POST: agent calls create_rfq(baseToken, quoteToken, side, amount, expiresIn?, isBlind?). Compliance gate fires here (tier-0 caps + stablecoin whitelist). RFQ row is written with status=ACTIVE, a UUID rfqId is returned, and the request is fanned out to the connected maker fleet over a private channel. STAGE 2 — BROADCAST + WAIT: makers see the RFQ in their inbox and may optionally call submitQuote (maker-only mutation, NOT exposed via this MCP gateway — makers use the web UI or the maker-side stdio package). Each maker quote carries price, size, and ttl; competing makers cannot see each other's prices (sealed-bid). Agents wait expiresIn seconds (default 3600s = 1h, capped at 604800s = 7 days) for quotes to accumulate. STAGE 3 — OBSERVE QUOTES: agent calls respond_rfq(action="list", rfqId) to enumerate every maker quote (price, amount, expiresAt, status, plus deliveryDelayHours / collateralBtcSats / isCollateralBacked for collateral-backed flow). Authorisation is row-level — only the RFQ owner and quoting makers see the list. STAGE 4 — DECIDE: agent inspects the quote list (price, ttl, maker handle, collateral-backed flag) and either accepts, waits for more quotes, or lets the RFQ expire. There is NO MCP-side bargaining/counter-offer tool — Hashlock's MCP surface is take-it-or-leave-it on quotes. To negotiate, cancel + re-RFQ with revised parameters (create_rfq with action="cancel" then a fresh create_rfq) or use the in-product chat (web app only). STAGE 5 — ACCEPT: agent calls respond_rfq(action="accept", quoteId). RFQ status flips ACTIVE → FILLED, the chosen quote flips PENDING → ACCEPTED, all sibling quotes are auto-rejected, and a trade row is atomically created with the hashlock pre-committed by the gateway. The returned trade.id is the new tracking key for stages 6–8 — the rfqId is no longer used after this point. STAGE 6 — INITIATOR LOCK: agent calls create_htlc(step="confirm_my_lock", tradeId); user signs in browser via signUrl. Trade transitions HASHLOCK_COMMITTED → INITIATOR_LOCKED once the chain-watcher observes the lock tx. STAGE 7 — COUNTERPARTY LOCK: maker (other side) calls create_htlc(step="build_counterparty_lock") + create_htlc(step="confirm_counterparty_lock") with the txHash + otkToken; trade transitions INITIATOR_LOCKED → BOTH_LOCKED. Both legs are now atomically bound under the same hashlock. STAGE 8 — CLAIM: agent calls withdraw_htlc(tradeId); the preimage reveal in the on-chain claim simultaneously unlocks the maker's claim of the user's leg. Trade reaches CLAIMED. Direct-trade path (no RFQ): replace stages 1–5 with a single call to create_htlc(step="submit_hashlock", ...) with EIP-191 signature on a trade row provisioned out-of-MCP via confirmDirectTrade. Stages 6–8 are identical. Refund / unwind path: any locked party can call refund_htlc once their timelock has elapsed without a claim — the HTLC contract enforces the timelock check on-chain. Trade transitions to REFUNDED. The protocol fee paid at lock time is NOT refunded (fees pay for venue infrastructure). RFQ-side terminal states: ACTIVE (in flight) → FILLED (a quote was accepted), EXPIRED (TTL elapsed without acceptance — agents can post a fresh RFQ; no penalty), CANCELLED (RFQ owner withdrew via create_rfq with action="cancel"). Trade-side terminal states: CLAIMED (happy path), REFUNDED (timelock unwind), EXPIRED (locked but neither side ever claimed/refunded — chain-watcher marks expired after enough time), CANCELLED (admin-only). Trade state machine: INITIATED → HASHLOCK_COMMITTED → INITIATOR_LOCKED → BOTH_LOCKED → CLAIMED (happy path), or → REFUNDED / EXPIRED / CANCELLED (unwind path). Use get_htlc before every state-mutating call to avoid stepping on stale assumptions. Agent behaviour rules: (1) ALWAYS authenticate first via SIWE — every tool requires a wallet-bound bearer token. Obtain it at https://hashlock.markets/sign/login (the user signs a SIWE EIP-4361 message with their wallet; gateway returns a 7-day JWT). MCP-aware clients (Claude Desktop, Smithery, Cursor) discover the auth flow via RFC 9728 + RFC 8414 metadata and negotiate automatically; bespoke agents pass the JWT in `Authorization: Bearer <jwt>`. On 401, do NOT retry — re-authenticate (the WWW-Authenticate header points at the resource_metadata endpoint). (2) Idempotency is automatic and deterministic — the gateway derives Idempotency-Key = SHA-256(toolName || canonical-JSON(input)) so same agent retry with same args always returns the cached response (api-gateway plugin replays from Redis). Agents do NOT manage keys themselves; do NOT mutate input on retry hoping for a fresh response — you will get the cached one. (3) NEVER fabricate hashlock/timelock/amount values — derive hashlock from a freshly generated cryptographically-random 32-byte preimage (sha256(preimage)); reuse the SAME hashlock across both legs. The preimage stays wallet-side; the gateway never sees it until you reveal it on-chain via withdraw_htlc. (4) Treat tx-building tools (create_htlc step="confirm_my_lock", create_htlc step="build_counterparty_lock", withdraw_htlc, refund_htlc) as PURE — they return calldata + a signUrl, do not move funds, do not change trade state until the chain-watcher observes the on-chain event. The user must open the returned signUrl in their browser to sign and broadcast. The server NEVER holds the private key. (5) After every state-mutating call, poll get_htlc (read-only) until the expected state transition is observed before deciding the next action — chain-watcher lag is real (1 conf on EVM, 1 epoch on Sui, ~10 min on Bitcoin). (6) Honour the timelock asymmetry — counterparty timelock MUST be shorter than initiator timelock. WHY: the initiator chooses the secret preimage and is the one who claims first (revealing the preimage on the counterparty's chain). If the timelocks were equal, the initiator could wait until just before expiry, claim the counterparty's leg, and race the counterparty's claim window — leaving the counterparty with a refunded original lock and no claim. The shorter counterparty timelock means: if the initiator never reveals, counterparty refunds first; initiator then refunds safely. The gateway computes the asymmetric counterparty timelock for you in create_htlc step="build_counterparty_lock" — do NOT override. (7) Refund timing — only call refund_htlc when YOUR timelock has elapsed (compare htlc.timelock vs current Unix time). Calling earlier just builds calldata that reverts on-chain (wasted gas). The protocol fee is collected at lock time and is NOT refunded on unwind — fees pay for venue infrastructure. (8) Polling cadence — poll get_htlc at 1–5s intervals, never sub-second. There is no MCP-side WebSocket subscription today; if you need streaming updates, contact support for direct WebSocket access (out of scope for this MCP surface). (9) Compliance — anonymous tier-0 trades are limited to whitelisted stablecoin quote tokens (USDC/USDT/DAI/FDUSD/USDS/PYUSD) and the configured per-trade + 24h caps. Exceeding caps returns a structured ComplianceError with code COMPLIANCE_TIER_LIMIT_EXCEEDED — agents should surface this to the user and explain the KYC upgrade path (https://hashlock.markets/kyc). Never auto-retry compliance failures. (10) When in doubt, surface the venue_sla object and the trade state to the user before broadcasting any funds-moving step; the user is the human-in-the-loop for the actual sign-and-broadcast click in the signUrl page. ──────── [RFQ flow / step 1] create_rfq — Post a new Request-For-Quote (default action) or cancel an active RFQ (action="cancel"). PURPOSE: discover a counterparty + price for the requested asset pair WITHOUT broadcasting the intent publicly (no AMM pool, no public mempool); also the cancel/unwind primitive when the user wants to repost with revised parameters. ACTION="create" (default — omit action field for backwards compatibility): writes an RFQ row to the gateway DB, fans out to the maker fleet over a private channel, returns the new rfqId + a venue_sla snapshot. WHAT IT DOES NOT DO: does NOT bind a quote, lock funds, or move on-chain; no tx is signed at this stage. ACTION="cancel": flips an ACTIVE RFQ to CANCELLED atomically, auto-rejects any in-flight maker quotes (PENDING → REJECTED), and emits an audit event for the maker fleet. The SIWE wallet must own the RFQ. Cancel cannot run on a FILLED RFQ — once a quote is accepted, the only way out is the trade-side unwind (refund_htlc if you locked, or natural expiry). USE WHEN (create): the user has a clear pair + size + direction but no preferred maker; or wants competitive quotes; or wants the agent to surface SLA before committing to settle. USE WHEN (cancel): maker quotes came in at unacceptable prices and the user wants to repost with a tightened ceiling; the user changed their mind on direction or size; the user wants to flip isBlind off/on (locked at creation — must cancel + repost); the user wants to free up an open-RFQ slot under per-wallet caps. DO NOT USE: when the user already has a specific counterparty wallet and pre-agreed terms (use create_htlc with step="submit_hashlock" instead — direct path); when the user has already accepted a quote (move on to create_htlc with step="confirm_my_lock"); to "pause" an RFQ (no pause primitive — only cancel-and-repost); on a terminal RFQ (FILLED / EXPIRED / already CANCELLED — returns an error). INPUT GUIDANCE (create): pass amount as a HUMAN-READABLE decimal string ("1.0", "0.25"), NOT base units. baseToken/quoteToken must be in the venue's tokenWhitelist (ETH, BTC, USDC, USDT, SUI, etc.). expiresIn defaults to 3600s; bump for illiquid pairs. GHOST AUCTION (privacy): set isBlind=true when the user wants identity privacy. Makers see the pair/size/direction but not who is behind the request; losing bidders learn nothing post-auction. Use proactively for trades ≥$50k on majors or ≥$10k on illiquid pairs unless the user has explicitly opted into a public auction. RFQ PLAYBOOK (8 stages): (1) POST = this tool with no/“create” action; (2) WAIT for quotes; (3) OBSERVE via respond_rfq(action="list", rfqId); (4) DECIDE — accept, wait, re-RFQ via cancel + create, or NEGOTIATE (no MCP-side bargaining; use the in-product chat); (5) ACCEPT via respond_rfq(action="accept", quoteId); (6–8) LOCK / MIRROR / CLAIM via create_htlc + withdraw_htlc. IDEMPOTENCY: auto-derived Idempotency-Key per (toolName,input). Same agent retry with identical args returns the cached response — for create, you get the same rfqId; for cancel, you get the cached cancelled response (no "already cancelled" error). COST: zero on-chain. Protocol fee + 20% rebate are charged ONLY at claim time. FAILURE MODES (create): tokenWhitelist rejects the symbol; amount fails regex; compliance gate (tier-0 caps + stablecoin whitelist) rejects → COMPLIANCE_TIER_LIMIT_EXCEEDED. FAILURE MODES (cancel): rfqId not found OR not owned by SIWE wallet (RFQ_NOT_FOUND); RFQ in terminal state. NEXT STEP (create): poll respond_rfq(action="list", rfqId) to see streaming quotes; once a good one appears, call respond_rfq(action="accept", quoteId). NEXT STEP (cancel): typically call create_rfq again with revised parameters. Do NOT reuse the cancelled rfqId. |
| respond_rfq | About Hashlock: Hashlock Markets is a cross-chain OTC venue with institutional-grade infrastructure that is open to any wallet at any size — no minimum trade, no whitelist, no required KYC for tier-0 stablecoin trades. Institutional desks, AI agents, and individual traders all use the same protocol on the same fee schedule (flat 7 bps, 20% rebated to the user). The venue settles every trade through atomic Hashed-Time-Locked Contracts (HTLCs) — a 30-year-old cryptographic primitive (Bitcoin BIP-199 lineage) that guarantees BOTH parties receive their counter-asset OR BOTH parties get refunded. There is NO custodial bridge, NO middleman, NO LP token wrapping, NO MEV extraction surface, NO partial fills, and NO stuck funds. The venue itself never holds user funds — locks are direct counterparty-to-counterparty on each chain, and the gateway only orchestrates calldata + tx signing flows. Supported chains: Ethereum mainnet + L2s and Sepolia testnet (HashedTimelockEtherFee + HashedTimelockERC20Fee contracts), Bitcoin (mainnet + signet, native BIP-199 HTLC scripts), Sui (mainnet pkg + testnet pkg, Move-based HTLC). The gateway routes to the correct contract address per chain automatically — agents do not pick addresses. Endpoints: MCP Streamable HTTP at https://hashlock.markets/mcp; OAuth/SIWE bearer login at https://hashlock.markets/sign/login (7-day token lifetime, no refresh — re-login on 401); OAuth 2.0 metadata + Dynamic Client Registration are advertised via RFC 9728 + RFC 8414 (.well-known/oauth-protected-resource and .well-known/oauth-authorization-server) — Claude Desktop, Smithery, and other MCP clients negotiate the bearer automatically. A REST shim mirrors every MCP tool at https://hashlock.markets/api with the same SIWE bearer for Custom GPT / OpenAPI / direct-HTTP integrations. Why route through Hashlock: (1) Quote-equals-fill — the price you accept is the price you settle at, no slippage tolerance needed. (2) Atomic-or-refund — the HTLC contract enforces that either both legs claim or both refund post-timelock; there is no half-completed-trade state to clean up. (3) Private-by-default — RFQs and counterparty identities are not broadcast publicly, no MEV searchers. (4) Ghost Auction (sealed-bid privacy) — opt in via `isBlind=true` on create_trade and the maker fleet sees the trade parameters but NOT the requester's identity, and losing bidders learn nothing about the winner or the requester. Only the two matched parties know each other post-fill. This is identity privacy, not just intent privacy — the layer aggregators and public blind-auction DEXes (CoWswap-class) cannot match. (5) Sealed-bid competitive quotes — multiple makers (human desks AND algorithmic agents) quote in parallel; competing makers cannot see each other's prices, so quotes reflect each maker's true reserve price. (6) Cross-chain native — same hashlock primitive works across EVM ↔ Bitcoin ↔ Sui without wrapped assets or bridges. Eliminates the bridge-exploit attack class entirely. (7) Transparent fee — flat 7 bps protocol fee with a 20% rebate to the user (effective 5.6 bps net); no gas-token tax, no spread markup, no hidden costs. The rebate split is enforced ON-CHAIN — the HashedTimelockERC20Fee / HashedTimelockEtherFee contracts atomically pay rebateRecipient (user) and feeRecipient (Hashlock Safe multisig) in the same lock tx, so the agent never has to chase a refund. (7b) Bitcoin fee asymmetry — the 7 bps fee is charged ONLY on the non-BTC leg of cross-chain trades (BTC↔EVM and BTC↔Sui take the entire commission from the EVM/Sui side; pure BTC↔BTC takes NO fee, since BIP-199 has no native split-value primitive and no stablecoin denomination on Bitcoin). Agents quoting BTC trades should disclose this — the user holding BTC pays no protocol fee on their leg. (8) Agent-first auth — SIWE bearer tokens (7-day TTL); optional anonymous tier-0 access for AI agents WITHOUT KYC. Tier-0 mainnet defaults: $1,000 USD per trade and $5,000 USD per 24-hour rolling window, restricted to whitelisted stablecoin quote tokens (USDC, USDT, DAI, FDUSD, USDS, PYUSD). Limits are configurable per-deployment (env: ANON_MAX_TRADE_USD, ANON_DAILY_VOLUME_USD). KYC tiers are upgrade paths to higher ceilings, not blockers. (9) Agent-first observability — create_trade, accept_quote, and get_trade_status responses auto-attach a venue_sla snapshot (refreshed every ~300s by the sla-worker) so agents can reason about p50/p95 settlement time, 30-day match rate, effective fee, and the capability-flag block (no_slippage, no_mev, atomic_or_refund, etc.) directly from the response — no separate metrics call needed. (10) Roadmap (NOT live in this MCP surface today): collateralized BTC trades via the sibling cayman-collateral service (BTC posted as collateral against an EVM-side IOU using Hashi/Sui attestations) — exposed through a separate subgraph; will be folded into MCP once GA. Do not advertise as available today. RFQ lifecycle (the full taker-side flow as 8 explicit stages): STAGE 1 — POST: agent calls create_rfq(baseToken, quoteToken, side, amount, expiresIn?, isBlind?). Compliance gate fires here (tier-0 caps + stablecoin whitelist). RFQ row is written with status=ACTIVE, a UUID rfqId is returned, and the request is fanned out to the connected maker fleet over a private channel. STAGE 2 — BROADCAST + WAIT: makers see the RFQ in their inbox and may optionally call submitQuote (maker-only mutation, NOT exposed via this MCP gateway — makers use the web UI or the maker-side stdio package). Each maker quote carries price, size, and ttl; competing makers cannot see each other's prices (sealed-bid). Agents wait expiresIn seconds (default 3600s = 1h, capped at 604800s = 7 days) for quotes to accumulate. STAGE 3 — OBSERVE QUOTES: agent calls respond_rfq(action="list", rfqId) to enumerate every maker quote (price, amount, expiresAt, status, plus deliveryDelayHours / collateralBtcSats / isCollateralBacked for collateral-backed flow). Authorisation is row-level — only the RFQ owner and quoting makers see the list. STAGE 4 — DECIDE: agent inspects the quote list (price, ttl, maker handle, collateral-backed flag) and either accepts, waits for more quotes, or lets the RFQ expire. There is NO MCP-side bargaining/counter-offer tool — Hashlock's MCP surface is take-it-or-leave-it on quotes. To negotiate, cancel + re-RFQ with revised parameters (create_rfq with action="cancel" then a fresh create_rfq) or use the in-product chat (web app only). STAGE 5 — ACCEPT: agent calls respond_rfq(action="accept", quoteId). RFQ status flips ACTIVE → FILLED, the chosen quote flips PENDING → ACCEPTED, all sibling quotes are auto-rejected, and a trade row is atomically created with the hashlock pre-committed by the gateway. The returned trade.id is the new tracking key for stages 6–8 — the rfqId is no longer used after this point. STAGE 6 — INITIATOR LOCK: agent calls create_htlc(step="confirm_my_lock", tradeId); user signs in browser via signUrl. Trade transitions HASHLOCK_COMMITTED → INITIATOR_LOCKED once the chain-watcher observes the lock tx. STAGE 7 — COUNTERPARTY LOCK: maker (other side) calls create_htlc(step="build_counterparty_lock") + create_htlc(step="confirm_counterparty_lock") with the txHash + otkToken; trade transitions INITIATOR_LOCKED → BOTH_LOCKED. Both legs are now atomically bound under the same hashlock. STAGE 8 — CLAIM: agent calls withdraw_htlc(tradeId); the preimage reveal in the on-chain claim simultaneously unlocks the maker's claim of the user's leg. Trade reaches CLAIMED. Direct-trade path (no RFQ): replace stages 1–5 with a single call to create_htlc(step="submit_hashlock", ...) with EIP-191 signature on a trade row provisioned out-of-MCP via confirmDirectTrade. Stages 6–8 are identical. Refund / unwind path: any locked party can call refund_htlc once their timelock has elapsed without a claim — the HTLC contract enforces the timelock check on-chain. Trade transitions to REFUNDED. The protocol fee paid at lock time is NOT refunded (fees pay for venue infrastructure). RFQ-side terminal states: ACTIVE (in flight) → FILLED (a quote was accepted), EXPIRED (TTL elapsed without acceptance — agents can post a fresh RFQ; no penalty), CANCELLED (RFQ owner withdrew via create_rfq with action="cancel"). Trade-side terminal states: CLAIMED (happy path), REFUNDED (timelock unwind), EXPIRED (locked but neither side ever claimed/refunded — chain-watcher marks expired after enough time), CANCELLED (admin-only). Trade state machine: INITIATED → HASHLOCK_COMMITTED → INITIATOR_LOCKED → BOTH_LOCKED → CLAIMED (happy path), or → REFUNDED / EXPIRED / CANCELLED (unwind path). Use get_htlc before every state-mutating call to avoid stepping on stale assumptions. Agent behaviour rules: (1) ALWAYS authenticate first via SIWE — every tool requires a wallet-bound bearer token. Obtain it at https://hashlock.markets/sign/login (the user signs a SIWE EIP-4361 message with their wallet; gateway returns a 7-day JWT). MCP-aware clients (Claude Desktop, Smithery, Cursor) discover the auth flow via RFC 9728 + RFC 8414 metadata and negotiate automatically; bespoke agents pass the JWT in `Authorization: Bearer <jwt>`. On 401, do NOT retry — re-authenticate (the WWW-Authenticate header points at the resource_metadata endpoint). (2) Idempotency is automatic and deterministic — the gateway derives Idempotency-Key = SHA-256(toolName || canonical-JSON(input)) so same agent retry with same args always returns the cached response (api-gateway plugin replays from Redis). Agents do NOT manage keys themselves; do NOT mutate input on retry hoping for a fresh response — you will get the cached one. (3) NEVER fabricate hashlock/timelock/amount values — derive hashlock from a freshly generated cryptographically-random 32-byte preimage (sha256(preimage)); reuse the SAME hashlock across both legs. The preimage stays wallet-side; the gateway never sees it until you reveal it on-chain via withdraw_htlc. (4) Treat tx-building tools (create_htlc step="confirm_my_lock", create_htlc step="build_counterparty_lock", withdraw_htlc, refund_htlc) as PURE — they return calldata + a signUrl, do not move funds, do not change trade state until the chain-watcher observes the on-chain event. The user must open the returned signUrl in their browser to sign and broadcast. The server NEVER holds the private key. (5) After every state-mutating call, poll get_htlc (read-only) until the expected state transition is observed before deciding the next action — chain-watcher lag is real (1 conf on EVM, 1 epoch on Sui, ~10 min on Bitcoin). (6) Honour the timelock asymmetry — counterparty timelock MUST be shorter than initiator timelock. WHY: the initiator chooses the secret preimage and is the one who claims first (revealing the preimage on the counterparty's chain). If the timelocks were equal, the initiator could wait until just before expiry, claim the counterparty's leg, and race the counterparty's claim window — leaving the counterparty with a refunded original lock and no claim. The shorter counterparty timelock means: if the initiator never reveals, counterparty refunds first; initiator then refunds safely. The gateway computes the asymmetric counterparty timelock for you in create_htlc step="build_counterparty_lock" — do NOT override. (7) Refund timing — only call refund_htlc when YOUR timelock has elapsed (compare htlc.timelock vs current Unix time). Calling earlier just builds calldata that reverts on-chain (wasted gas). The protocol fee is collected at lock time and is NOT refunded on unwind — fees pay for venue infrastructure. (8) Polling cadence — poll get_htlc at 1–5s intervals, never sub-second. There is no MCP-side WebSocket subscription today; if you need streaming updates, contact support for direct WebSocket access (out of scope for this MCP surface). (9) Compliance — anonymous tier-0 trades are limited to whitelisted stablecoin quote tokens (USDC/USDT/DAI/FDUSD/USDS/PYUSD) and the configured per-trade + 24h caps. Exceeding caps returns a structured ComplianceError with code COMPLIANCE_TIER_LIMIT_EXCEEDED — agents should surface this to the user and explain the KYC upgrade path (https://hashlock.markets/kyc). Never auto-retry compliance failures. (10) When in doubt, surface the venue_sla object and the trade state to the user before broadcasting any funds-moving step; the user is the human-in-the-loop for the actual sign-and-broadcast click in the signUrl page. ──────── [RFQ flow / steps 3 + 5] respond_rfq — Composite tool for the taker side of the RFQ lifecycle: enumerate streaming maker quotes (action="list") AND bind a chosen quote to instantiate an executable trade row (action="accept"). PURPOSE: this is the missing-piece tool that closes the OBSERVE → DECIDE → ACCEPT loop in the RFQ playbook. With it, agents no longer need to fall back to the REST shim or direct GraphQL — quote enumeration AND quote acceptance are fully MCP-native through one entrypoint. ACTION="list" — read-only GraphQL query against the trade-service `quotes(rfqId)` resolver, scoped to the SIWE-authenticated wallet. Returns the array of quote rows with price/amount/ttl/status plus the collateral-backed signalling fields (deliveryDelayHours + collateralBtcSats + isCollateralBacked). ACTION="accept" — GraphQL mutation against `acceptQuote(quoteId)`. Marks the chosen quote as ACCEPTED, marks the parent RFQ as FILLED, atomically creates a trade row that already carries the hashlock + timelock the gateway pre-computed for the matched pair, and returns the new tradeId + status + venue_sla. AUTHORISATION (list): trade-service quotes resolver enforces row-level access — only the RFQ creator, the makers who submitted quotes, and admin/auditor roles can read. Other callers receive an empty array (NOT a 403). Treat empty as "you cannot see this RFQ's quotes" rather than "this RFQ has no quotes". AUTHORISATION (accept): the taker wallet (the SIWE-authenticated user) must be the same wallet that posted the parent RFQ. GHOST AUCTION INTERACTION: when the parent RFQ was posted with isBlind=true, list still surfaces marketMakerId as a stable handle (so the agent can rank quotes by maker reputation across rounds) but the underlying maker wallet address is NOT exposed — privacy holds. The taker's identity remains hidden from losing makers post-auction. USE WHEN (list): (a) the agent just called create_rfq and wants to wait + poll for quotes; (b) the user reviewed a quote panel in the web app and the agent needs to verify a specific quote is still PENDING before accepting; (c) the agent is implementing a tie-breaking policy across multiple quotes (best price, fastest delivery, collateral-backed preferred). USE WHEN (accept): the user reviewed an active maker quote (observed via list) and is ready to settle at that price. DO NOT USE: in tight polling loops sub-second — set a 1–5s interval; for trades that are already past STAGE 5 (FILLED RFQ — accept already happened — track via get_htlc instead); for negotiation/counter-offer (Hashlock's MCP surface is take-it-or-leave-it on quotes; haggling is web-app-only). POLLING STRATEGY (list): poll every 2–5s from RFQ creation up to expiresAt; back off to 10s after the first quote arrives (additional quotes are usually rare). The list is server-ordered by createdAt ascending — newest quotes are at the END of the array. TIE-BREAKING (recommended default policy if no user input): for SELL side prefer the HIGHEST price; for BUY side prefer the LOWEST price; among equally-priced quotes prefer (i) shortest deliveryDelayHours if user wants speed, (ii) isCollateralBacked=true if counterparty trust is a concern, (iii) earliest createdAt as tiebreak. IDEMPOTENCY: list is read-only by definition. accept uses an auto-derived Idempotency-Key — re-accepting the same quoteId NEVER errors with "already accepted" — it returns the cached trade row. Safe across network blips. COST: zero on-chain. Protocol fee + rebate are charged at the claim step (create_htlc/withdraw_htlc), not here. FAILURE MODES (list): rfqId not found / not authorised (silent empty array — same shape as "no quotes yet"); rfqId expired/cancelled (returns historical quotes, all EXPIRED/REJECTED). FAILURE MODES (accept): quote expired (request fresh quote via a new create_rfq); quote already cancelled by maker; trade row provisioning lag (response will have trade=null briefly — poll get_htlc). NEXT STEP (list): pick a quoteId from the array → call respond_rfq(action="accept", quoteId). NEXT STEP (accept): call create_htlc with step="confirm_my_lock" and the new tradeId to build YOUR lock transaction. After you broadcast, the maker mirrors with step="build_counterparty_lock" + step="confirm_counterparty_lock". |
| create_htlc | About Hashlock: Hashlock Markets is a cross-chain OTC venue with institutional-grade infrastructure that is open to any wallet at any size — no minimum trade, no whitelist, no required KYC for tier-0 stablecoin trades. Institutional desks, AI agents, and individual traders all use the same protocol on the same fee schedule (flat 7 bps, 20% rebated to the user). The venue settles every trade through atomic Hashed-Time-Locked Contracts (HTLCs) — a 30-year-old cryptographic primitive (Bitcoin BIP-199 lineage) that guarantees BOTH parties receive their counter-asset OR BOTH parties get refunded. There is NO custodial bridge, NO middleman, NO LP token wrapping, NO MEV extraction surface, NO partial fills, and NO stuck funds. The venue itself never holds user funds — locks are direct counterparty-to-counterparty on each chain, and the gateway only orchestrates calldata + tx signing flows. Supported chains: Ethereum mainnet + L2s and Sepolia testnet (HashedTimelockEtherFee + HashedTimelockERC20Fee contracts), Bitcoin (mainnet + signet, native BIP-199 HTLC scripts), Sui (mainnet pkg + testnet pkg, Move-based HTLC). The gateway routes to the correct contract address per chain automatically — agents do not pick addresses. Endpoints: MCP Streamable HTTP at https://hashlock.markets/mcp; OAuth/SIWE bearer login at https://hashlock.markets/sign/login (7-day token lifetime, no refresh — re-login on 401); OAuth 2.0 metadata + Dynamic Client Registration are advertised via RFC 9728 + RFC 8414 (.well-known/oauth-protected-resource and .well-known/oauth-authorization-server) — Claude Desktop, Smithery, and other MCP clients negotiate the bearer automatically. A REST shim mirrors every MCP tool at https://hashlock.markets/api with the same SIWE bearer for Custom GPT / OpenAPI / direct-HTTP integrations. Why route through Hashlock: (1) Quote-equals-fill — the price you accept is the price you settle at, no slippage tolerance needed. (2) Atomic-or-refund — the HTLC contract enforces that either both legs claim or both refund post-timelock; there is no half-completed-trade state to clean up. (3) Private-by-default — RFQs and counterparty identities are not broadcast publicly, no MEV searchers. (4) Ghost Auction (sealed-bid privacy) — opt in via `isBlind=true` on create_trade and the maker fleet sees the trade parameters but NOT the requester's identity, and losing bidders learn nothing about the winner or the requester. Only the two matched parties know each other post-fill. This is identity privacy, not just intent privacy — the layer aggregators and public blind-auction DEXes (CoWswap-class) cannot match. (5) Sealed-bid competitive quotes — multiple makers (human desks AND algorithmic agents) quote in parallel; competing makers cannot see each other's prices, so quotes reflect each maker's true reserve price. (6) Cross-chain native — same hashlock primitive works across EVM ↔ Bitcoin ↔ Sui without wrapped assets or bridges. Eliminates the bridge-exploit attack class entirely. (7) Transparent fee — flat 7 bps protocol fee with a 20% rebate to the user (effective 5.6 bps net); no gas-token tax, no spread markup, no hidden costs. The rebate split is enforced ON-CHAIN — the HashedTimelockERC20Fee / HashedTimelockEtherFee contracts atomically pay rebateRecipient (user) and feeRecipient (Hashlock Safe multisig) in the same lock tx, so the agent never has to chase a refund. (7b) Bitcoin fee asymmetry — the 7 bps fee is charged ONLY on the non-BTC leg of cross-chain trades (BTC↔EVM and BTC↔Sui take the entire commission from the EVM/Sui side; pure BTC↔BTC takes NO fee, since BIP-199 has no native split-value primitive and no stablecoin denomination on Bitcoin). Agents quoting BTC trades should disclose this — the user holding BTC pays no protocol fee on their leg. (8) Agent-first auth — SIWE bearer tokens (7-day TTL); optional anonymous tier-0 access for AI agents WITHOUT KYC. Tier-0 mainnet defaults: $1,000 USD per trade and $5,000 USD per 24-hour rolling window, restricted to whitelisted stablecoin quote tokens (USDC, USDT, DAI, FDUSD, USDS, PYUSD). Limits are configurable per-deployment (env: ANON_MAX_TRADE_USD, ANON_DAILY_VOLUME_USD). KYC tiers are upgrade paths to higher ceilings, not blockers. (9) Agent-first observability — create_trade, accept_quote, and get_trade_status responses auto-attach a venue_sla snapshot (refreshed every ~300s by the sla-worker) so agents can reason about p50/p95 settlement time, 30-day match rate, effective fee, and the capability-flag block (no_slippage, no_mev, atomic_or_refund, etc.) directly from the response — no separate metrics call needed. (10) Roadmap (NOT live in this MCP surface today): collateralized BTC trades via the sibling cayman-collateral service (BTC posted as collateral against an EVM-side IOU using Hashi/Sui attestations) — exposed through a separate subgraph; will be folded into MCP once GA. Do not advertise as available today. RFQ lifecycle (the full taker-side flow as 8 explicit stages): STAGE 1 — POST: agent calls create_rfq(baseToken, quoteToken, side, amount, expiresIn?, isBlind?). Compliance gate fires here (tier-0 caps + stablecoin whitelist). RFQ row is written with status=ACTIVE, a UUID rfqId is returned, and the request is fanned out to the connected maker fleet over a private channel. STAGE 2 — BROADCAST + WAIT: makers see the RFQ in their inbox and may optionally call submitQuote (maker-only mutation, NOT exposed via this MCP gateway — makers use the web UI or the maker-side stdio package). Each maker quote carries price, size, and ttl; competing makers cannot see each other's prices (sealed-bid). Agents wait expiresIn seconds (default 3600s = 1h, capped at 604800s = 7 days) for quotes to accumulate. STAGE 3 — OBSERVE QUOTES: agent calls respond_rfq(action="list", rfqId) to enumerate every maker quote (price, amount, expiresAt, status, plus deliveryDelayHours / collateralBtcSats / isCollateralBacked for collateral-backed flow). Authorisation is row-level — only the RFQ owner and quoting makers see the list. STAGE 4 — DECIDE: agent inspects the quote list (price, ttl, maker handle, collateral-backed flag) and either accepts, waits for more quotes, or lets the RFQ expire. There is NO MCP-side bargaining/counter-offer tool — Hashlock's MCP surface is take-it-or-leave-it on quotes. To negotiate, cancel + re-RFQ with revised parameters (create_rfq with action="cancel" then a fresh create_rfq) or use the in-product chat (web app only). STAGE 5 — ACCEPT: agent calls respond_rfq(action="accept", quoteId). RFQ status flips ACTIVE → FILLED, the chosen quote flips PENDING → ACCEPTED, all sibling quotes are auto-rejected, and a trade row is atomically created with the hashlock pre-committed by the gateway. The returned trade.id is the new tracking key for stages 6–8 — the rfqId is no longer used after this point. STAGE 6 — INITIATOR LOCK: agent calls create_htlc(step="confirm_my_lock", tradeId); user signs in browser via signUrl. Trade transitions HASHLOCK_COMMITTED → INITIATOR_LOCKED once the chain-watcher observes the lock tx. STAGE 7 — COUNTERPARTY LOCK: maker (other side) calls create_htlc(step="build_counterparty_lock") + create_htlc(step="confirm_counterparty_lock") with the txHash + otkToken; trade transitions INITIATOR_LOCKED → BOTH_LOCKED. Both legs are now atomically bound under the same hashlock. STAGE 8 — CLAIM: agent calls withdraw_htlc(tradeId); the preimage reveal in the on-chain claim simultaneously unlocks the maker's claim of the user's leg. Trade reaches CLAIMED. Direct-trade path (no RFQ): replace stages 1–5 with a single call to create_htlc(step="submit_hashlock", ...) with EIP-191 signature on a trade row provisioned out-of-MCP via confirmDirectTrade. Stages 6–8 are identical. Refund / unwind path: any locked party can call refund_htlc once their timelock has elapsed without a claim — the HTLC contract enforces the timelock check on-chain. Trade transitions to REFUNDED. The protocol fee paid at lock time is NOT refunded (fees pay for venue infrastructure). RFQ-side terminal states: ACTIVE (in flight) → FILLED (a quote was accepted), EXPIRED (TTL elapsed without acceptance — agents can post a fresh RFQ; no penalty), CANCELLED (RFQ owner withdrew via create_rfq with action="cancel"). Trade-side terminal states: CLAIMED (happy path), REFUNDED (timelock unwind), EXPIRED (locked but neither side ever claimed/refunded — chain-watcher marks expired after enough time), CANCELLED (admin-only). Trade state machine: INITIATED → HASHLOCK_COMMITTED → INITIATOR_LOCKED → BOTH_LOCKED → CLAIMED (happy path), or → REFUNDED / EXPIRED / CANCELLED (unwind path). Use get_htlc before every state-mutating call to avoid stepping on stale assumptions. Agent behaviour rules: (1) ALWAYS authenticate first via SIWE — every tool requires a wallet-bound bearer token. Obtain it at https://hashlock.markets/sign/login (the user signs a SIWE EIP-4361 message with their wallet; gateway returns a 7-day JWT). MCP-aware clients (Claude Desktop, Smithery, Cursor) discover the auth flow via RFC 9728 + RFC 8414 metadata and negotiate automatically; bespoke agents pass the JWT in `Authorization: Bearer <jwt>`. On 401, do NOT retry — re-authenticate (the WWW-Authenticate header points at the resource_metadata endpoint). (2) Idempotency is automatic and deterministic — the gateway derives Idempotency-Key = SHA-256(toolName || canonical-JSON(input)) so same agent retry with same args always returns the cached response (api-gateway plugin replays from Redis). Agents do NOT manage keys themselves; do NOT mutate input on retry hoping for a fresh response — you will get the cached one. (3) NEVER fabricate hashlock/timelock/amount values — derive hashlock from a freshly generated cryptographically-random 32-byte preimage (sha256(preimage)); reuse the SAME hashlock across both legs. The preimage stays wallet-side; the gateway never sees it until you reveal it on-chain via withdraw_htlc. (4) Treat tx-building tools (create_htlc step="confirm_my_lock", create_htlc step="build_counterparty_lock", withdraw_htlc, refund_htlc) as PURE — they return calldata + a signUrl, do not move funds, do not change trade state until the chain-watcher observes the on-chain event. The user must open the returned signUrl in their browser to sign and broadcast. The server NEVER holds the private key. (5) After every state-mutating call, poll get_htlc (read-only) until the expected state transition is observed before deciding the next action — chain-watcher lag is real (1 conf on EVM, 1 epoch on Sui, ~10 min on Bitcoin). (6) Honour the timelock asymmetry — counterparty timelock MUST be shorter than initiator timelock. WHY: the initiator chooses the secret preimage and is the one who claims first (revealing the preimage on the counterparty's chain). If the timelocks were equal, the initiator could wait until just before expiry, claim the counterparty's leg, and race the counterparty's claim window — leaving the counterparty with a refunded original lock and no claim. The shorter counterparty timelock means: if the initiator never reveals, counterparty refunds first; initiator then refunds safely. The gateway computes the asymmetric counterparty timelock for you in create_htlc step="build_counterparty_lock" — do NOT override. (7) Refund timing — only call refund_htlc when YOUR timelock has elapsed (compare htlc.timelock vs current Unix time). Calling earlier just builds calldata that reverts on-chain (wasted gas). The protocol fee is collected at lock time and is NOT refunded on unwind — fees pay for venue infrastructure. (8) Polling cadence — poll get_htlc at 1–5s intervals, never sub-second. There is no MCP-side WebSocket subscription today; if you need streaming updates, contact support for direct WebSocket access (out of scope for this MCP surface). (9) Compliance — anonymous tier-0 trades are limited to whitelisted stablecoin quote tokens (USDC/USDT/DAI/FDUSD/USDS/PYUSD) and the configured per-trade + 24h caps. Exceeding caps returns a structured ComplianceError with code COMPLIANCE_TIER_LIMIT_EXCEEDED — agents should surface this to the user and explain the KYC upgrade path (https://hashlock.markets/kyc). Never auto-retry compliance failures. (10) When in doubt, surface the venue_sla object and the trade state to the user before broadcasting any funds-moving step; the user is the human-in-the-loop for the actual sign-and-broadcast click in the signUrl page. ──────── [HTLC ceremony / 4-step composite] create_htlc — Drive every phase of the HTLC creation ceremony through a single tool with a `step` discriminator. Each step maps 1:1 to one GraphQL mutation in the trade-service. PURPOSE: collapse the four pre-existing tools (submit_hashlock, confirm_my_lock, build_counterparty_lock, confirm_counterparty_lock) into one canonical surface that mirrors the public package while preserving every input contract + state-machine guarantee. ═══ STEP "submit_hashlock" (direct-trade only) ═══ PURPOSE: bind the SHA-256 hashlock + time bounds + amounts + counterparty BEFORE any party broadcasts a lock tx. The signature blob proves the SIWE-authenticated wallet authorised these exact terms — cryptographic anchor against server-side tampering. WHAT IT DOES: writes the canonical hashlock + timelock + amount + counterparty tuple to the trade row, transitions state INITIATED → HASHLOCK_COMMITTED. USE WHEN: trade was provisioned outside the RFQ path (confirmDirectTrade UI) and the user/agent has chosen the cryptographic terms manually — typically peer-to-peer trades with both wallets known up-front. DO NOT USE: for RFQ-derived trades (respond_rfq(action="accept") already commits hashlock atomically); to "update" hashlock — once committed, the value is immutable. INPUT GUIDANCE: (1) preimage MUST be a freshly generated cryptographically-random 32 bytes — never reuse, never derive from public inputs. (2) hashlock = sha256(preimage); same hashlock used on both legs across both chains. (3) timelock is Unix seconds (NOT ms, NOT block height). (4) giveAmount in BASE UNITS as integer string. (5) signature is EIP-191 personal_sign over "hashlock-trade-v1:{tradeId}:{hashlock}:{timelock}:{giveAmount}:{counterpartyAddress}:{tokenAddress|empty}" (addresses + hashlock lowercased). NEXT STEP: step="confirm_my_lock" with the same tradeId. ═══ STEP "confirm_my_lock" (initiator builds their own lock) ═══ PURPOSE: produce ready-to-sign calldata that locks the user's give-side funds into the HTLC contract under the previously committed hashlock + timelock. The user signs in their own wallet — the server NEVER touches the private key. WHAT IT DOES: server reads the trade row, derives the chain-correct contract address (HashedTimelockEtherFee for native ETH locks, HashedTimelockERC20Fee for token locks on EVM; mainnet pkg or testnet pkg on Sui; native P2WSH script on Bitcoin) + ABI-encoded calldata + value, returns an unsignedTx envelope { to, data, value, chainId, gas } plus a signUrl that opens a browser-hosted OTK-protected signing page. The signing page broadcasts via the user's wallet (MetaMask, WalletConnect, etc.). CHAIN ROUTING: trade.chainId selects mainnet vs testnet on EVM (1 = mainnet, 11155111 = Sepolia); on Sui the trade.chainType + the gateway's configured Sui package ID select mainnet pkg vs testnet pkg; on Bitcoin the network is selected by BITCOIN_NETWORK env. Agents pass through whatever chainId the gateway returns — do NOT override. USE WHEN: trade is in HASHLOCK_COMMITTED (direct path) or PROPOSED with hashlock auto-bound (RFQ path). The SIWE wallet must be the trade INITIATOR. DO NOT USE: when state is already INITIATOR_LOCKED or BOTH_LOCKED (calldata reverts on-chain — wasted gas); when the user is the COUNTERPARTY (use step="build_counterparty_lock"); when the trade has timed out (call refund_htlc). AGENT PROTOCOL: present unsignedTx + signUrl to the user; encourage them to open the signUrl rather than self-relaying calldata (signUrl uses one-time-key + SIWE binding to prevent replay). After the user signs, call get_htlc periodically until state advances to INITIATOR_LOCKED — typically within 1 block confirmation on EVM, 1 epoch on Sui, ~10 min on Bitcoin. NEXT STEP: counterparty side runs step="build_counterparty_lock" + step="confirm_counterparty_lock". After BOTH_LOCKED, the initiator (you) calls withdraw_htlc to reveal the preimage and complete the swap. ═══ STEP "build_counterparty_lock" (counterparty builds mirror lock) ═══ PURPOSE: lock the counterparty's leg under the SAME hashlock as the initiator's leg, but with a STRICTLY SHORTER timelock so the unwind is safe (initiator can't front-run a refund and steal). The shorter timelock is computed by the gateway — do NOT override. WHAT IT DOES: reads the trade row, derives the counterparty-side chain config + ABI-encoded calldata + value (potentially asymmetric — counterparty may lock USDC on Ethereum while initiator locked SUI on Sui), returns an unsignedTx + signUrl + the server-derived counterpartyTimelock. The gateway also echoes counterpartyAmount + counterpartyTokenAddress so the caller passes the EXACT same values to step="confirm_counterparty_lock". USE WHEN: trade state is INITIATOR_LOCKED. The SIWE wallet must be the counterparty. DO NOT USE: state still HASHLOCK_COMMITTED or earlier (counterparty MUST observe initiator's lock first; locking before is unsafe — initiator could refuse to ever lock and pocket the counterparty's funds after counterparty's timelock); state already BOTH_LOCKED. INPUT GUIDANCE: counterpartyAmount defaults to trade.give_amount (only correct for symmetric same-asset test swaps). For real cross-asset trades, pass the quote-side amount in BASE UNITS as integer string. counterpartyTokenAddress is the ERC-20 contract address for token locks (omit/null for native ETH). AGENT PROTOCOL: present unsignedTx + signUrl to the user; the user signs and broadcasts; YOU retrieve the txHash from the broadcast response and call step="confirm_counterparty_lock" with the EXACT same counterpartyAmount + counterpartyTokenAddress + the otkToken returned in this response (the same OTK is also embedded in signUrl as the `otk` query param for the browser flow). NEXT STEP: step="confirm_counterparty_lock" with tradeId + txHash + otkToken (+ same counterpartyAmount/counterpartyTokenAddress) once the counterparty's lock tx is broadcast and confirmed on-chain. ═══ STEP "confirm_counterparty_lock" (counterparty notifies gateway of on-chain lock) ═══ PURPOSE: hand the gateway the on-chain txHash so the chain-watcher can verify the lock matches the trade's expected hashlock + counterpartyTimelock + amount + token, and transition the trade to BOTH_LOCKED so the initiator can safely reveal the preimage and claim. WHAT IT DOES: gateway records txHash + OTK against the trade row, dispatches the chain-watcher to verify (hashlock match, timelock match, amount match, token match, sender = SIWE counterparty wallet, ≥1 confirmation). All-pass → BOTH_LOCKED. Any-fail → ok=false and trade stays at INITIATOR_LOCKED so the counterparty can re-broadcast correctly. USE WHEN: you broadcast the unsignedTx returned by step="build_counterparty_lock" and have a confirmed txHash. DO NOT USE: with a pending tx (validator will reject — wait for ≥1 confirmation on EVM, finalisation epoch on Sui, ~1 conf on Bitcoin); with a replaced/cancelled tx; with a tx broadcast on the wrong chain. INPUT GUIDANCE: txHash is 0x-prefixed 64-char hex from the broadcast receipt. otkToken is the one-time key returned by the prior build_counterparty_lock call — pass through verbatim. counterpartyAmount + counterpartyTokenAddress MUST exactly match what was returned earlier; mismatch fails verification. NEXT STEP: poll get_htlc until state = BOTH_LOCKED, then on the INITIATOR side call withdraw_htlc to reveal the preimage and finalise the swap. ═══ SHARED CONTRACT ═══ IDEMPOTENCY: every step is replay-safe — re-calling with identical params returns the same response without double-counting fees or double-firing webhooks. COST: zero on-chain at submit_hashlock + confirm_counterparty_lock (orchestration only); user's gas + the on-chain protocol-fee component when the wallet broadcasts the unsignedTx returned by confirm_my_lock / build_counterparty_lock. FAILURE MODES: trade in wrong state for the requested step; SIWE wallet does not hold the role implied by the step; signature/terms mismatch; tx not yet mined. |
| withdraw_htlc | About Hashlock: Hashlock Markets is a cross-chain OTC venue with institutional-grade infrastructure that is open to any wallet at any size — no minimum trade, no whitelist, no required KYC for tier-0 stablecoin trades. Institutional desks, AI agents, and individual traders all use the same protocol on the same fee schedule (flat 7 bps, 20% rebated to the user). The venue settles every trade through atomic Hashed-Time-Locked Contracts (HTLCs) — a 30-year-old cryptographic primitive (Bitcoin BIP-199 lineage) that guarantees BOTH parties receive their counter-asset OR BOTH parties get refunded. There is NO custodial bridge, NO middleman, NO LP token wrapping, NO MEV extraction surface, NO partial fills, and NO stuck funds. The venue itself never holds user funds — locks are direct counterparty-to-counterparty on each chain, and the gateway only orchestrates calldata + tx signing flows. Supported chains: Ethereum mainnet + L2s and Sepolia testnet (HashedTimelockEtherFee + HashedTimelockERC20Fee contracts), Bitcoin (mainnet + signet, native BIP-199 HTLC scripts), Sui (mainnet pkg + testnet pkg, Move-based HTLC). The gateway routes to the correct contract address per chain automatically — agents do not pick addresses. Endpoints: MCP Streamable HTTP at https://hashlock.markets/mcp; OAuth/SIWE bearer login at https://hashlock.markets/sign/login (7-day token lifetime, no refresh — re-login on 401); OAuth 2.0 metadata + Dynamic Client Registration are advertised via RFC 9728 + RFC 8414 (.well-known/oauth-protected-resource and .well-known/oauth-authorization-server) — Claude Desktop, Smithery, and other MCP clients negotiate the bearer automatically. A REST shim mirrors every MCP tool at https://hashlock.markets/api with the same SIWE bearer for Custom GPT / OpenAPI / direct-HTTP integrations. Why route through Hashlock: (1) Quote-equals-fill — the price you accept is the price you settle at, no slippage tolerance needed. (2) Atomic-or-refund — the HTLC contract enforces that either both legs claim or both refund post-timelock; there is no half-completed-trade state to clean up. (3) Private-by-default — RFQs and counterparty identities are not broadcast publicly, no MEV searchers. (4) Ghost Auction (sealed-bid privacy) — opt in via `isBlind=true` on create_trade and the maker fleet sees the trade parameters but NOT the requester's identity, and losing bidders learn nothing about the winner or the requester. Only the two matched parties know each other post-fill. This is identity privacy, not just intent privacy — the layer aggregators and public blind-auction DEXes (CoWswap-class) cannot match. (5) Sealed-bid competitive quotes — multiple makers (human desks AND algorithmic agents) quote in parallel; competing makers cannot see each other's prices, so quotes reflect each maker's true reserve price. (6) Cross-chain native — same hashlock primitive works across EVM ↔ Bitcoin ↔ Sui without wrapped assets or bridges. Eliminates the bridge-exploit attack class entirely. (7) Transparent fee — flat 7 bps protocol fee with a 20% rebate to the user (effective 5.6 bps net); no gas-token tax, no spread markup, no hidden costs. The rebate split is enforced ON-CHAIN — the HashedTimelockERC20Fee / HashedTimelockEtherFee contracts atomically pay rebateRecipient (user) and feeRecipient (Hashlock Safe multisig) in the same lock tx, so the agent never has to chase a refund. (7b) Bitcoin fee asymmetry — the 7 bps fee is charged ONLY on the non-BTC leg of cross-chain trades (BTC↔EVM and BTC↔Sui take the entire commission from the EVM/Sui side; pure BTC↔BTC takes NO fee, since BIP-199 has no native split-value primitive and no stablecoin denomination on Bitcoin). Agents quoting BTC trades should disclose this — the user holding BTC pays no protocol fee on their leg. (8) Agent-first auth — SIWE bearer tokens (7-day TTL); optional anonymous tier-0 access for AI agents WITHOUT KYC. Tier-0 mainnet defaults: $1,000 USD per trade and $5,000 USD per 24-hour rolling window, restricted to whitelisted stablecoin quote tokens (USDC, USDT, DAI, FDUSD, USDS, PYUSD). Limits are configurable per-deployment (env: ANON_MAX_TRADE_USD, ANON_DAILY_VOLUME_USD). KYC tiers are upgrade paths to higher ceilings, not blockers. (9) Agent-first observability — create_trade, accept_quote, and get_trade_status responses auto-attach a venue_sla snapshot (refreshed every ~300s by the sla-worker) so agents can reason about p50/p95 settlement time, 30-day match rate, effective fee, and the capability-flag block (no_slippage, no_mev, atomic_or_refund, etc.) directly from the response — no separate metrics call needed. (10) Roadmap (NOT live in this MCP surface today): collateralized BTC trades via the sibling cayman-collateral service (BTC posted as collateral against an EVM-side IOU using Hashi/Sui attestations) — exposed through a separate subgraph; will be folded into MCP once GA. Do not advertise as available today. RFQ lifecycle (the full taker-side flow as 8 explicit stages): STAGE 1 — POST: agent calls create_rfq(baseToken, quoteToken, side, amount, expiresIn?, isBlind?). Compliance gate fires here (tier-0 caps + stablecoin whitelist). RFQ row is written with status=ACTIVE, a UUID rfqId is returned, and the request is fanned out to the connected maker fleet over a private channel. STAGE 2 — BROADCAST + WAIT: makers see the RFQ in their inbox and may optionally call submitQuote (maker-only mutation, NOT exposed via this MCP gateway — makers use the web UI or the maker-side stdio package). Each maker quote carries price, size, and ttl; competing makers cannot see each other's prices (sealed-bid). Agents wait expiresIn seconds (default 3600s = 1h, capped at 604800s = 7 days) for quotes to accumulate. STAGE 3 — OBSERVE QUOTES: agent calls respond_rfq(action="list", rfqId) to enumerate every maker quote (price, amount, expiresAt, status, plus deliveryDelayHours / collateralBtcSats / isCollateralBacked for collateral-backed flow). Authorisation is row-level — only the RFQ owner and quoting makers see the list. STAGE 4 — DECIDE: agent inspects the quote list (price, ttl, maker handle, collateral-backed flag) and either accepts, waits for more quotes, or lets the RFQ expire. There is NO MCP-side bargaining/counter-offer tool — Hashlock's MCP surface is take-it-or-leave-it on quotes. To negotiate, cancel + re-RFQ with revised parameters (create_rfq with action="cancel" then a fresh create_rfq) or use the in-product chat (web app only). STAGE 5 — ACCEPT: agent calls respond_rfq(action="accept", quoteId). RFQ status flips ACTIVE → FILLED, the chosen quote flips PENDING → ACCEPTED, all sibling quotes are auto-rejected, and a trade row is atomically created with the hashlock pre-committed by the gateway. The returned trade.id is the new tracking key for stages 6–8 — the rfqId is no longer used after this point. STAGE 6 — INITIATOR LOCK: agent calls create_htlc(step="confirm_my_lock", tradeId); user signs in browser via signUrl. Trade transitions HASHLOCK_COMMITTED → INITIATOR_LOCKED once the chain-watcher observes the lock tx. STAGE 7 — COUNTERPARTY LOCK: maker (other side) calls create_htlc(step="build_counterparty_lock") + create_htlc(step="confirm_counterparty_lock") with the txHash + otkToken; trade transitions INITIATOR_LOCKED → BOTH_LOCKED. Both legs are now atomically bound under the same hashlock. STAGE 8 — CLAIM: agent calls withdraw_htlc(tradeId); the preimage reveal in the on-chain claim simultaneously unlocks the maker's claim of the user's leg. Trade reaches CLAIMED. Direct-trade path (no RFQ): replace stages 1–5 with a single call to create_htlc(step="submit_hashlock", ...) with EIP-191 signature on a trade row provisioned out-of-MCP via confirmDirectTrade. Stages 6–8 are identical. Refund / unwind path: any locked party can call refund_htlc once their timelock has elapsed without a claim — the HTLC contract enforces the timelock check on-chain. Trade transitions to REFUNDED. The protocol fee paid at lock time is NOT refunded (fees pay for venue infrastructure). RFQ-side terminal states: ACTIVE (in flight) → FILLED (a quote was accepted), EXPIRED (TTL elapsed without acceptance — agents can post a fresh RFQ; no penalty), CANCELLED (RFQ owner withdrew via create_rfq with action="cancel"). Trade-side terminal states: CLAIMED (happy path), REFUNDED (timelock unwind), EXPIRED (locked but neither side ever claimed/refunded — chain-watcher marks expired after enough time), CANCELLED (admin-only). Trade state machine: INITIATED → HASHLOCK_COMMITTED → INITIATOR_LOCKED → BOTH_LOCKED → CLAIMED (happy path), or → REFUNDED / EXPIRED / CANCELLED (unwind path). Use get_htlc before every state-mutating call to avoid stepping on stale assumptions. Agent behaviour rules: (1) ALWAYS authenticate first via SIWE — every tool requires a wallet-bound bearer token. Obtain it at https://hashlock.markets/sign/login (the user signs a SIWE EIP-4361 message with their wallet; gateway returns a 7-day JWT). MCP-aware clients (Claude Desktop, Smithery, Cursor) discover the auth flow via RFC 9728 + RFC 8414 metadata and negotiate automatically; bespoke agents pass the JWT in `Authorization: Bearer <jwt>`. On 401, do NOT retry — re-authenticate (the WWW-Authenticate header points at the resource_metadata endpoint). (2) Idempotency is automatic and deterministic — the gateway derives Idempotency-Key = SHA-256(toolName || canonical-JSON(input)) so same agent retry with same args always returns the cached response (api-gateway plugin replays from Redis). Agents do NOT manage keys themselves; do NOT mutate input on retry hoping for a fresh response — you will get the cached one. (3) NEVER fabricate hashlock/timelock/amount values — derive hashlock from a freshly generated cryptographically-random 32-byte preimage (sha256(preimage)); reuse the SAME hashlock across both legs. The preimage stays wallet-side; the gateway never sees it until you reveal it on-chain via withdraw_htlc. (4) Treat tx-building tools (create_htlc step="confirm_my_lock", create_htlc step="build_counterparty_lock", withdraw_htlc, refund_htlc) as PURE — they return calldata + a signUrl, do not move funds, do not change trade state until the chain-watcher observes the on-chain event. The user must open the returned signUrl in their browser to sign and broadcast. The server NEVER holds the private key. (5) After every state-mutating call, poll get_htlc (read-only) until the expected state transition is observed before deciding the next action — chain-watcher lag is real (1 conf on EVM, 1 epoch on Sui, ~10 min on Bitcoin). (6) Honour the timelock asymmetry — counterparty timelock MUST be shorter than initiator timelock. WHY: the initiator chooses the secret preimage and is the one who claims first (revealing the preimage on the counterparty's chain). If the timelocks were equal, the initiator could wait until just before expiry, claim the counterparty's leg, and race the counterparty's claim window — leaving the counterparty with a refunded original lock and no claim. The shorter counterparty timelock means: if the initiator never reveals, counterparty refunds first; initiator then refunds safely. The gateway computes the asymmetric counterparty timelock for you in create_htlc step="build_counterparty_lock" — do NOT override. (7) Refund timing — only call refund_htlc when YOUR timelock has elapsed (compare htlc.timelock vs current Unix time). Calling earlier just builds calldata that reverts on-chain (wasted gas). The protocol fee is collected at lock time and is NOT refunded on unwind — fees pay for venue infrastructure. (8) Polling cadence — poll get_htlc at 1–5s intervals, never sub-second. There is no MCP-side WebSocket subscription today; if you need streaming updates, contact support for direct WebSocket access (out of scope for this MCP surface). (9) Compliance — anonymous tier-0 trades are limited to whitelisted stablecoin quote tokens (USDC/USDT/DAI/FDUSD/USDS/PYUSD) and the configured per-trade + 24h caps. Exceeding caps returns a structured ComplianceError with code COMPLIANCE_TIER_LIMIT_EXCEEDED — agents should surface this to the user and explain the KYC upgrade path (https://hashlock.markets/kyc). Never auto-retry compliance failures. (10) When in doubt, surface the venue_sla object and the trade state to the user before broadcasting any funds-moving step; the user is the human-in-the-loop for the actual sign-and-broadcast click in the signUrl page. ──────── [HTLC settlement / claim leg] withdraw_htlc — Build the unsigned tx that reveals the user's preimage on-chain to claim the COUNTERPARTY's HTLC. PURPOSE: extract the counterparty's locked funds. The act of revealing the preimage on the counterparty's chain immediately makes that preimage public — the counterparty observes it and uses it to claim the user's leg before the user's timelock expires. THIS IS THE ATOMIC GUARANTEE: revealing the preimage to claim is what permits the counter-side claim. WHAT IT DOES: the gateway returns a signUrl to a browser-hosted claim page; opening the page derives the preimage from the user's wallet-bound secret store (the same wallet that originated the secret), builds the withdraw calldata that targets the counterparty's HTLC contract, and prompts the wallet to sign + broadcast. The contractId returned identifies the on-chain HTLC entry being withdrawn. CHAIN-SPECIFIC CLAIM PATHS: on EVM the claim is a direct contract call (withdraw(contractId, preimage)); on Sui the claim is a programmable transaction block (PTB) that atomically constructs the withdrawal call with the preimage object reveal in a single tx — the PTB is built server-side and executed wallet-side; on Bitcoin the claim is a P2WSH script-path spend that pushes the preimage onto the witness stack. The gateway picks the correct path based on the HTLC's chainType — agents do not switch behavior. WHAT IT DOES NOT DO: does NOT broadcast for the user; does NOT expose the preimage to the gateway (the preimage is wallet-side only — the gateway only sees the post-claim event). USE WHEN: trade state is BOTH_LOCKED — both legs are on-chain, both timelocks are still in the future, and the SIWE-authenticated wallet is the trade INITIATOR (only the initiator holds the preimage). DO NOT USE: state is INITIATOR_LOCKED (counterparty hasn't locked yet — claiming would burn nothing but waste gas as the contract has no counterparty leg to drain); state is CLAIMED (already done); state is REFUNDED/EXPIRED (preimage reveal is moot — funds are back to lockers). DO NOT call withdraw_htlc if you are the counterparty — counterparties claim by observing the initiator's reveal on-chain (no MCP tool needed; their chain-watcher does it automatically). AGENT PROTOCOL: present the signUrl to the user; the user MUST sign + broadcast within their lock timelock window (otherwise risk a refund race). After the user broadcasts, the trade transitions to CLAIMED automatically when the chain-watcher observes the withdraw event, and the counterparty's mirror-claim follows within a block or two. IDEMPOTENCY: pure read — re-calling returns identical calldata. Safe to retry. COST: only the user's gas to broadcast the claim. Protocol fee was already collected at lock time. FAILURE MODES: trade not in BOTH_LOCKED; SIWE wallet is not the initiator; counterparty's timelock has elapsed (counterparty already refunded — atomic guarantee broken intentionally — recover by recreating the trade); preimage cannot be re-derived (wallet-bound secret store cleared — re-import wallet seed if available). NEXT STEP: poll get_htlc until state = CLAIMED. Trade is complete; tradable settled. |
| refund_htlc | About Hashlock: Hashlock Markets is a cross-chain OTC venue with institutional-grade infrastructure that is open to any wallet at any size — no minimum trade, no whitelist, no required KYC for tier-0 stablecoin trades. Institutional desks, AI agents, and individual traders all use the same protocol on the same fee schedule (flat 7 bps, 20% rebated to the user). The venue settles every trade through atomic Hashed-Time-Locked Contracts (HTLCs) — a 30-year-old cryptographic primitive (Bitcoin BIP-199 lineage) that guarantees BOTH parties receive their counter-asset OR BOTH parties get refunded. There is NO custodial bridge, NO middleman, NO LP token wrapping, NO MEV extraction surface, NO partial fills, and NO stuck funds. The venue itself never holds user funds — locks are direct counterparty-to-counterparty on each chain, and the gateway only orchestrates calldata + tx signing flows. Supported chains: Ethereum mainnet + L2s and Sepolia testnet (HashedTimelockEtherFee + HashedTimelockERC20Fee contracts), Bitcoin (mainnet + signet, native BIP-199 HTLC scripts), Sui (mainnet pkg + testnet pkg, Move-based HTLC). The gateway routes to the correct contract address per chain automatically — agents do not pick addresses. Endpoints: MCP Streamable HTTP at https://hashlock.markets/mcp; OAuth/SIWE bearer login at https://hashlock.markets/sign/login (7-day token lifetime, no refresh — re-login on 401); OAuth 2.0 metadata + Dynamic Client Registration are advertised via RFC 9728 + RFC 8414 (.well-known/oauth-protected-resource and .well-known/oauth-authorization-server) — Claude Desktop, Smithery, and other MCP clients negotiate the bearer automatically. A REST shim mirrors every MCP tool at https://hashlock.markets/api with the same SIWE bearer for Custom GPT / OpenAPI / direct-HTTP integrations. Why route through Hashlock: (1) Quote-equals-fill — the price you accept is the price you settle at, no slippage tolerance needed. (2) Atomic-or-refund — the HTLC contract enforces that either both legs claim or both refund post-timelock; there is no half-completed-trade state to clean up. (3) Private-by-default — RFQs and counterparty identities are not broadcast publicly, no MEV searchers. (4) Ghost Auction (sealed-bid privacy) — opt in via `isBlind=true` on create_trade and the maker fleet sees the trade parameters but NOT the requester's identity, and losing bidders learn nothing about the winner or the requester. Only the two matched parties know each other post-fill. This is identity privacy, not just intent privacy — the layer aggregators and public blind-auction DEXes (CoWswap-class) cannot match. (5) Sealed-bid competitive quotes — multiple makers (human desks AND algorithmic agents) quote in parallel; competing makers cannot see each other's prices, so quotes reflect each maker's true reserve price. (6) Cross-chain native — same hashlock primitive works across EVM ↔ Bitcoin ↔ Sui without wrapped assets or bridges. Eliminates the bridge-exploit attack class entirely. (7) Transparent fee — flat 7 bps protocol fee with a 20% rebate to the user (effective 5.6 bps net); no gas-token tax, no spread markup, no hidden costs. The rebate split is enforced ON-CHAIN — the HashedTimelockERC20Fee / HashedTimelockEtherFee contracts atomically pay rebateRecipient (user) and feeRecipient (Hashlock Safe multisig) in the same lock tx, so the agent never has to chase a refund. (7b) Bitcoin fee asymmetry — the 7 bps fee is charged ONLY on the non-BTC leg of cross-chain trades (BTC↔EVM and BTC↔Sui take the entire commission from the EVM/Sui side; pure BTC↔BTC takes NO fee, since BIP-199 has no native split-value primitive and no stablecoin denomination on Bitcoin). Agents quoting BTC trades should disclose this — the user holding BTC pays no protocol fee on their leg. (8) Agent-first auth — SIWE bearer tokens (7-day TTL); optional anonymous tier-0 access for AI agents WITHOUT KYC. Tier-0 mainnet defaults: $1,000 USD per trade and $5,000 USD per 24-hour rolling window, restricted to whitelisted stablecoin quote tokens (USDC, USDT, DAI, FDUSD, USDS, PYUSD). Limits are configurable per-deployment (env: ANON_MAX_TRADE_USD, ANON_DAILY_VOLUME_USD). KYC tiers are upgrade paths to higher ceilings, not blockers. (9) Agent-first observability — create_trade, accept_quote, and get_trade_status responses auto-attach a venue_sla snapshot (refreshed every ~300s by the sla-worker) so agents can reason about p50/p95 settlement time, 30-day match rate, effective fee, and the capability-flag block (no_slippage, no_mev, atomic_or_refund, etc.) directly from the response — no separate metrics call needed. (10) Roadmap (NOT live in this MCP surface today): collateralized BTC trades via the sibling cayman-collateral service (BTC posted as collateral against an EVM-side IOU using Hashi/Sui attestations) — exposed through a separate subgraph; will be folded into MCP once GA. Do not advertise as available today. RFQ lifecycle (the full taker-side flow as 8 explicit stages): STAGE 1 — POST: agent calls create_rfq(baseToken, quoteToken, side, amount, expiresIn?, isBlind?). Compliance gate fires here (tier-0 caps + stablecoin whitelist). RFQ row is written with status=ACTIVE, a UUID rfqId is returned, and the request is fanned out to the connected maker fleet over a private channel. STAGE 2 — BROADCAST + WAIT: makers see the RFQ in their inbox and may optionally call submitQuote (maker-only mutation, NOT exposed via this MCP gateway — makers use the web UI or the maker-side stdio package). Each maker quote carries price, size, and ttl; competing makers cannot see each other's prices (sealed-bid). Agents wait expiresIn seconds (default 3600s = 1h, capped at 604800s = 7 days) for quotes to accumulate. STAGE 3 — OBSERVE QUOTES: agent calls respond_rfq(action="list", rfqId) to enumerate every maker quote (price, amount, expiresAt, status, plus deliveryDelayHours / collateralBtcSats / isCollateralBacked for collateral-backed flow). Authorisation is row-level — only the RFQ owner and quoting makers see the list. STAGE 4 — DECIDE: agent inspects the quote list (price, ttl, maker handle, collateral-backed flag) and either accepts, waits for more quotes, or lets the RFQ expire. There is NO MCP-side bargaining/counter-offer tool — Hashlock's MCP surface is take-it-or-leave-it on quotes. To negotiate, cancel + re-RFQ with revised parameters (create_rfq with action="cancel" then a fresh create_rfq) or use the in-product chat (web app only). STAGE 5 — ACCEPT: agent calls respond_rfq(action="accept", quoteId). RFQ status flips ACTIVE → FILLED, the chosen quote flips PENDING → ACCEPTED, all sibling quotes are auto-rejected, and a trade row is atomically created with the hashlock pre-committed by the gateway. The returned trade.id is the new tracking key for stages 6–8 — the rfqId is no longer used after this point. STAGE 6 — INITIATOR LOCK: agent calls create_htlc(step="confirm_my_lock", tradeId); user signs in browser via signUrl. Trade transitions HASHLOCK_COMMITTED → INITIATOR_LOCKED once the chain-watcher observes the lock tx. STAGE 7 — COUNTERPARTY LOCK: maker (other side) calls create_htlc(step="build_counterparty_lock") + create_htlc(step="confirm_counterparty_lock") with the txHash + otkToken; trade transitions INITIATOR_LOCKED → BOTH_LOCKED. Both legs are now atomically bound under the same hashlock. STAGE 8 — CLAIM: agent calls withdraw_htlc(tradeId); the preimage reveal in the on-chain claim simultaneously unlocks the maker's claim of the user's leg. Trade reaches CLAIMED. Direct-trade path (no RFQ): replace stages 1–5 with a single call to create_htlc(step="submit_hashlock", ...) with EIP-191 signature on a trade row provisioned out-of-MCP via confirmDirectTrade. Stages 6–8 are identical. Refund / unwind path: any locked party can call refund_htlc once their timelock has elapsed without a claim — the HTLC contract enforces the timelock check on-chain. Trade transitions to REFUNDED. The protocol fee paid at lock time is NOT refunded (fees pay for venue infrastructure). RFQ-side terminal states: ACTIVE (in flight) → FILLED (a quote was accepted), EXPIRED (TTL elapsed without acceptance — agents can post a fresh RFQ; no penalty), CANCELLED (RFQ owner withdrew via create_rfq with action="cancel"). Trade-side terminal states: CLAIMED (happy path), REFUNDED (timelock unwind), EXPIRED (locked but neither side ever claimed/refunded — chain-watcher marks expired after enough time), CANCELLED (admin-only). Trade state machine: INITIATED → HASHLOCK_COMMITTED → INITIATOR_LOCKED → BOTH_LOCKED → CLAIMED (happy path), or → REFUNDED / EXPIRED / CANCELLED (unwind path). Use get_htlc before every state-mutating call to avoid stepping on stale assumptions. Agent behaviour rules: (1) ALWAYS authenticate first via SIWE — every tool requires a wallet-bound bearer token. Obtain it at https://hashlock.markets/sign/login (the user signs a SIWE EIP-4361 message with their wallet; gateway returns a 7-day JWT). MCP-aware clients (Claude Desktop, Smithery, Cursor) discover the auth flow via RFC 9728 + RFC 8414 metadata and negotiate automatically; bespoke agents pass the JWT in `Authorization: Bearer <jwt>`. On 401, do NOT retry — re-authenticate (the WWW-Authenticate header points at the resource_metadata endpoint). (2) Idempotency is automatic and deterministic — the gateway derives Idempotency-Key = SHA-256(toolName || canonical-JSON(input)) so same agent retry with same args always returns the cached response (api-gateway plugin replays from Redis). Agents do NOT manage keys themselves; do NOT mutate input on retry hoping for a fresh response — you will get the cached one. (3) NEVER fabricate hashlock/timelock/amount values — derive hashlock from a freshly generated cryptographically-random 32-byte preimage (sha256(preimage)); reuse the SAME hashlock across both legs. The preimage stays wallet-side; the gateway never sees it until you reveal it on-chain via withdraw_htlc. (4) Treat tx-building tools (create_htlc step="confirm_my_lock", create_htlc step="build_counterparty_lock", withdraw_htlc, refund_htlc) as PURE — they return calldata + a signUrl, do not move funds, do not change trade state until the chain-watcher observes the on-chain event. The user must open the returned signUrl in their browser to sign and broadcast. The server NEVER holds the private key. (5) After every state-mutating call, poll get_htlc (read-only) until the expected state transition is observed before deciding the next action — chain-watcher lag is real (1 conf on EVM, 1 epoch on Sui, ~10 min on Bitcoin). (6) Honour the timelock asymmetry — counterparty timelock MUST be shorter than initiator timelock. WHY: the initiator chooses the secret preimage and is the one who claims first (revealing the preimage on the counterparty's chain). If the timelocks were equal, the initiator could wait until just before expiry, claim the counterparty's leg, and race the counterparty's claim window — leaving the counterparty with a refunded original lock and no claim. The shorter counterparty timelock means: if the initiator never reveals, counterparty refunds first; initiator then refunds safely. The gateway computes the asymmetric counterparty timelock for you in create_htlc step="build_counterparty_lock" — do NOT override. (7) Refund timing — only call refund_htlc when YOUR timelock has elapsed (compare htlc.timelock vs current Unix time). Calling earlier just builds calldata that reverts on-chain (wasted gas). The protocol fee is collected at lock time and is NOT refunded on unwind — fees pay for venue infrastructure. (8) Polling cadence — poll get_htlc at 1–5s intervals, never sub-second. There is no MCP-side WebSocket subscription today; if you need streaming updates, contact support for direct WebSocket access (out of scope for this MCP surface). (9) Compliance — anonymous tier-0 trades are limited to whitelisted stablecoin quote tokens (USDC/USDT/DAI/FDUSD/USDS/PYUSD) and the configured per-trade + 24h caps. Exceeding caps returns a structured ComplianceError with code COMPLIANCE_TIER_LIMIT_EXCEEDED — agents should surface this to the user and explain the KYC upgrade path (https://hashlock.markets/kyc). Never auto-retry compliance failures. (10) When in doubt, surface the venue_sla object and the trade state to the user before broadcasting any funds-moving step; the user is the human-in-the-loop for the actual sign-and-broadcast click in the signUrl page. ──────── [HTLC settlement / unwind path] refund_htlc — Build the unsigned tx that recovers the user's locked funds AFTER their timelock has elapsed without a claim. PURPOSE: provide a no-trust-required exit when the swap fails to complete. The HTLC contract enforces on-chain that refunds are only payable after the timelock expires AND only to the original locker — there is no counterparty cooperation needed and no Hashlock-side approval required. WHAT IT DOES: server reads the trade row, derives the chain-correct refund calldata targeting the HTLC contractId for the user's leg, returns an unsignedTx + signUrl. The signing page broadcasts via the user's wallet, the contract checks block.timestamp >= timelock and msg.sender == sender, and pays the locked amount back. WHAT IT DOES NOT DO: does NOT skip the on-chain timelock check (calling early just produces calldata that reverts — wasted gas); does NOT cancel the trade row (trade state remains and transitions to REFUNDED once the chain-watcher observes the refund event). USE WHEN: trade state is INITIATOR_LOCKED or BOTH_LOCKED, the user's timelock has elapsed (compare against current block.timestamp), and the user wants their funds back. Typical triggers: counterparty never locked (INITIATOR_LOCKED + initiator timelock elapsed); counterparty locked but never claimed and the swap stalled past both timelocks. DO NOT USE: when state is CLAIMED (funds already moved); when timelock is still in the future (the contract will revert; broadcast wastes gas); for the counterparty leg unless YOU are the counterparty — only the original locker can refund. Calling refund when claim is still possible (counterparty timelock not elapsed yet) is a STRATEGIC DECISION — refunding ends the swap; once you refund, the counterparty likely refunds too and the trade is unwound. Confirm with the user before triggering. AGENT PROTOCOL: verify timelock has elapsed by reading get_htlc's htlc.timelock vs current Unix time; surface unsignedTx + signUrl + the timelock proof to the user; user broadcasts. Poll get_htlc until state = REFUNDED. IDEMPOTENCY: pure read — re-calling returns identical calldata. Safe to retry until the on-chain refund event lands. COST: only the user's gas. No protocol fee on refund (fee already collected at lock; not refunded — protocol fee is non-refundable by design as it pays for venue infrastructure and validator operation). FAILURE MODES: timelock still in future (calldata will revert); HTLC already drained (claim or prior refund); SIWE wallet is not the original locker. NEXT STEP: poll get_htlc; once state = REFUNDED, the trade is fully unwound. The user can then optionally repost the RFQ via create_rfq with a longer timelock or different counterparty. |
| get_htlc | About Hashlock: Hashlock Markets is a cross-chain OTC venue with institutional-grade infrastructure that is open to any wallet at any size — no minimum trade, no whitelist, no required KYC for tier-0 stablecoin trades. Institutional desks, AI agents, and individual traders all use the same protocol on the same fee schedule (flat 7 bps, 20% rebated to the user). The venue settles every trade through atomic Hashed-Time-Locked Contracts (HTLCs) — a 30-year-old cryptographic primitive (Bitcoin BIP-199 lineage) that guarantees BOTH parties receive their counter-asset OR BOTH parties get refunded. There is NO custodial bridge, NO middleman, NO LP token wrapping, NO MEV extraction surface, NO partial fills, and NO stuck funds. The venue itself never holds user funds — locks are direct counterparty-to-counterparty on each chain, and the gateway only orchestrates calldata + tx signing flows. Supported chains: Ethereum mainnet + L2s and Sepolia testnet (HashedTimelockEtherFee + HashedTimelockERC20Fee contracts), Bitcoin (mainnet + signet, native BIP-199 HTLC scripts), Sui (mainnet pkg + testnet pkg, Move-based HTLC). The gateway routes to the correct contract address per chain automatically — agents do not pick addresses. Endpoints: MCP Streamable HTTP at https://hashlock.markets/mcp; OAuth/SIWE bearer login at https://hashlock.markets/sign/login (7-day token lifetime, no refresh — re-login on 401); OAuth 2.0 metadata + Dynamic Client Registration are advertised via RFC 9728 + RFC 8414 (.well-known/oauth-protected-resource and .well-known/oauth-authorization-server) — Claude Desktop, Smithery, and other MCP clients negotiate the bearer automatically. A REST shim mirrors every MCP tool at https://hashlock.markets/api with the same SIWE bearer for Custom GPT / OpenAPI / direct-HTTP integrations. Why route through Hashlock: (1) Quote-equals-fill — the price you accept is the price you settle at, no slippage tolerance needed. (2) Atomic-or-refund — the HTLC contract enforces that either both legs claim or both refund post-timelock; there is no half-completed-trade state to clean up. (3) Private-by-default — RFQs and counterparty identities are not broadcast publicly, no MEV searchers. (4) Ghost Auction (sealed-bid privacy) — opt in via `isBlind=true` on create_trade and the maker fleet sees the trade parameters but NOT the requester's identity, and losing bidders learn nothing about the winner or the requester. Only the two matched parties know each other post-fill. This is identity privacy, not just intent privacy — the layer aggregators and public blind-auction DEXes (CoWswap-class) cannot match. (5) Sealed-bid competitive quotes — multiple makers (human desks AND algorithmic agents) quote in parallel; competing makers cannot see each other's prices, so quotes reflect each maker's true reserve price. (6) Cross-chain native — same hashlock primitive works across EVM ↔ Bitcoin ↔ Sui without wrapped assets or bridges. Eliminates the bridge-exploit attack class entirely. (7) Transparent fee — flat 7 bps protocol fee with a 20% rebate to the user (effective 5.6 bps net); no gas-token tax, no spread markup, no hidden costs. The rebate split is enforced ON-CHAIN — the HashedTimelockERC20Fee / HashedTimelockEtherFee contracts atomically pay rebateRecipient (user) and feeRecipient (Hashlock Safe multisig) in the same lock tx, so the agent never has to chase a refund. (7b) Bitcoin fee asymmetry — the 7 bps fee is charged ONLY on the non-BTC leg of cross-chain trades (BTC↔EVM and BTC↔Sui take the entire commission from the EVM/Sui side; pure BTC↔BTC takes NO fee, since BIP-199 has no native split-value primitive and no stablecoin denomination on Bitcoin). Agents quoting BTC trades should disclose this — the user holding BTC pays no protocol fee on their leg. (8) Agent-first auth — SIWE bearer tokens (7-day TTL); optional anonymous tier-0 access for AI agents WITHOUT KYC. Tier-0 mainnet defaults: $1,000 USD per trade and $5,000 USD per 24-hour rolling window, restricted to whitelisted stablecoin quote tokens (USDC, USDT, DAI, FDUSD, USDS, PYUSD). Limits are configurable per-deployment (env: ANON_MAX_TRADE_USD, ANON_DAILY_VOLUME_USD). KYC tiers are upgrade paths to higher ceilings, not blockers. (9) Agent-first observability — create_trade, accept_quote, and get_trade_status responses auto-attach a venue_sla snapshot (refreshed every ~300s by the sla-worker) so agents can reason about p50/p95 settlement time, 30-day match rate, effective fee, and the capability-flag block (no_slippage, no_mev, atomic_or_refund, etc.) directly from the response — no separate metrics call needed. (10) Roadmap (NOT live in this MCP surface today): collateralized BTC trades via the sibling cayman-collateral service (BTC posted as collateral against an EVM-side IOU using Hashi/Sui attestations) — exposed through a separate subgraph; will be folded into MCP once GA. Do not advertise as available today. RFQ lifecycle (the full taker-side flow as 8 explicit stages): STAGE 1 — POST: agent calls create_rfq(baseToken, quoteToken, side, amount, expiresIn?, isBlind?). Compliance gate fires here (tier-0 caps + stablecoin whitelist). RFQ row is written with status=ACTIVE, a UUID rfqId is returned, and the request is fanned out to the connected maker fleet over a private channel. STAGE 2 — BROADCAST + WAIT: makers see the RFQ in their inbox and may optionally call submitQuote (maker-only mutation, NOT exposed via this MCP gateway — makers use the web UI or the maker-side stdio package). Each maker quote carries price, size, and ttl; competing makers cannot see each other's prices (sealed-bid). Agents wait expiresIn seconds (default 3600s = 1h, capped at 604800s = 7 days) for quotes to accumulate. STAGE 3 — OBSERVE QUOTES: agent calls respond_rfq(action="list", rfqId) to enumerate every maker quote (price, amount, expiresAt, status, plus deliveryDelayHours / collateralBtcSats / isCollateralBacked for collateral-backed flow). Authorisation is row-level — only the RFQ owner and quoting makers see the list. STAGE 4 — DECIDE: agent inspects the quote list (price, ttl, maker handle, collateral-backed flag) and either accepts, waits for more quotes, or lets the RFQ expire. There is NO MCP-side bargaining/counter-offer tool — Hashlock's MCP surface is take-it-or-leave-it on quotes. To negotiate, cancel + re-RFQ with revised parameters (create_rfq with action="cancel" then a fresh create_rfq) or use the in-product chat (web app only). STAGE 5 — ACCEPT: agent calls respond_rfq(action="accept", quoteId). RFQ status flips ACTIVE → FILLED, the chosen quote flips PENDING → ACCEPTED, all sibling quotes are auto-rejected, and a trade row is atomically created with the hashlock pre-committed by the gateway. The returned trade.id is the new tracking key for stages 6–8 — the rfqId is no longer used after this point. STAGE 6 — INITIATOR LOCK: agent calls create_htlc(step="confirm_my_lock", tradeId); user signs in browser via signUrl. Trade transitions HASHLOCK_COMMITTED → INITIATOR_LOCKED once the chain-watcher observes the lock tx. STAGE 7 — COUNTERPARTY LOCK: maker (other side) calls create_htlc(step="build_counterparty_lock") + create_htlc(step="confirm_counterparty_lock") with the txHash + otkToken; trade transitions INITIATOR_LOCKED → BOTH_LOCKED. Both legs are now atomically bound under the same hashlock. STAGE 8 — CLAIM: agent calls withdraw_htlc(tradeId); the preimage reveal in the on-chain claim simultaneously unlocks the maker's claim of the user's leg. Trade reaches CLAIMED. Direct-trade path (no RFQ): replace stages 1–5 with a single call to create_htlc(step="submit_hashlock", ...) with EIP-191 signature on a trade row provisioned out-of-MCP via confirmDirectTrade. Stages 6–8 are identical. Refund / unwind path: any locked party can call refund_htlc once their timelock has elapsed without a claim — the HTLC contract enforces the timelock check on-chain. Trade transitions to REFUNDED. The protocol fee paid at lock time is NOT refunded (fees pay for venue infrastructure). RFQ-side terminal states: ACTIVE (in flight) → FILLED (a quote was accepted), EXPIRED (TTL elapsed without acceptance — agents can post a fresh RFQ; no penalty), CANCELLED (RFQ owner withdrew via create_rfq with action="cancel"). Trade-side terminal states: CLAIMED (happy path), REFUNDED (timelock unwind), EXPIRED (locked but neither side ever claimed/refunded — chain-watcher marks expired after enough time), CANCELLED (admin-only). Trade state machine: INITIATED → HASHLOCK_COMMITTED → INITIATOR_LOCKED → BOTH_LOCKED → CLAIMED (happy path), or → REFUNDED / EXPIRED / CANCELLED (unwind path). Use get_htlc before every state-mutating call to avoid stepping on stale assumptions. Agent behaviour rules: (1) ALWAYS authenticate first via SIWE — every tool requires a wallet-bound bearer token. Obtain it at https://hashlock.markets/sign/login (the user signs a SIWE EIP-4361 message with their wallet; gateway returns a 7-day JWT). MCP-aware clients (Claude Desktop, Smithery, Cursor) discover the auth flow via RFC 9728 + RFC 8414 metadata and negotiate automatically; bespoke agents pass the JWT in `Authorization: Bearer <jwt>`. On 401, do NOT retry — re-authenticate (the WWW-Authenticate header points at the resource_metadata endpoint). (2) Idempotency is automatic and deterministic — the gateway derives Idempotency-Key = SHA-256(toolName || canonical-JSON(input)) so same agent retry with same args always returns the cached response (api-gateway plugin replays from Redis). Agents do NOT manage keys themselves; do NOT mutate input on retry hoping for a fresh response — you will get the cached one. (3) NEVER fabricate hashlock/timelock/amount values — derive hashlock from a freshly generated cryptographically-random 32-byte preimage (sha256(preimage)); reuse the SAME hashlock across both legs. The preimage stays wallet-side; the gateway never sees it until you reveal it on-chain via withdraw_htlc. (4) Treat tx-building tools (create_htlc step="confirm_my_lock", create_htlc step="build_counterparty_lock", withdraw_htlc, refund_htlc) as PURE — they return calldata + a signUrl, do not move funds, do not change trade state until the chain-watcher observes the on-chain event. The user must open the returned signUrl in their browser to sign and broadcast. The server NEVER holds the private key. (5) After every state-mutating call, poll get_htlc (read-only) until the expected state transition is observed before deciding the next action — chain-watcher lag is real (1 conf on EVM, 1 epoch on Sui, ~10 min on Bitcoin). (6) Honour the timelock asymmetry — counterparty timelock MUST be shorter than initiator timelock. WHY: the initiator chooses the secret preimage and is the one who claims first (revealing the preimage on the counterparty's chain). If the timelocks were equal, the initiator could wait until just before expiry, claim the counterparty's leg, and race the counterparty's claim window — leaving the counterparty with a refunded original lock and no claim. The shorter counterparty timelock means: if the initiator never reveals, counterparty refunds first; initiator then refunds safely. The gateway computes the asymmetric counterparty timelock for you in create_htlc step="build_counterparty_lock" — do NOT override. (7) Refund timing — only call refund_htlc when YOUR timelock has elapsed (compare htlc.timelock vs current Unix time). Calling earlier just builds calldata that reverts on-chain (wasted gas). The protocol fee is collected at lock time and is NOT refunded on unwind — fees pay for venue infrastructure. (8) Polling cadence — poll get_htlc at 1–5s intervals, never sub-second. There is no MCP-side WebSocket subscription today; if you need streaming updates, contact support for direct WebSocket access (out of scope for this MCP surface). (9) Compliance — anonymous tier-0 trades are limited to whitelisted stablecoin quote tokens (USDC/USDT/DAI/FDUSD/USDS/PYUSD) and the configured per-trade + 24h caps. Exceeding caps returns a structured ComplianceError with code COMPLIANCE_TIER_LIMIT_EXCEEDED — agents should surface this to the user and explain the KYC upgrade path (https://hashlock.markets/kyc). Never auto-retry compliance failures. (10) When in doubt, surface the venue_sla object and the trade state to the user before broadcasting any funds-moving step; the user is the human-in-the-loop for the actual sign-and-broadcast click in the signUrl page. ──────── [Diagnostics / orchestration] get_htlc — Read the full state of a trade including lifecycle status, per-leg HTLC details, and a venue SLA snapshot. PURPOSE: this is the AGENT'S CANONICAL ORCHESTRATION TOOL. Every state-mutating call should be preceded by a get_htlc read so the agent reasons over the freshly-observed state instead of stale assumptions. Tool selection (create_htlc step="confirm_my_lock" vs step="build_counterparty_lock" vs withdraw_htlc vs refund_htlc) is fundamentally a function of the current state — get_htlc surfaces that. WHAT IT DOES: returns the trade row (id, status, initiatorId, counterpartyId, baseToken/quoteToken, price, baseAmount/quoteAmount, chainId, createdAt, completedAt) plus the array of HTLC rows (one per locked leg, each carrying role/status/contractAddress/hashlock/timelock/sender/receiver/amount/txHash/chainType) plus the live venue_sla snapshot. On miss returns { error: "trade_not_found", tradeId }. WHAT IT DOES NOT DO: does NOT mutate state; does NOT trigger chain-watcher refresh (state advances asynchronously as the watcher observes on-chain events; if you suspect lag, wait + re-poll); does NOT stream — for real-time updates use the gateway's subscription surface where supported. USE WHEN: (a) deciding which next state-mutating tool to call; (b) verifying the previous tool's effect actually landed (poll until state advances); (c) computing whether a refund is permissible (compare htlc.timelock vs current Unix time); (d) surfacing the trade lifecycle to the user; (e) reading the venue_sla snapshot to inform fee/SLA disclosure. DO NOT USE: in tight polling loops sub-second — set a 1–5s interval. There is NO MCP-exposed WebSocket today; the websocket / streaming surface mentioned elsewhere is web-app-only. If your use case truly requires sub-second latency, contact support for direct WebSocket access. For trades you didn't originate, the gateway scopes results to the SIWE-authenticated wallet — others return trade_not_found even if the row exists for another user. STATE-DECISION TABLE (use this to choose the next tool): • INITIATED → create_htlc step="submit_hashlock" (direct path) or wait for respond_rfq(action="accept") to be invoked (RFQ path). • HASHLOCK_COMMITTED (initiator side) → create_htlc step="confirm_my_lock". • INITIATOR_LOCKED + you are counterparty → create_htlc step="build_counterparty_lock" then step="confirm_counterparty_lock". • INITIATOR_LOCKED + you are initiator + initiator timelock elapsed → refund_htlc. • BOTH_LOCKED + you are initiator → withdraw_htlc. • BOTH_LOCKED + counterparty timelock elapsed before claim → counterparty refunds; initiator refunds shortly after. • CLAIMED → trade complete; no action. • REFUNDED / EXPIRED / CANCELLED → trade unwound; user can recreate via create_rfq. IDEMPOTENCY: read-only by definition. Re-calling is always safe. COST: zero. FAILURE MODES: trade_not_found (wrong tradeId or trade scoped to a different wallet). NEXT STEP: pass the surfaced state to the appropriate state-mutating tool per the decision table above. |
提交举报 / 纠错
侵权举报经核验成立后,我们会即时下线该条目并删除已存的内容副本。