When a human walks into an OTC desk, counterparty validation is a meeting. There is a know-your-customer file somewhere, a credit committee that meets quarterly, and a relationship manager who can pull a phone if a leg looks wrong. The check is mostly human, mostly slow, and almost entirely off-chain.
When an autonomous agent picks a trade counterparty, none of those instruments exist. The agent cannot phone a desk. It cannot read a credit memo. It cannot sit on a board call where someone explains why the maker is good for it this quarter. The decision happens in milliseconds, on chain, against a counterparty the agent has never seen and may never see again — and it has to be wrong less often than the agent's owner can tolerate.
Counterparty validation for agents is not KYC. It is a different problem in a different shape, and it needs a different primitive.
Hashlock's third invented primitive is a Verified Counterparty Directory: an on-chain, agent-readable layer that screens a bid through four filters before any HTLC ever locks. The filters run in order, because each one is cheaper than the next and the cheap ones screen out the most takers. The deepest filter — the HTLC itself — only matters if the first three pass.
This post walks through the four filters, the order, the code an agent uses to query each one, and where this layer sits relative to ERC-8183, x402, and the verification-layer projects that have started to ship around it.
The four filters, in order
The filter ordering matters as much as the filters themselves. A naive design checks the most expensive condition first and inverts the cost curve. The directory pushes the cheapest, highest-signal check to the front and only descends into cryptography after policy is already satisfied.
Filter 1 — On-chain settlement history
The cheapest filter is also the highest-signal one for repeated counterparties. Every Hashlock atomic swap emits a settlement event with (maker_address, swap_id, committed_deadline, actual_settlement_time, notional). After a few weeks an address has produced a settlement track record that is more predictive of the next trade than any single identity claim about who is behind it.
The directory computes per-address statistics over a rolling 30-day window:
swaps_completed-
on_time_rate(settled before committed deadline, weighted by notional) -
timeout_rate(HTLC refunded because the counterparty did not deliver in time) -
dispute_rate(legs that went to refund because of a parameter mismatch)
The check is a single read against an indexer or a directory contract; the math is a pure function over public events; the agent gets a pass / inspect / fail flag in one round trip.
// Pseudocode — agent-side query against the directory
const stats = await directory.getCounterpartyStats(makerAddress, {
windowDays: 30,
notionalWeighted: true,
});
if (stats.swapsCompleted >= 50 && stats.onTimeRate >= 0.97) {
// pass — proceed to filter 2
} else if (stats.swapsCompleted < 10) {
// unknown counterparty — escalate to filter 3 (attestation) before continuing
} else {
// fail — the address has a track record and it is bad
return REJECT;
}
The reason this is filter 1 and not filter 4: an address with 200 clean swaps does not need cryptography to be a known quantity. The directory has already done the work. Cheap to query, expensive to fake, predictive enough that most bids resolve here.
Filter 2 — Sealed-bid RFQ commitment
The second filter is structural rather than computational. Hashlock uses a sealed-bid RFQ for the bid phase: a maker submits a bid that is binding once accepted, with a commit-reveal step that prevents the maker from seeing other bids before they commit theirs. Once the taker accepts, withdrawal is not a free option — the maker either delivers or loses their bond.
This filter prevents a specific class of griefing that custodial OTC desks can absorb but agent commerce cannot: the maker who bids aggressively, watches the order book, then withdraws when the market moves against them. In a human OTC desk, the relationship manager fixes this with a phone call. In agent commerce, there is no phone. The primitive has to make the griefing strategy uneconomic.
The agent does not have to evaluate this filter — it is enforced by the protocol. But the agent's strategy can take it as a given: if a bid was accepted by Hashlock's RFQ contract, the maker has already committed.
Filter 3 — Tiered KYC attestation
The third filter is where identity comes back in, but in a layered form. Most agent trades will never need this filter; it exists for the trades that do.
The directory supports three attestation tiers:
- Tier 0 — anonymous. No identity claim. Settlement history is the only signal. Agents trading their own balance against retail liquidity sit here.
- Tier 1 — attested. A third-party attestation has been issued against the address. This is a single on-chain reference to an off-chain attestation, not the attestation itself. Useful for "this address is associated with an institution" claims.
- Tier 2 — verified. A full off-chain identity check has been completed by a regulated attester, and a hashed reference is on chain. Required for sizes or counterparties that policy says are gated.
The attestation tier is policy-readable: an agent operating under a treasurer's policy can require Tier 1 above a threshold size, or Tier 2 for certain instrument classes, without the protocol having any opinion about who can trade. The directory is permissionless at Tier 0; tiering is opt-in.
const tier = await directory.getAttestationTier(makerAddress);
if (policy.requireTier(notional) > tier) {
return REJECT; // not because the address is bad, but because policy says so
}
This is where the protocol stops being a single-tier system and starts being a stack: institutional flow can require attestation, retail-agent flow does not have to.
Filter 4 — HTLC timelock + hashlock
The fourth filter is the cryptographic floor. Once a bid has passed filters 1–3, the actual settlement uses an HTLC: a hash-time-locked contract where both legs are conditioned on the same preimage and the same timelock. Either both legs settle or both refund. There is no window where one party has delivered and the other has not.
This is the filter people usually argue about. It is also the filter that matters least if the first three pass — because if the maker has 200 clean swaps, is bid-bound by the RFQ, and meets the attestation tier the taker requires, the HTLC is a safety net under a counterparty that is unlikely to need it. The cryptography earns its keep when filter 1 returns "unknown counterparty" — for a brand-new address with no track record, the HTLC is the entire trust assumption, and it is enough.
The point of having the HTLC underneath everything is not that it is the strongest filter. It is that it is the floor: even if filters 1, 2, and 3 are misconfigured or compromised, the worst case is a timed refund, not a stolen leg.
Where this sits relative to ERC-8183, x402, and verification layers
The agent-commerce stack has gotten more crowded in the last two months. It helps to be explicit about which layer the directory is, because the projects in this category are mostly complementary rather than competing.
- x402 (Coinbase) and APP (OKX) answer how does an agent pay. They are payment-rail protocols — message formats, settlement triggers, batching. They sit above the settlement primitive. An agent using x402 to authorize a payment still needs something to atomically settle the underlying value if it crosses chains.
- ERC-8183 is a state machine for escrowed task-based payments — a standardized way for an escrow contract to hold value while a task is in progress and release it on completion. It answers what state machine holds the escrow. It does not answer who the counterparty should be in the first place.
- Nava is a verification layer — does this transaction match what the user actually meant. Pre-trade semantic check. Complementary to a directory that checks the counterparty.
- The Verified Counterparty Directory answers should this leg exist at all. It runs before the escrow opens, before the payment rail authorizes, before the state machine starts.
Stacked, the four answer different questions, and an agent in production will hit all of them in the same trade. That is the point: an agent stack is layered, and the directory is the layer that decides whether the rest of the layers ever run.
What the agent actually sees
From the agent's side, the four filters are surfaced as one tool call. The Hashlock MCP server exposes counterparty data through the quote tool, which returns the directory state for the maker on the other side of the bid before the agent commits:
const quote = await mcp.callTool("quote", {
baseAsset: "ETH",
quoteAsset: "USDC",
side: "buy",
amount: "10.0",
});
// quote.maker.directory:
// {
// swapsCompleted30d: 187,
// onTimeRate: 0.994,
// timeoutRate: 0.0,
// attestationTier: 1,
// rfqBindingAfterAccept: true,
// htlcParams: { timelockSec: 7200, hashlockAlgo: "sha256" }
// }
The agent can apply its policy against that object in a few lines. The four filters become a single pre-trade evaluation, not a multi-step workflow.
Where we are in the build
The directory is live on Ethereum mainnet (settlement-history component reading from the V1 HTLC events). SUI has the contracts deployed and CLI-tested; the directory read path is being wired to the SUI mainnet gateway. BTC is signet-validated with mainnet wiring pending. Attestation tiering is on Ethereum first; the SUI and BTC tier reads will follow once the mainnet gateway audits clear.
Chain registry as of this writing: live on ETH, BTC, SUI; roadmap Base, Arbitrum, Solana, TON. Current published version of the MCP server is hashlock-tech/mcp (scoped npm package) at 0.2.0, with the directory query path included in the quote tool response.
Open question
The honest one: what should the directory do with the median counterparty — the address with, say, 30 swaps, 92% on-time, no attestation? Filter 1 says "inspect" rather than pass or fail. The current policy is to escalate to the agent's own decision (return the stats, let the agent decide), but the wider question is whether the protocol should publish a recommended threshold curve, or stay agnostic and let every operator pick. We are reading the responses on this one — if you have an opinion, the post on hashlock.markets has the canonical version of the question and a place to drop a number.
Hashlock Markets — atomic settlement for the agent economy. Protocol page: hashlock.markets. MCP server source: github.com/Hashlock-Tech/hashlock-mcp. The academic version of the primitive (sealed-bid RFQ + HTLC settlement, written up as a working paper) is at SSRN abstract 6712722.
Top comments (0)