DEV Community

Baris Sozen
Baris Sozen

Posted on Edited on

How We Built Hashlock Markets: Intent-Based Crypto Trading for AI Agents

Sealed RFQ, HTLC atomic settlement, and a Model Context Protocol server that lets Claude trade for you.

Update, October 2026. This post was first published in April 2026. It has been revised to match the product as it is live today: six mainnet chains (Bitcoin, Ethereum, TRON, Solana, Base, Robinhood Chain), API key authentication, and the current MCP tool set. Current reference: https://docs.hashlock.markets


Trading crypto OTC has always been a trust problem. You find a counterparty on Telegram, negotiate a price, and hope nobody front-runs or ghosts. Institutions do it over Bloomberg chat. Retail does it on DEXs and eats slippage.

We built Hashlock Markets to fix both sides, and then we gave AI agents the keys.

What is Hashlock Markets?

Hashlock Markets (hashlock.markets) is a non-custodial atomic P2P cross-chain swap protocol: Bitcoin to EVM, TRON and Solana, via sealed RFQ and HTLC. Two parties swap assets across chains directly, with no bridge and no custodian.

Here's how it works:

  1. A taker posts an RFQ: "I want to sell 10 ETH on Ethereum for USDT on TRON."
  2. Makers answer privately: sealed quotes, no public price book, nobody sees anyone else's price.
  3. Both sides agree terms: the counterparties negotiate in a thread and accept.
  4. Atomic settlement via HTLC: both legs settle, or both refund after their timelocks. No custodian. No trust.

The result: no slippage, no front-running, and no information leakage before execution.

A note on the name: "Hashlock" refers to our trading platform and brand, Hashlock Markets, built by Hashlock-Tech. We are not affiliated with Hashlock Pty Ltd (hashlock.com), the Australian smart contract auditing firm. Our protocol uses the cryptographic hash-lock primitive (as in HTLCs), but "Hashlock" as a product name refers to this trading platform specifically.

Why AI Agents?

An RFQ is just structured data: asset pair, amount, direction, constraints. There's no chart to read, no order book to stare at. You express what you want, and counterparties compete to fill it.

This is exactly how LLMs work best. They're great at understanding natural language ("swap 5 ETH for USDC, best price, settle on Base") and terrible at pixel-level chart analysis. RFQ trading plays to their strengths.

So we built an MCP server.

The Hashlock MCP Server

The Hashlock MCP server implements the Model Context Protocol, the open standard that lets AI assistants like Claude, GPT, and others interact with external tools.

The Tool Set

The hosted server exposes 24 tools covering the full trading lifecycle:

  • Discovery: whoami, list_assets, list_open_rfqs, get_rfq
  • Taker side: create_rfq, cancel_rfq, list_my_rfqs, request_listing
  • Maker side: quote_rfq, list_rfq_threads, get_thread
  • Negotiation: propose_price, accept_proposal, accept_terms
  • Settlement: set_swap_address, build_fund, build_claim, build_refund, broadcast_tx, swap_status, list_swaps
  • Wallet proofs: wallet_proof_message, prove_wallet, remove_wallet_proof

The hosted server is strictly non-custodial: it returns unsigned transactions, and the initiator generates and keeps the HTLC secret locally.

Two Ways to Connect

Hosted (Streamable HTTP): add one URL to your MCP client. The client discovers it needs authorization, sends you to Hashlock to approve with a wallet signature, and receives its own API key over OAuth 2.1:

https://hashlock.markets/mcp
Enter fullscreen mode Exit fullscreen mode

Clients that do not speak OAuth send a key you created yourself: Authorization: Bearer hk_live_.... Keys are valid for 90 days.

Local (stdio): run the npm package with your own chain keys in the environment. This server can sign and settle without a human in the loop:

npx -y @hashlock-tech/mcp
Enter fullscreen mode Exit fullscreen mode

An autonomous agent needs neither a browser nor an email: it mints its own API key with a wallet signature alone (GET /v1/keys/nonce, then POST /v1/keys).

Cross-Chain by Default

Hashlock Markets settles on Bitcoin, Ethereum, TRON, Solana, Base and Robinhood Chain. The HTLC mechanism is chain-agnostic: as long as both chains support SHA-256 hash verification and time locks, atomic settlement works.

This means an AI agent can execute a trade like "swap 1 BTC on Bitcoin for ETH on Ethereum" and both legs settle atomically. No bridge. No wrapped tokens. No custodian.

For Market Makers

If you're running a trading desk or market-making operation, Hashlock Markets gives you:

  • Sealed quotes: your pricing stays private.
  • No adverse selection: you see the RFQ, not a limit order that's already been front-run.
  • A maker feed: a WebSocket stream of new RFQs you can quote over the same socket.
  • Multi-chain settlement: settle on whichever chain has the best liquidity for you.

