otcvault settles otc deals nobody has to trust

Deals created
from DealCreated events
Deals settled
from DealSettled events
Stable volume settled
USDC + USDT, gross
Fee
0.75%
total · cap 1.00%

OTC today runs on trust. Trust is the attack surface.

Most token deals outside exchanges still happen in a chat: someone sends first, someone hopes. The failure modes are well known.

Fake stablecoins. A token called "USDT" with the right logo and the wrong address. By the time you check the explorer, the real tokens are gone. Who sends first? Every OTC deal has a moment where one side is fully exposed. Escrow agents solve it by adding a third party you also have to trust.

Vesting on a handshake. Discounted tokens usually come with a lock-up — enforced by a promise. Buyers dump early; sellers stop delivering. Neither side has recourse. Risk in the middleman. Centralised OTC desks and escrow services hold your funds, can freeze them, and can disappear. Several have.

OTCVault removes the trusted party instead of adding one.

A steel vault door standing ajar in daylight

Four states. One contract. No one in the middle.

Every deal is a single entry in the escrow contract and moves through four states. The state you see on a deal page is read directly from the chain.

  1. 01
    OPEN

    Maker creates and deposits

    The maker names the counterparty, both legs, the delivery schedule and an expiry. Their side is pulled into escrow in the same transaction.

    createDeal()
  2. 02
    FUNDED

    Taker deposits

    The named taker sees exactly what is escrowed — real address, live symbol and decimals, whitelist and registry status — and deposits their side. A 24-hour cool-off starts.

    fund()
  3. 03
    CONFIRMED

    Both verify and confirm

    Each party checks the other's deposit and types the last 4 characters of the counterparty's token address. The first confirm is recorded; the second one settles.

    confirm()
  4. 04
    SETTLED

    Atomic delivery

    Stablecoins go to the seller minus 0.375%. The token goes to the buyer — immediately, or into a Sablier vesting stream that cannot be cancelled by anyone.

    confirm() → _settle()

Exit. CANCELLED — maker cancels while open; mutual cancel while funded; unilateral withdrawal after the 24h cool-off; or anyone triggers a refund after expiry. Every deposit returns in full. No fee on cancelled deals.

Trust the contract you can read, not the people you can't.

  1. 01

    Both legs are escrowed.

    Nobody sends first. The contract holds both deposits and releases both — or neither.

  2. 02

    No admin key can touch your funds.

    No rescue, sweep, pause or upgrade function. The owner can only set the fee (capped at 1%) and the vesting rail for future deals.

  3. 03

    Non-upgradeable, verified source.

    Five plain contracts, no proxies, source verified on Arbiscan. What you read is what runs — forever.

  4. 04

    One leg is always a real stablecoin.

    Every deal must include USDC or USDT from the on-chain registry. A fake "USDT" cannot be the stable leg — by construction.

  5. 05

    The token leg is whitelisted.

    Reviewed for verified source, no transfer tax, no blacklist or pause functions, sane holder distribution.

  6. 06

    Vesting is a Sablier stream, not a promise.

    Delivered as a Sablier Lockup v4 stream created with cancelable = false. The seller cannot claw it back; the buyer cannot skip the schedule.

Fee, rail and schedule are snapshotted per deal: what you confirm is what settles.

Two hands and one paper contract across a table

0.75%, split evenly. Nothing else.

Charged only on the stablecoin leg and only when a deal settles. Cancelled and expired deals cost nothing beyond gas.

Buyer pays
stable amount + 0.375%
Seller receives
stable amount − 0.375%
Protocol receives
0.75% of the stable amount
Charged on the token leg
Never
Charged on cancel / refund
Never
Maximum fee, ever
1.00% — MAX_FEE_BPS_X2 = 200, immutable

Worked example — $100,000 deal

Bob deposits
100,375 USDC
Alice receives
99,625 USDC
Fee vault
750 USDC

Alice sells 250,000 XYZ to Bob for 100,000 USDC, vesting linearly over 12 months.

Bob deposits 100,375 USDC (100,000 + 375 fee). Alice deposits 250,000 XYZ.

On settlement: Alice receives 99,625 USDC. Bob receives a Sablier stream of 250,000 XYZ over 12 months. The fee vault receives 750 USDC.

