Search projects, hackathons, sponsors and tech

    ← All projects

    Sotto

    Sotto is a request-for-quote block-trading venue built on Hedera for ATS-issued securities.

    Demo

    About

    ATS, which is Hedera's Asset Tokenization Studio, lets an issuer mint a compliant bond. It does not let anyone trade one. Sotto is the missing secondary market. A bondholder escrows a block of an ATS-issued security and puts it up for bid. Dealers compete under commit–reveal: during the quote window only keccak256(price, quantity, nonce, dealer) reaches the venue, so nobody, including us — can read a price before the window closes. The window closes on a Hedera Consensus Service timestamp rather than our clock, so the venue cannot quietly extend it for a favoured dealer. Dealers then reveal, and each price is recomputed against its commit and must match exactly or the quote is void. The book is allocated on strict price priority across several dealers at once. In a live testnet run, a 30-unit block filled 3 units at 98.80 to one dealer and 20 at 98.55 to another, leaving 7 unfilled — each dealer paying its own quoted price, never a rival's and never a uniform clearing price. Every fill is a separate EIP-712 Trade with its own signatures, its own nonce and its own firmness deadline, and a dealer's firmness bounds its own settlement deadline on-chain rather than merely in our engine. Settlement is atomic delivery-versus-payment in a single transaction. Cash moves by transferFrom; the security moves by executeHoldByPartition on the ATS diamond, which runs compliance at the transfer itself. Any failure reverts both legs. There is no window in which the buyer holds the security and the seller has not been paid, and a revoked-KYC buyer fails inside the delivery call, taking the payment back with it. Compliance is enforced by ATS, not bolted on by the venue. A NAV band guard inside the settlement contract additionally refuses any trade priced more than 5% from an on-chain reference, with a staleness bound so an unmaintained mark blocks settlement rather than being trusted. Every stage — the request, the escrow, each sealed commit, the close, each reveal, the award and each fill — is timestamped on HCS topic 0.0.10383803. The venue is auditable after the fact without being transparent while the auction is live. The bond's whole lifecycle is on-chain too: coupons the contract schedules for itself through HIP-1215 scheduled calls, and redemption at maturity through the ATS Bond facet. Live on Hedera testnet at https://sotto-tau.vercel.app. Every claim in the README links to HashScan or to the HCS topic, so none of it has to be taken on trust.

    How it's made

    Hedera testnet throughout, against the real Asset Tokenization Studio diamond (factory 0.0.7708432) and real Circle USDC (0.0.429274), not a mock token. Contracts are Solidity 0.8.24 under Hardhat, all verified on Sourcify as exact_match: • SottoSettlement — EIP-712 verification of both parties, nonces consumed inside the settling call so a signature cannot replay, hold validation (escrow is self, amount sufficient, not expired), then transferFrom and executeHoldByPartition in one call. Every guard is a custom error, and the API decodes those four-byte selectors back into sentences for the UI. • SottoNavOracle — administrator-published NAV with a staleness bound, behind an IPriceSource seam. • ChainlinkPriceSource — reads live Chainlink AggregatorV3 feeds on Hedera testnet through that seam, so an asset with a real market price overrides the administrator's mark. A bond's NAV is not on a price feed and never will be, which is why the administrator path is primary and the feed is the override rather than the reverse. • SottoCouponScheduler — schedules its own coupon payments using HIP-1215 scheduled contract calls via the Hedera Schedule Service system contract at 0x16b. • SottoDealerBond — a dealer posts a bond and forfeits it to the seller, not to the venue, for failing to reveal. Two settlement paths, both live: Path A is EVM-native. The buyer approves exact USDC and anyone at all can relay the fully-signed trade, because the authorisation is the two signatures rather than the sender. Path B grants no allowance anywhere. The buyer signs a native HTS transfer of their own cash with a HIP-820 wallet session, the venue batches it together with the delivery call as a HIP-551 atomic batch, and the network provides the atomicity instead of a contract. The relay verifies the inner cash leg before batching — correct token id, exact debit and credit, exactly two transfers, no hbar movement, our batch key, already frozen — because without that check a buyer could sign a one-unit transfer and take the whole block. Backend is Fastify with ethers 6: the RFQ engine and allocator, an HCS audit writer, and the batch relay. Auction windows are judged against HCS consensus timestamps rather than the server clock. Frontend is Next.js 15 with Reown AppKit, wagmi and @hashgraph/hedera-wallet-connect, so a single ECDSA account serves both the EVM and Hedera-native namespaces from one wallet connection. The most useful thing we learned is in docs/ATS-SPIKE.md and is absent from Hedera's published docs: ATS v3.1.0 as deployed uses different role hashes from repo HEAD. Grants made with HEAD's constants land on hashes the deployed contract never checks — hasRole returns true and every guarded call still reverts, with no useful error. Only DEFAULT_ADMIN_ROLE is stable across the two versions, which is what let us repair it from outside. One more that cost us a live trade: Hedera's JSON-RPC relay attaches no revert data to the error from a sent transaction, so a custom error's four bytes never reach the caller. Recovering them needs an eth_call replay against the same block, which the venue now does automatically whenever a revert comes back unexplained.