Client
Signed and encrypted locally
EIP-712 orders bound to one chain, one contract and one epoch. Timelock encryption to the drand threshold network, which publishes a key every 3 seconds. The scheme is pluggable; Shutter was evaluated.
Confidential execution for onchain markets
Your order stays unreadable to everyone, Sotto included, until its batch closes. Then it clears at one price that anyone can check.
A sealed order is a window you can see straight through. The three on this desktop replay an example batch: when the clock in the taskbar reaches zero they decrypt, clear at one price, and go back to glass.
An order, sealed. Unreadable to everyone until batch 48392 closes.An order, sealed. Unreadable to everyone until batch 48392 closes.Sotto protects intent before the trade. It is not a mixer, a privacy coin or an anonymity tool, and it does not hide what settles.
Hidden from the relay too. Encryption happens in your browser.
Settlement is transparent on purpose. That is what makes execution verifiable.
| Name | What happens | Public after this step |
|---|---|---|
1Sealed | Your browser signs the order and encrypts it. The relay receives a ciphertext, its hash and a salted commitment. | Nothing about the order |
2Committed | The epoch closes. The relay fixes the batch as a Merkle root before any key exists, so no order can be added later. | Order count and root |
3Decrypted | The drand network publishes the round signature that opens every ciphertext in the batch at once. | All orders in the batch |
4Matched | A deterministic auction finds the single price that trades the most volume, closest to the oracle. | Clearing price and result hash |
5Settled | The settlement contract re-checks every signature, limit and nonce, then moves tokens atomically. | Every transfer |
| Ciphertext | Side | Qty | Limit | Filled | Fill |
|---|---|---|---|---|---|
| 0x9f3a…c21e | Buy | 120 | 332.60 | 120 | |
| 0x41d0…7b9a | Sell | 150 | 332.05 | 80 | |
| 0xe6b2…03f4 | Buy | 60 | 331.80 | 0 | |
| 0x1c77…a8d5 | Sell | 80 | 331.90 | 80 | |
| 0xb90e…5e12 | Buy | 40 | 332.30 | 40 | |
| 0x73a4…d6c0 | Sell | 30 | 332.90 | 0 |
When the epoch closes the Merkle root 0xbb17b3…c5f5a3 is committed, the orders are decrypted, and four of them fill at a single clearing price of 332.19 USDG.
Four parts, each checkable without trusting the one before it.
Signed and encrypted locally
EIP-712 orders bound to one chain, one contract and one epoch. Timelock encryption to the drand threshold network, which publishes a key every 3 seconds. The scheme is pluggable; Shutter was evaluated.
Accountable, not trusted
Every accepted ciphertext gets a signed inclusion receipt. If the published root leaves your order out, the receipt is proof of censorship.
One price, no queue
A uniform-price batch auction in integer arithmetic. The Rust matcher and an independent TypeScript reference produce identical result hashes for every fixture.
The contract enforces
The matcher only proposes a price and fills. Signatures, limits, minimum fills, nonces, oracle bounds and batch membership are verified again onchain.
| Name | Original location | Size |
|---|---|---|
| front-run.txt | Every batch | 0 bytes |
const client = new SottoClient({ relayUrl });
const { markets } = await client.config();
const order = await client.buildOrder({
owner: account.address,
market: markets[0],
side: SIDE_BUY,
baseAmount: 120n * 10n ** 18n,
limitPrice: toProtocolPrice("332.60", 18, 6),
});
// Signed, then encrypted, in this process.
const sealed = await client.signAndSeal(order, (typedData) =>
account.signTypedData(typedData),
);
const receipt = await client.submit(sealed.envelope);
// After the epoch closes.
const check = await client.verifyInclusion(receipt);{
"protocolVersion": 1,
"scheme": 1,
"epochId": 48392,
"ciphertext": "0x2d2d2d2d2d424547…",
"ciphertextHash": "0x9f3a…c21e",
"orderCommitment": "0x5be1…90d7"
}No ticker, side, size, price or wallet. The relay rejects any envelope that carries an extra field.