Skip to content

Protocol

Three pieces.
One is on a chain.

V4 Privacy is a settlement layer for signed orders, not a chain of its own — the matching engine around it is the planned piece, and the list below says so. Everything private happens before the transaction exists. Everything the transaction does is public and auditable. Here is each piece and where it is unfinished.

Off-chain

The book

Signed orders arrive encrypted and sit in a book nobody publishes. There is no mempool to watch and no partial depth to reconstruct. This is also the piece that has to be run honestly, and the piece we are working hardest to make unnecessary.

Off-chain

The prover

When two orders cross, the engine produces a proof that the match respected the reference price and both signatures, without publishing either side. Today it produces an attestation instead. That is a placeholder and it is written on the front page.

On-chain

The contract

One Solidity file re-derives the digest, checks the signature, the nonce, the deadline, the proof and the quote, then performs two ERC-20 transfers atomically. Every check is a revert. There are no warnings.

§ 01

What your signature commits you to.

The struct below is the whole agreement. It is hashed with the domain separator for Kryptis v1, signed in your wallet, and goes no further than the matching engine until someone takes the other side.

Yes, the domain still says Kryptis. The contract predates the rename and touching that string would invalidate every signature already produced against it. It moves at the next deploy, not before.

The typed data

struct Intent {
    address trader;        // must equal msg.sender
    address tokenIn;
    address tokenOut;
    uint256 amountIn;
    uint256 minAmountOut;  // your floor, enforced on-chain
    uint256 slippageBps;   // must be 0
    uint256 deadline;
    uint256 nonce;         // strictly increasing
}

The quote, decimal-aware

function quote(address tokenIn, address tokenOut, uint256 amountIn)
    public view returns (uint256)
{
    uint256 pIn  = priceUsd8[tokenIn];
    uint256 pOut = priceUsd8[tokenOut];
    if (pIn == 0 || pOut == 0) revert NoPrice();

    uint8 dIn  = IERC20Metadata(tokenIn).decimals();
    uint8 dOut = IERC20Metadata(tokenOut).decimals();

    return (amountIn * pIn * (10 ** uint256(dOut)))
         / (pOut * (10 ** uint256(dIn)));
}

Settlement — every branch is a revert

function executeIntent(
    Intent calldata intent,
    bytes  calldata signature,
    bytes  calldata proof
) external nonReentrant returns (bytes32 intentHash, uint256 amountOut) {
    if (intent.amountIn == 0)               revert ZeroAmount();
    if (intent.tokenIn == intent.tokenOut)  revert SameToken();
    if (block.timestamp > intent.deadline)  revert Expired();
    if (intent.trader != msg.sender)        revert Unauthorized();
    if (intent.nonce <= usedNonces[intent.trader]) revert NonceUsed();
    if (intent.slippageBps != 0)            revert Slippage();

    intentHash = hashIntent(intent);
    if (settled[intentHash])                revert AlreadySettled();

    address signer = ECDSA.recover(intentHash, signature);
    if (signer != intent.trader)            revert BadSignature();
    if (!_verifyAttestation(proof, intentHash)) revert InvalidProof();

    amountOut = quote(intent.tokenIn, intent.tokenOut, intent.amountIn);
    if (amountOut < intent.minAmountOut)    revert Slippage();
    if (IERC20(intent.tokenOut).balanceOf(address(this)) < amountOut)
        revert InsufficientLiquidity();

    usedNonces[intent.trader] = intent.nonce;
    settled[intentHash] = true;

    IERC20(intent.tokenIn).safeTransferFrom(intent.trader, address(this), intent.amountIn);
    IERC20(intent.tokenOut).safeTransfer(intent.trader, amountOut);

    emit IntentSettled(intentHash, intent.trader, intent.tokenIn,
                       intent.tokenOut, intent.amountIn, amountOut);
}

KryptisIntentPool · Solidity 0.8.24 · domain Kryptis v1

§ 02

Four ways this breaks.

The price feed

A crossed trade prices off a reference. Whoever controls that reference controls your fill.

Never price off a spot tick. Production wants a Uniswap TWAP checked against an independent feed — Chainlink or Pyth — with settlement reverting when the two disagree beyond a bound.

Today: an owner-set priceUsd8. Fine for a testnet, not fine for money.

The cost of the proof

Verifying a SNARK on L1 can cost more than the spread you were trying to save.

Keep the proof small — Groth16 or PLONK, a few hundred thousand gas — and settle where that gas is cheap. Base or Arbitrum, not mainnet.

Today: the verifier is a swappable component. The ABI does not change when it lands.

The race on your funds

An order signed at 14:00 is worthless if the tokens moved at 14:01, and the revert lands after a counterparty was promised a fill.

Short deadlines plus escrow: size committed to an order sits in a temporary account until it settles or expires. ERC-4337 session keys make that bearable to trade against.

Today: the contract reverts on a failed transferFrom. Escrow is not implemented.

Us

The engine sees both sides. It could cross you against itself, or sit on your order.

The proof constrains what the engine can settle, not what it can see. Shrinking what it can see — threshold decryption, a committee, an enclave — is the open problem. We would rather write that down than claim it is solved.

Today: a single operator. The trust assumption is real.

§ 03

What's live, in one list.

No roadmap gradient, no “coming soon” badges. If a line says planned, there is no code for it yet.

  • EIP-712 order signinglive
  • Nonce and replay protectionlive
  • Atomic ERC-20 settlementlive
  • On-chain quote and floor checklive
  • Foundry tests and deploy scriptslive
  • Order crossing between two intentsplanned
  • Attestation verifierhalf-built
  • Groth16 circuithalf-built
  • Uniswap v4 hook, fallback routinghalf-built
  • TWAP and external price guardplanned
  • Escrow / ERC-4337 session keysplanned
  • External auditplanned

Unaudited research software that moves real tokens the moment you point it at a real network. That is exactly why this list is written plainly.

Back to the simulator