If either side walks away before both confirm: Bob gets back exactly 100,375 USDC, Alice exactly 250,000 XYZ.

Listing fee. Adding a new token to the whitelist costs 1,000 USDC, paid to the fee vault when you request a review. It pays for the review; it does not guarantee approval.

Where fees go. Fees accumulate in the FeeVault contract. Planned use: buy-back-and-burn of a future protocol token. Until then, the balance is publicly visible on-chain. View vault

An open bound ledger with a pencil

Built for both sides of the table.

For sellers you hold the token

  • Get paid in real USDC or USDT — the registry, not a logo, decides what counts.
  • Set the vesting terms yourself: liquid, cliff, or linear with cliff and daily/weekly unlocks.
  • Your tokens never leave escrow until the buyer's stablecoins are in escrow too.
  • Walk away any time before both confirmations — full refund after the 24h cool-off.
  • You receive the stable leg the instant the deal settles.

For buyers you hold the stablecoins

  • See the exact token contract before you commit: address, live metadata, whitelist status, and red flags such as proxy or pausable bytecode.
  • Vested tokens arrive as a Sablier NFT — the seller cannot cancel or alter the stream.
  • Claim as tokens unlock at app.sablier.com, with no dependency on OTCVault.
  • If the seller never confirms, withdraw your stablecoins after 24 hours.
  • Pay 0.375% — the same as the seller.

Where we are, honestly.

Pre-audit · v1 · Use with amounts you can afford to lose

OTCVault is live on Arbitrum One with real funds and has not yet been audited by an external firm. We would rather say that plainly than imply otherwise.

Done
  • 97 Foundry tests. Unit, fuzz, invariant and Arbitrum fork tests against the real Sablier Lockup v4. Core invariant: escrow balance ≥ sum of deposits for OPEN and FUNDED deals; settled deals deliver exactly stable − fee and exactly the asset amount.
  • Slither static analysis. 0 High. 1 Medium (strict-equality sentinel in the registry) triaged as false positive; 1 Low reentrancy note accepted — state is SETTLED before any external call and confirm is nonReentrant.
  • Verified source. All five contracts verified on Arbiscan. No proxies.
  • Minimal admin surface. Owner can set the fee (≤1%), the rail for future deals, manage whitelist and registry (48h timelock on additions), sweep earned fees. Cannot move escrowed deposits.
  • Deposit measurement. Every deposit is measured balanceAfter − balanceBefore and reverts on shortfall, so fee-on-transfer tokens fail loudly.
Not yet
  • External audit. Scheduled before the per-deal cap is lifted. Until then, treat the protocol as beta.
  • Multisig ownership. The owner is currently a single key. Transfer to a multisig is planned before audit.
  • Bug bounty. To follow the audit.
Full security page

Questions people actually ask.

Once a deal is FUNDED, a 24-hour cool-off starts. If both parties have not confirmed by then, either party can call Cancel and both deposits are refunded in full. Before the cool-off ends, a cancel request is recorded; if the other party also requests cancel, refunds happen immediately (mutual cancel).

What's next.

Now
Arbitrum One · named-counterparty deals · Sablier Lockup v4 rail · USDC/USDT · token whitelist
Next
External audit → per-deal cap lifted · ownership to multisig · Base deployment
Then
Ethereum mainnet · BNB Chain · open offers · Hedgey TokenLockupPlans as an alternative rail
Later
Protocol token + buy-back-and-burn from FeeVault · governance over fee, registry and whitelist · Hebrew interface

Five contracts. Verify them yourself.

Verify the contracts yourself

  • DeOTCEscrow0x909Dab616CF39d00BeDb8Eae6d3Ab96D54AfA2B7
  • TokenWhitelist0x443CC5b294054D057Fc30cAb45Fcaad0982664da
  • StableRegistry0x7c1b6dd8B4f31b09A5fbd13694395dE576Ec38B3
  • SablierRail0xc924a8b3B785ddf5aFb22bF33432Dc43b4242f19
  • FeeVault0xFF27e5EdD20ef6254D388aA21F66222daE9CFacD

ready when both of you are.

Create a deal