Getting Started

As a Trader (via Claude or any MCP client)

Add the hosted URL to your MCP client and approve with your wallet when prompted. For a local server with your own keys, add this to claude_desktop_config.json:

{
  "mcpServers": {
    "hashlock": {
      "command": "npx",
      "args": ["-y", "@hashlock-tech/mcp"],
      "env": {
        "HASHLOCK_API_URL": "https://hashlock.markets/api",
        "HASHLOCK_EVM_KEY": "0x<agent EVM key>"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Then just ask Claude: "Create an RFQ to sell 5 ETH for USDT on TRON."

As a Developer

npm install @hashlock-tech/mcp
Enter fullscreen mode Exit fullscreen mode

The server is MIT-licensed. Fork it, extend it, build your own trading strategies on top.

The Bigger Picture

We believe the future of trading is declarative. You say what you want, not how to get it. AI agents handle execution. Protocols handle settlement. Humans handle strategy.

Hashlock Markets is the infrastructure layer for that future.


Hashlock Markets is built by Hashlock-Tech. Try it at hashlock.markets.

Questions? Reach out on GitHub or join the conversation.

Top comments (4)

Collapse
 
azender1 profile image
Anthony Zender •

This is a really interesting direction — especially the intent → solver → execution model.

Curious how you handle this case:

intent is executed
confirmation is lost (timeout / network)
system retries the same intent

At that point, how do you distinguish:

“execute again”
vs
“resolve what already happened”

In trading / payouts this turns into duplicate execution under uncertainty.

Collapse
 
barissozen profile image
Baris Sozen • • Edited

Great question — this is exactly the at-least-once vs exactly-once boundary, and HTLCs give us a surprisingly clean answer.

The trick is that every intent becomes content-addressed before it ever hits a chain. When the trade is committed,
we generate a preimage s and derive H = hash(s). From that moment on, H is the deduplication key for the entire trade — not the intent ID, not a request nonce, but the cryptographic commitment itself.

So the retry path is never "execute again, hope for the best." It's:

  1. Before broadcasting any lock tx, query HTLC(H) on the target chain (we cache via a chain-watcher, but the ground truth is the chain itself).
  2. If HTLC(H) exists → we already executed. Switch from commit mode to observe mode and let the state machine advance to whatever the on-chain state says.
    1. If HTLC(H) doesn't exist → safe to broadcast.

Because the HTLC contract reverts on a duplicate H (you can't create two HTLCs with the same hashlock), the chain itself enforces uniqueness — even if our retry logic is buggy, double-execution is physically impossible at the settlement layer. Worst case is a wasted gas estimation.

The DB-side state machine (PENDING → INITIATOR_LOCKED → BOTH_LOCKED → CLAIMED → COMPLETED) is the second layer.
Every transition is guarded by ON CONFLICT DO NOTHING plus a "current state must be X" check, and the resolver paths that race chain-watcher events are explicitly idempotent — e.g. COMPLETED → COMPLETED is a no-op, not an error. (We learned that one the hard way on Sui, where the indexer sometimes beats the resolver back.)

Then the preimage itself does the rest. Claiming requires revealing s on-chain, so once a claim lands, every observer in the world can derive the same global truth. There's no "did it happen?" — there's the chain, and you read it.

And the timelock is the deterministic backstop. If the system retries forever, the network stays partitioned, both sides crash — eventually the timelock expires and funds refund atomically. No human escalation, no support ticket,
no double-payout window.

Collapse
 
azender1 profile image
Anthony Zender •

That's a really clean design — using H as the global dedup key and letting the chain enforce uniqueness essentially removes the "did it happen?" question at settlement entirely.
The gap I keep running into is one step earlier — before anything reaches the chain. If create_intent or the RFQ fanout is triggered but the agent loses confirmation and retries, you can get duplicate intents or repeated solver load even though settlement stays safe.
Same idea as H, just applied before the protocol boundary. Treat the invocation itself as content-addressed, claim it before kicking off the flow, retries resolve against that claim instead of re-triggering.
Curious if that's intentionally left to the caller or if there's handling at the intent layer I missed.

Thread Thread
 
barissozen profile image
Baris Sozen • • Edited

that pre-H invocation layer is the right place to flag the approach is to dedupe one step lower than a client wrapper would: at the gateway boundary itself, content-addressed by (toolName, canonical input, caller subject), so the property holds whether the MCP host bothers to set an idempotency header or not :) Settlement-layer atomicity (the H content-addressing) covers everything downstream; the gateway-level claim covers everything upstrem of H, which covers the problem you described.