OkinawaTrader v4 — User Documentation
This is the source the research assistant cites. Every answer it gives links back to a section on this page.
00OkinawaTrader v4 — User Documentation
OkinawaTrader v4 is a non-custodial protocol for trading and physically settling tokenized agricultural commodities. This document set explains what the protocol is and how to use it, with one guide per audience so you can start with the parts that apply to you.
In thirty seconds
OkinawaTrader lets buyers, sellers, and liquidity providers transact in tokens that represent real, physically stored grain — blackgram (cBLG), tur dal (cTUR), and yellow maize (cMZE) — alongside USDC. Pricing happens on an automated market maker. Physical delivery happens through licensed warehouse operators called ADCs. There is no operator and no admin in the middle; the smart contracts are immutable, permissionless, and audited.
There are exactly three on-chain participants:
- Trader — buys or sells exposure to a commodity through an AMM pool.
- Liquidity Provider (LP) — supplies the pool with assets and earns trading fees.
- ADC Facility — operates a warehouse, holds physical grain, and issues electronic warehouse receipts (eWRs) that back the commodity tokens.
What is in this document set
Every guide is self-contained — read only the ones that apply to you. Internal documents and the auditor pack are included so contributors and reviewers do not have to chase scattered files.
| Document | Audience | When to read |
|---|---|---|
| 01 — What is OkinawaTrader | General public; people new to crypto | You want a plain-language introduction with no jargon. |
| 02 — Investor & Stakeholder Guide | Investors, partners, board observers, journalists | You need protocol thesis, token model, governance posture, risk, roadmap. |
| 03 — Trader Guide | Anyone buying or selling commodity exposure | You want to swap cBLG / cTUR / cMZE or trade the perpetual. |
| 04 — Liquidity Provider Guide | Capital allocators supplying AMM liquidity | You are depositing into a pool, creating one, or tracking LP returns. |
| 05 — ADC Facility & Off-taker Guide | Warehouse operators, suppliers, physical-delivery buyers | You are minting eWRs, registering supply offers, or settling a purchase order. |
| 06 — OST Internal & Onboarding Guide | OST employees and new hires | You work at OST and need to ship code, file stories, or read the kanban. |
| 07 — Auditor & Researcher Guide | External auditors, security researchers, regulators | You are reviewing the protocol safety, invariants, or compliance posture. |
| 08 — RFC Mechanisms (Audit perspective) | Smart-contract audit teams | You are scoping a contract audit and need RFC-by-RFC mechanism, invariants, and attack surfaces. |
| 09 — RFC Mechanisms (Compliance perspective) | Banking and financial-settlements compliance | You are reviewing custody, settlement finality, VASP posture, and the CC-1 to CC-8 controls register. |
| 10 — RFC Mechanisms (Engineering perspective) | Engineering — contributors and integrators | You need RFC mechanism plus contract surface, integration points, and test paths. |
What is in production today
OkinawaTrader v4 is currently a Sepolia (Ethereum testnet) product. Mainnet is not live and is gated on three independent conditions: a finalized Cantina security audit, a counsel-signed legal addendum, and acceptance of the governing RFCs after a public comment window.
| Layer | Status | Where it lives |
|---|---|---|
| Smart contracts (base + trust layer) | Deployed and verified on Sepolia | contracts/src/ — 311 forge tests, no admin keys |
| Smart contracts (settlement layer) | Code complete, awaiting governance | contracts/src/ — Round-3 deploy script ready |
| Web app | Read flows live; write flows partially wired | website/ (Next.js 14) — sign in with any EVM wallet |
| SDK | Feature-complete for current contracts | @okinawatrader/sdk-v4 (viem-based) |
| Reference indexer (subgraph) | Base events indexed; settlement handlers pending | Hosted on The Graph (Sepolia) |
| Reference solver | Scaffold only | solver/ — anyone may run their own |
How to verify everything in this document set
The protocol is permissionless and the source of truth is on-chain. Every fact in these guides can be independently verified:
- Contract addresses live in contracts/deployments/<network>.json and are mirrored on Etherscan with verified source.
- Architecture and invariants live in docs/ARCHITECTURE.md and docs/operations/SECURITY.md (publicly readable in the repo).
- The internal engineering audit is at docs/AUDIT_REPORT.md; the 2026 Q2 security pass is at docs/operations/audit/2026-Q2-internal/.
- There are no admin keys: contracts/scripts/no-admin-detector.sh enforces this on every pull request and is the cleanest invariant to verify yourself if you are skeptical.
| Important These guides describe behaviour as of 2026-05-19. Settlement-layer contracts are coded and tested but are not yet deployed to any network. Anything tagged "RFC-005" or "settlement" is live as code, not yet as a usable on-chain surface. |
|---|
Where to start
- Curious reader / new to the project: read Document 01.
- Looking to evaluate the protocol as an investment or partnership: read Document 02.
- Want to trade or provide liquidity: read Documents 03 and 04.
- Operate a warehouse or want forward physical exposure: read Document 05.
- Joining the OST engineering team: read Document 06.
- Auditing or researching: read Document 07 — it points to the canonical sources.
- Smart-contract audit team: read Document 08 — RFC mechanism with invariants and attack surfaces.
- Banking / settlements compliance: read Document 09 — RFC mechanism mapped onto custody, finality, dispute, and the controls register.
- Engineering deep dive on protocol mechanism: read Document 10.
Contact
For security disclosures, email security@openstreet.consulting. Do not file a public issue for a suspected vulnerability. Acknowledgement within 48 hours; triage within five business days.
For general inquiries — partnerships, ADC onboarding, press — email hello@openstreet.consulting.
01What is OkinawaTrader?
This guide answers a simple question: what is OkinawaTrader, and why does it exist? It is written for readers who have never used a crypto wallet and who want the plain-English version before deciding whether to dig deeper.
The problem in one paragraph
Farmers and small mills who grow grain — blackgram, tur dal, yellow maize and similar staples — are price-takers. They sell into local mandi markets at prices set the morning of harvest, with no easy way to lock in a price in advance. Buyers on the other side of the trade have the mirror-image problem: a flour mill or feed processor can rarely lock in supply at a known price for the next month. Both sides absorb price risk that does not need to exist.
What OkinawaTrader does
OkinawaTrader is a digital marketplace where each kilogram of grain stored in a participating warehouse is represented by a digital token. You can buy and sell those tokens just like shares — and you can also cash them in for the underlying physical grain when you actually want delivery.
The tokens are named after the grain they represent:
| Token | Underlying | What it represents |
|---|---|---|
| cBLG | Blackgram (urad dal) | One cBLG corresponds to one kilogram of blackgram stored at a participating warehouse. |
| cTUR | Tur dal (pigeon pea) | One cTUR corresponds to one kilogram of tur dal stored at a participating warehouse. |
| cMZE | Yellow maize | One cMZE corresponds to one kilogram of maize stored at a participating warehouse. |
| USDC | US dollar | A US-dollar stablecoin used for pricing and payment. Not issued by OkinawaTrader. |
| cDEP | Protocol utility token | Used only to create new trading pools. Has no claim on revenue (there is no protocol revenue). |
| cCLD | AI inference credits | Prepaid credits a farmer or merchant can spend on natural-language trading help. |
Traders
A trader is anyone who wants exposure to the price of grain — to speculate, to hedge, or to lock in a future purchase. They open a wallet, buy cBLG (or cTUR or cMZE) with USDC, and hold or sell the token as the market price moves. They never have to handle physical grain unless they want to.
Liquidity providers
A liquidity provider (LP) is someone who supplies the marketplace with inventory — a deposit of USDC paired with cBLG, for example — so that other people can trade. In return for that service, LPs earn a small fee on every trade against their inventory. It is the same role a market-maker plays on a stock exchange.
ADC facilities
ADC stands for Authorized Delivery Custodian. An ADC is a working warehouse — somewhere with physical silos and quality-assurance staff — that signs up to the protocol, deposits a financial bond, and attests on-chain to the grain they hold. The token supply is backed one-to-one by ADC-attested kilograms; if the attestations are wrong, the bond can be slashed.
Why on a blockchain
OkinawaTrader runs on Ethereum because three properties matter for a commodity market and Ethereum gives them for free: open access (anyone with a wallet can use it, no permission required), public verifiability (every trade and every warehouse attestation is recorded permanently in a way anyone can audit), and trust-minimisation (no single company can change the rules or freeze a user account).
OkinawaTrader itself — OST, the company — does not run the protocol. The smart contracts are immutable, meaning once deployed they cannot be changed, paused, upgraded, or shut down by OST or anyone else. OST built the protocol and runs a reference website and indexer, but the protocol does not depend on OST being around. Anyone may run a competing frontend, indexer, or solver.
| A common worry: is it custodial? No. OkinawaTrader never holds your money or your grain. Your USDC and your commodity tokens sit in your own wallet at all times. When you trade, you sign the transaction yourself. When you take physical delivery, you coordinate directly with the ADC. The protocol never takes custody. |
|---|
How money gets in and out
OkinawaTrader is a non-custodial protocol — fiat money never touches OST. To put dollars in, the web app offers an on-ramp widget that sends your card payment to a licensed third-party vendor (Transak, Onmeta, or Onramp.money) which gives you USDC in your wallet. To take dollars out, you transfer USDC to the same vendor and they pay your bank.
OST is not a money-services business, never custodies fiat, and never sees your card details or bank information. On the Sepolia testnet there is no fiat at all — the on-ramp simply mints test USDC from a faucet so you can try the whole flow without spending money.
What happens when something goes wrong
Disputes are settled on-chain by a bonded voting process. If an ADC attests inventory that is materially wrong, anyone can open a dispute, post a bond, and put it to a vote weighted by participants’ bonds. If the dispute is upheld, the ADC’s bond is slashed; if it is dismissed, the challenger forfeits theirs. There is no protocol-level pause button: the deliberate trade-off is that bad actors lose money, not access.
For software issues — a website outage, a stale price feed, a buggy indexer — the mitigation is off-chain: roll back the broken component, post a status notice, file a postmortem. The trading contracts keep working regardless, because no single component can take them down.
Is this for me?
- If you are a farmer, mill, or trader exposed to grain prices, OkinawaTrader is a way to hedge or arbitrage without a broker.
- If you are an investor used to commodity ETFs, this is a more direct claim — every token corresponds to attested physical grain, and you can redeem if you want.
- If you are a curious crypto user, the protocol is a working example of bringing a non-financial real-world asset on-chain without an operator in the middle.
- If you are uncomfortable holding tokens in a self-custody wallet, this product is not for you yet — there is no managed-account option, and there will not be one.
Next steps
If you want to keep reading, the right next document depends on what you plan to do. Investors and partners should read Document 02. Traders should read Document 03. Liquidity providers should read Document 04. Warehouse operators and forward-purchase buyers should read Document 05.
02Investor & Stakeholder Guide
This guide is written for people evaluating OkinawaTrader as an investment, a partnership, or a topic for serious diligence. It covers the protocol thesis, the token model, the governance and compliance posture, the known risks, and the productionization roadmap. It is descriptive, not promotional, and every claim is verifiable from the public source tree.
| Disclaimer Nothing in this document is investment, financial, tax, or legal advice. OkinawaTrader is permissionless software; OST does not sell tokens, manage funds, or operate a regulated financial product. Do your own diligence and consult licensed advisors before any commitment. |
|---|
Thesis
Agricultural commodity markets in emerging economies — and particularly in South Asia for blackgram, tur dal, and yellow maize — share three structural inefficiencies: opaque spot pricing dominated by mandi auctions, no on-ramp for forward contracts at small lot sizes, and large warehouse-receipt fraud risk. OkinawaTrader replaces the trust edge of each problem with on-chain primitives: a continuous AMM for spot pricing, a permissionless purchase-order layer for forward matching, and a bonded warehouse-receipt registry for delivery integrity.
The protocol does not aim to replace existing exchanges or commodity companies. It aims to be the open settlement layer underneath them — the place where price discovery, forward matching, and physical delivery share one immutable substrate, the same way Ethereum became the substrate beneath retail DEX frontends.
What you are looking at
| Component | Maturity | Notes |
|---|---|---|
| Smart contracts (RFC-001, RFC-006) | Sepolia rounds 1 & 2 deployed; 311 forge tests passing | No admin keys; enforced by no-admin-detector.sh in CI |
| Smart contracts (RFC-005 settlement) | Coded, tested, not deployed | Awaiting governance acceptance to broadcast Round-3 |
| Web app | Reads working; ~22 write paths return 501 | The highest-impact write paths are being wired in active development |
| SDK (@okinawatrader/sdk-v4) | Feature-complete for current contracts | Hand-written ABIs; codegen pipeline tracked under OST-DP-3540 |
| Subgraph (The Graph) | Base events indexed; settlement handlers stubbed | Hosted service, Sepolia-only at this stage |
| Solver (reference) | Scaffold; watcher/matcher/submitter all stubs | Anyone may run their own — protocol does not depend on OST’s |
| Audits | Internal audit pass complete; Cantina re-audit pending | Mainnet promote refuses to run until Cantina audit at HEAD |
Token model
OkinawaTrader is not a token-launch project. The protocol uses several tokens, each with a narrow purpose, none of which represent equity in OST or claims on protocol revenue (there is no protocol revenue).
| Token | Purpose | Issuance |
|---|---|---|
| cDEP | Utility token burned to create new pools. Sybil resistance, not revenue. | Fixed-supply fair-launch sale; OST holds zero at genesis; unsold supply is burned at window close. |
| cBLG / cTUR / cMZE | Fungible 1-kg commodity tokens; backed 1:1 by ADC-attested grain. | Minted only by WarehouseReceiptRegistry against a signed eWR; burned on redemption to physical. |
| USDC | External US-dollar stablecoin used for pricing and payment. | Not issued by OST. The protocol consumes USDC as a primitive. |
| cCLD | Prepaid AI-inference credits for natural-language trade help. | Bought with USDC; spent by off-chain relayers per credit. |
| eWR (NFT) | Non-fungible electronic warehouse receipt with quality metadata. | Minted by an ADC against deposited physical grain. |
| BuyerEWR / SellerEWR | Soulbound proof-of-completion receipts at pickup. | Non-transferable; record that a forward settlement closed. |
Why a burn, not a fee
Routing pool-creation fees to OST would re-introduce the operator edge that v4 removes. Burning cDEP destroys value to the zero address and creates the same sybil-resistance without anyone collecting the cost. OST is not a beneficiary of the burn; OST may optionally be an LP of the default (cDEP, USDC) pool on the same terms as anyone else, subject to a publicly disclosed cap.
Governance posture
The protocol has no governance contract, no token-weighted voting, no multi-sig, and no upgrade path. Change happens through new contract deployments, gated by Requests for Comments (RFCs). The full RFC corpus is in docs/rfc/.
| Gate | What it requires |
|---|---|
| RFC acceptance | OST officer review, ≥14-day public comment, counsel sign-off, audit handoff. |
| Counsel-signed addendum | DECENTRALIZATION_MEMO_v4_ADDENDUM.md committed to OST’s governance repo. |
| Formal audit | Cantina engagement at the HEAD of the deploy candidate. |
| Independent CI gate | OST-DP-3501 — CI refuses to run mainnet deploy unless CANTINA_AUDIT_SHA matches HEAD. |
Compliance posture
OST has structured the protocol to keep its operating perimeter small. OST never custodies user funds, never custodies fiat, never operates as a money-services business, and never controls the protocol after deployment. The compliance controls are documented in docs/operations/COMPLIANCE.md under control IDs CC-1 through CC-8.
- Permissionless on-chain contracts — no KYC at the protocol layer.
- Fiat on-ramp and off-ramp delegated to a licensed third-party vendor (Transak, Onmeta, or Onramp.money).
- Sanctions / OFAC screening enforced at the OST-operated frontend, not the protocol.
- ADC bonding required for any facility wishing to appear in the OST directory; the underlying contract is permissionless.
- No PII collected. Server log retention 30 days; sanctions-block logs 7 years (regulatory).
Risk register
| Risk | Mitigation |
|---|---|
| Mainnet deploy before re-audit | CI gate (OST-DP-3501) blocks broadcast unless audit SHA matches HEAD. |
| No protocol-level pause | Deliberate trade-off. Mitigation surface is off-chain: frontend delist, SDK deprecation, indexer flagging. |
| MEV / sandwich on AMM trades | Documented; user-side mitigations (split size, private RPC) until aggregator integration (OST-DP-3700) lands. |
| Low-liquidity pool manipulation | Default pool seeded by cDEPSeedDistribution; OST LP share capped at 10%; users see pool TVL in UI. |
| Solver-key compromise | Solver cannot drain user funds; worst case is griefing. Production operators advised to use KMS/HSM (OST-DP-3555). |
| Indexer drift | Subgraph is one of multiple sources; SDK reconciles material decisions against RPC (OINV-1 sync-check in CI). |
| Regulatory change | Frontend tier is the regulatory perimeter; the protocol itself is unchanged. OST adjusts CC-1 to CC-8 as posture evolves. |
What is in flight
Active development is focused on the productionization foundation: CI/CD, the highest-impact write paths, settlement subgraph handlers, and Cantina re-audit handoff. After that: completing the solver loop, finishing the settlement server routes, observability, and broadcasting Round-3 to Sepolia. The public testnet candidate then layers on E2E tests, load tests, a security review, and documentation polish before RFC-001 promotion to Accepted (counsel willing).
Mainnet is explicitly out of scope until the two external gates clear: the Cantina re-audit (atomic-seed redesign) and the counsel-signed addendum to DECENTRALIZATION_MEMO.md. Both are external to OST engineering.
Where to look next
- docs/ARCHITECTURE.md — single source of truth on the contract suite and off-chain stack.
- docs/AUDIT_REPORT.md — point-in-time engineering audit; basis for the productionization punch list.
- docs/operations/SECURITY.md — invariants, trust model, accepted risks, disclosure timeline.
- docs/operations/COMPLIANCE.md — CC-1 to CC-8 controls and counsel gates.
- docs/rfc/ — every protocol change starts as an RFC; the corpus is the design history.
Talking to OST
Partnership and investor enquiries: hello@openstreet.consulting. Press: press@openstreet.consulting. Security disclosures: security@openstreet.consulting (do not file public issues). OST does not run private chat groups for token discussions or fundraising signal — there is no OST fundraising round.
03Trader Guide
This guide walks a trader through the OkinawaTrader web app: getting a wallet ready, funding it with USDC, swapping into cBLG / cTUR / cMZE, and (eventually) opening or closing a perpetual position. Mainnet is not live; everything in this guide currently happens on Ethereum Sepolia testnet.
What you need before you start
- A wallet. Any EIP-1193 wallet works — MetaMask and Coinbase Wallet are explicitly tested. The wallet must support the Sepolia network (Chain ID 11155111).
- Sepolia ETH. You need a small amount for gas. Use the Alchemy Sepolia faucet (alchemy.com/faucets/ethereum-sepolia) or the PoW faucet (sepolia-faucet.pk910.de). A tenth of an ETH is plenty for many transactions.
- Sepolia USDC. You can mint test USDC from the in-app Add USDC widget — it pulls from the deployed MockUSDC faucet. No card or bank required on testnet.
Step 1 — Connect your wallet
Open the web app and click "Connect Wallet" in the top right. Approve the connection request in your wallet extension. The app uses wagmi with injected and Coinbase Wallet connectors; if you have multiple wallets installed, pick the one you want. Switch your wallet network to Sepolia if it is not already there — the app will prompt you.
Step 2 — Add USDC
On every persona page in /learn, the first node is "Add USDC". On Sepolia this calls the MockUSDC faucet, which mints test USDC straight to your wallet. There is no fee, no KYC, no card.
On mainnet (when it launches) the same widget hands off to a configured fiat vendor (Transak, Onmeta, or Onramp.money) for the card or bank leg. OST never sees your payment details. The widget calls a third party that issues the USDC.
Step 3 — Pick a pool
The /pools page lists every pool that has been deployed through PoolFactoryV4. Each pool shows the two assets, current reserves, total LP shares, and recent volume. Click into a pool to see a quote form and the price impact curve.
| The price curve is LMSR, not constant-product OkinawaTrader pools use a Logarithmic Market Scoring Rule (LMSR) cost function rather than the more common x*y=k. The practical difference is that quotes always come from the on-chain pool.quote() view — never approximated client-side — because LMSR involves exponentials. The web app does this automatically; if you are coding against the SDK, quote through PoolClient.quote() rather than hand-rolling math. |
|---|
Step 4 — Get a quote
Enter how much you want to spend (or how much you want to receive). The quote shows:
- Expected output for your input amount.
- Effective price per unit — how much each kg of commodity is costing you, blended across the curve.
- Price impact — how much your trade is moving the marginal price. Anything above 1% on a small pool is worth a second look.
- Pool fee — the LP fee charged on this swap, paid into the pool reserves (so LPs earn it via increased share value).
Step 5 — Sign the swap
Click "Swap". Your wallet pops a confirmation showing the calldata, the gas estimate, and the tokens flowing. Approve in your wallet. The pool takes your input and credits your output in a single block. There is no operator, no settlement delay, and no batch — your trade either lands in the next block or reverts.
If the price moved against you between quote and confirmation, the transaction reverts with an InsufficientOutput error. Raise the slippage tolerance and try again, or split your trade into smaller pieces.
Cashing out — USDC back to your bank
The /learn workflow graph ends every persona journey with a "Cash out" node. It transfers USDC on-chain to the configured vendor escrow address and opens the vendor’s hosted payout page where you provide bank details directly to them. For INR payouts the widget surfaces an indicative tax estimate (30% gains tax + 1% TDS); this is for your information only — OST does not withhold, remit, or report tax.
On Sepolia the off-ramp shows the estimate but the on-chain leg is disabled because no fiat vendor is wired. Use the widget end-to-end on mainnet, when it launches.
Optional — Trading the perpetual
Each commodity has a PerpetualVault and a PerpMarketplace. The perpetual lets you take leveraged exposure without ever holding the physical token — useful when you want directional bets larger than your USDC balance.
- Funding accrues every block from the pool mark vs the 24-hour TWAP.
- There is no liquidation operator; positions are closed via the marketplace.
- The vault never backs physical inventory — perpetuals and commodity tokens are separate invariant domains.
| Sepolia status Perpetual vaults are coded and tested but Round-3 has not been broadcast yet. The /perp page in the web app shows empty-state placeholders until the deploy lands. Use the SDK directly if you need to exercise the contracts before the UI catches up. |
|---|
Reading your positions
/positions shows everything tied to a single wallet: USDC, cDEP, cBLG, cTUR, cMZE balances; LP-share holdings per pool; eWRs you hold; open purchase orders; supplier offers; soulbound BuyerEWR / SellerEWR receipts for completed settlements.
MEV and sandwich attacks
LMSR pools have no commit-reveal scheme. Large trades visibly move the curve and attract front-running. If your trade is large enough to move the marginal price more than ~0.5%, consider splitting it across several blocks, using a private RPC, or waiting for the aggregator integration tracked under OST-DP-3700.
Low-liquidity pools
Pools with less than roughly one million USDC of liquidity can be manipulated by a single large trader. The default pool is seeded by cDEPSeedDistribution and is the deepest by design; user-created pools vary. Check pool TVL before trading size against any pool.
No protocol-level pause
If a critical bug is found post-launch, OST cannot pause the contracts. Mitigation is off-chain: the OST frontend will banner-warn and delist affected pools; SDK clients will deprecate them; indexers will flag them. Watch okinawatrader.io for status if something looks wrong.
Where to look next
- docs/guides/USER_GUIDE.md — the canonical end-user walkthrough, also covers LP and ADC flows.
- docs/operations/SECURITY.md — the full known-accepted-risks list.
- docs/spec/LMSR.md — the AMM cost-function math if you want to compute quotes off-chain.
- Document 04 — if you want to provide liquidity in addition to trading.
04Liquidity Provider Guide
This guide is for liquidity providers — anyone who wants to earn AMM trading fees by supplying inventory to an OkinawaTrader pool. It walks through how the pools work, how fees accrue, the first-LP and pool-creation flows, the impermanent-loss profile, and the risks specific to commodity-backed pools.
How LP works on OkinawaTrader
Each pool is an instance of LMSRAMMPool — a permissionless contract deployed through PoolFactoryV4. An LP deposits a pair of assets and receives LP-share tokens proportional to their contribution. The pool reserves are addressable only by burning those LP shares. Trade fees accrue inside the reserves, raising the value of each LP share over time — Uniswap-v2 style, not explicit per-block payouts.
| Property | Behaviour |
|---|---|
| Pricing curve | LMSR — C(q) = b·ln(e^(qA/b) + e^(qB/b)). The b liquidity constant is set at construction. |
| Fee model | Flat fee tier set at construction, paid into the pool reserves. No protocol fee, no fee recipient. |
| LP-share token | ERC-20 minted at deposit; burned at withdraw. Transferrable. |
| First depositor | For new factory pools, the first LP is cDEPSeedDistribution by construction (atomic seed redesign). For user-created pools, the creator seeds. |
| Withdrawal | Always available — burn LP shares, receive proportional reserves. No timelock, no fee. |
| Admin keys | None. Pool parameters (b, fee) are immutable from deploy. |
Step 1 — Pick a pool
The /pools page lists every pool deployed via PoolFactoryV4. The metrics worth focusing on:
- Total LP shares — proxy for the pool’s capital base.
- Reserve composition — current ratio of the two assets.
- 24h volume — recent activity, useful for projecting fee yield.
- LP fee tier — the per-swap fee, which is paid into your share value.
- Realised b — the LMSR liquidity constant; deeper b means smaller price impact per trade.
Step 2 — Deposit
Choose a deposit size. The pool’s current reserve ratio sets your deposit ratio — if your deposit does not match, the pool rebalances via an internal swap, charging you the swap fee. This is the same behaviour as Uniswap v2.
The flow is two transactions:
- Approve each input token to the pool address (one approve per token).
- Call deposit(amountA, amountB) on the pool. The pool transfers your tokens, mints LP shares to your address, and emits a Deposit event.
Step 3 — Earn fees
Every swap pays the pool fee. The fee is not airdropped to LPs; it remains inside the reserves, increasing the value of every LP share proportionally. To "claim" your fees you withdraw a portion of your LP shares — the redeemed reserves include accumulated fees.
Annualised LP yield from fees is approximately (24h fee revenue × 365) / (pool TVL). The web app shows a rolling 24h figure on each pool page. This is a backward-looking estimate, not a forward guarantee.
Step 4 — Withdraw
Open the pool page, switch to the Withdraw tab, and pick a percentage. The pool burns your LP shares and credits your wallet with the proportional reserves. There is no fee on withdrawal and no waiting period.
Creating a new pool
Anyone can create a pool. The cost is paid in cDEP, which is burned (destroyed) on createPool. Burn amount is read from PoolFactoryV4.burnAmount() and is constant.
- Acquire cDEP — buy at /sale during the seed window, or from the secondary cDEP/USDC pool, or from another holder.
- Approve cDEP to PoolFactoryV4 (or use ERC-2612 permit).
- Call createPool(tokenA, tokenB, b, feeTier). The factory burns the cDEP, deploys a fresh LMSRAMMPool via MasterCoDeployer (CREATE2), and emits PoolCreated.
- Seed liquidity as the first depositor — your deposit anchors the reserve ratio.
| Pool parameters are immutable LMSR b and the fee tier are constructor arguments and cannot be changed afterwards. Pick deliberately — a too-small b makes the pool fragile; a too-large b makes fees harder to earn. Look at comparable pools for reasonable values. |
|---|
Impermanent loss profile
LMSR pools have a different impermanent-loss profile from constant-product AMMs. The cost function is more conservative at the curve edges, which reduces directional bleed during large moves but also reduces fee capture during heavy two-way flow.
For a commodity / USDC pool, the realised IL depends heavily on whether the commodity price drifts (you bleed) or oscillates (you earn). Backtest using historical mandi prices for blackgram, tur dal, or maize before committing significant size.
eWR-quality risk
The commodity token (cBLG, cTUR, cMZE) is backed 1:1 by ADC-attested kilograms. If an ADC issues a fraudulent or off-spec attestation and is caught via the Dispute process, the dependent eWRs are invalidated. As an LP holding the commodity token, you are exposed to this — though the slashed ADC bond is the primary mitigation, and disputes are public.
Default pool concentration
The default cDEP/USDC pool is unusually deep relative to other pools because its initial liquidity is permanently locked by cDEPSeedDistribution (the seed contract holds those LP shares forever and they are sent to address(0) after deposit). The trade-off is lower fee yield per unit of LP capital, in exchange for being unsqueezable on price.
First-LP for user-created pools
If you are the first LP on a user-created pool (one not seeded via cDEPSeedDistribution), you set the reserve ratio. There is no protocol-level anchor — bad ratios mostly hurt you, but they also become a target for arbitrage. Match the prevailing external price before depositing.
Operational tips
- Use the SDK (PoolClient) for programmatic monitoring. server/ is a read-only cache and may lag.
- Recompute your share-of-pool occasionally — your share migrates as other LPs deposit and withdraw.
- Watch the dispute log for any ADC that backs commodity in pools you hold.
- Verify pool addresses against contracts/deployments/sepolia.json before approving large allowances.
Where to look next
- docs/guides/USER_GUIDE.md — the canonical end-user walkthrough.
- docs/spec/LMSR.md — the AMM cost-function math.
- docs/rfc/RFC-001-v4-pool-factory-gate.md — pool factory and cDEP design.
- Document 03 — if you also want to trade.
- Document 07 — if you want to verify invariants and audit posture before allocating.
05ADC Facility & Off-taker Guide
This guide is for the three counterparties in the physical-settlement workflow: ADC facilities (warehouse operators), suppliers (offering forward grain), and buyers (locking in forward physical purchases). It covers the RFC-005 mechanism, the on-chain steps, the cancellation paths, and the bonded dispute process.
| Status note The RFC-005 settlement contracts are coded, tested, and audit-pending; they are not yet deployed to Sepolia or mainnet. Until Round-3 broadcasts, the /settlement UI shows honest empty state. If you want to integrate ahead of launch, work against the SDK clients in @okinawatrader/sdk-v4. |
|---|
Architectural picture
There are four building blocks. They compose without any operator in the loop:
| Contract | Role |
|---|---|
| BufferRegistry | Per-(ADC, grade) physical-inventory ledger. A doubly-linked FIFO lot queue. ADC-signed inventory attestations are publicly verifiable; anyone may flag divergence and claim a bounty. |
| SupplierRegistry | Permissionless registry of supplier offers under a USDC delivery bond. Suppliers post (commodity, grade, kg, price, deadline). The bond is burned on default — it does not route to OST. |
| PurchaseOrder | Buyer USDC escrow with a match → deliver → pickup state machine. Single-fill MVP. Auto-conversion to PerpetualVault on non-pickup so inventory is never permanently encumbered. |
| BuyerEWR / SellerEWR | Soulbound (non-transferable) ERC-721 proof-of-completion receipts minted at pickup for both sides. |
Becoming an ADC
Anyone with a working warehouse facility can register, on-chain, as an ADC. To appear in the OST-curated directory you also submit a KYC pack off-chain — but the on-chain participation is permissionless.
- Prepare facility metadata: storage capacity, grades handled, location ID, contact info.
- Generate or designate a QA-attestation signing key. This key signs eWRs and inventory updates.
- Post the USDC bond required by ADCFacilityRegistry. The bond is locked into the registry and is what disputes can slash.
- Call ADCFacilityRegistry.register(metadata, signingKey). Your facility is now visible on-chain.
- (Optional) Submit KYC pack to OST to be listed in ops/adc-directory.json — the OST web app filters its directory to listed ADCs only, but other frontends may show your facility regardless.
Minting an eWR (electronic warehouse receipt)
After physical grain arrives at your facility:
- Run QA on the lot — moisture, foreign matter, broken-kernel percentage, etc.
- Compute the QA attestation payload hash according to the schema in QAAttestationLib.
- Anchor the attestation via Attestation.anchor(schemaHash, payloadHash, signature).
- Call WarehouseReceiptRegistry.mint(lotID, grade, kg, expiry, qaAttestationRef). The registry mints the eWR NFT (to your address or to the supplier) and mints the corresponding amount of the fungible commodity token (cBLG / cTUR / cMZE).
- Optionally credit the BufferRegistry FIFO queue if the lot is intended for redemption fulfillment rather than direct sale.
Supplier flow — registering forward supply
- Acquire USDC for the offer bond. Bond size is set per offer and locked when registered.
- Call SupplierRegistry.registerOffer({commodityID, pricePerKg, quantityKg, deadlineSeconds, bondUSDC}). Note: pricePerKg must be at or above the cBLG pool TWAP mid — RFC-005 §7-F enforces a TWAP floor so suppliers cannot underprice the AMM.
- Wait for a permissionless solver to match your offer with a buyer’s open purchase order.
- Deliver: call PurchaseOrder.deliver(poID, ewrID, signedQAAttestation). The ADC custody is referenced by the eWR; the QA attestation is the EIP-712 signature you produced.
- Get paid: once the buyer confirms pickup, USDC releases to your address and your offer bond unlocks at the same time.
Buyer flow — locking in a forward purchase
- Decide your needed (commodity, kg, deadline, budget). Top up USDC.
- Call PurchaseOrder.open({commodityID, quantityKg, deadlineSeconds, budgetUSDC}). The contract escrows your USDC.
- A solver pairs your PO with an eligible supplier offer and calls proposeMatch.
- The supplier delivers an eWR + signed QA attestation via PurchaseOrder.deliver. You now have a 3-day exclusive pickup window.
- Confirm pickup with your own signed attestation: PurchaseOrder.confirmPickup. USDC releases to the supplier, the eWR transfers to you, and both parties mint their soulbound BuyerEWR / SellerEWR receipts.
- Coordinate physical pickup with the ADC out-of-band — the on-chain pickup attestation is the protocol’s evidence of delivery.
What happens if something goes wrong
| Scenario | On-chain path | Outcome |
|---|---|---|
| Buyer changes mind before any match | PurchaseOrder.buyerCancel | Full USDC refund to buyer. |
| Deadline passes, no match | PurchaseOrder.cancel | Full USDC refund to buyer. |
| Supplier matched but never delivered before deadline | PurchaseOrder.cancel (post-deadline) | Buyer refunded. Supplier’s bond is slashed. |
| Buyer never confirmed pickup despite signed delivery | PurchaseOrder.convertAfterWindow | Supplier’s USDC pays out after the grace window. Buyer’s position auto-converts to a PerpetualVault long on cBLG — they end up with exposure, not a refund. This frees the eWR for redemption. |
| QA attestation disputed | Dispute.open | Bonded vote decides. Sustained → ADC bond slashed, eWR invalidated, PO routes to its failure branch. Overturned → challenger bond forfeited. |
Disputes — how the bonded vote works
- Anyone who believes an attestation is materially wrong opens a Dispute by posting a USDC bond and the disputed attestation reference + evidence hash.
- Participants vote, weighted by their posted bond. Vote weight reset between disputes.
- Outcome — sustained (stakeForSlash > stakeAgainstSlash) → ADC bond is slashed and the attestation is invalidated; downstream eWRs / POs / redemptions are routed to their failure paths. Challenger gets bond back plus a share of the slashed bond.
- Overturned (otherwise) → challenger forfeits their bond.
| No quorum floor today There is currently no minimum-participation requirement on a dispute — a zero-vote dispute simply never slashes. This is tracked under OST-DP-3733; expect a quorum floor in a follow-up RFC. |
|---|
ADC operational responsibilities
- Maintain the USDC bond at all times. Below-bond ADCs cannot mint eWRs or attest buffer.
- Submit BufferRegistry.attestBuffer regularly per the cadence in RFC-005. Divergence > 25% from the prior attestation is slashable via slashAttestationDivergence (2% bounty to caller).
- Respond to delivery requests within RFC-005 §3.5 deadlines. Missing delivery slashes your bond.
- Keep your QA-signing key secure — compromise lets a third party mint fraudulent eWRs in your name.
- Coordinate physical pickups promptly when buyers confirm. The 3-day window starts at deliver().
Where to look next
- docs/rfc/RFC-005-buffer-and-po-settlement.md — the full settlement-layer mechanism specification.
- docs/spec/FULFILLMENT.md — physical-grain redemption mechanics.
- docs/guides/USER_GUIDE.md — end-to-end walkthrough including the trader and LP flows.
- docs/operations/SECURITY.md — the dispute and slashing invariants (INV-7 through INV-10).
- @okinawatrader/sdk-v4 — PurchaseOrderService, MatchingService, BufferService.
06OST Internal & Onboarding Guide
You work at OST or you just joined. This is your orientation document. It does not replace CLAUDE.md or the docs/ tree — those are the source of truth — but it is the quickest path from zero to your first merged PR.
What v4 is and what we work on
OkinawaTrader v4 is a non-custodial AMM and physical-settlement protocol for tokenized agricultural commodities. The repo is self-contained at okinawatrader-v4. Anything you read in legacy docs about "packages/contracts/v3" or "OkinawaTrader/docs/sprintNN" refers to the pre-consolidation monorepo we extracted from — follow in-tree paths instead.
There are three on-chain participants: Trader, Liquidity Provider, ADC Facility. The protocol has no operator and no admin keys; this is the load-bearing constraint that shapes every design decision.
Day one — get the repo running
| Step | What to do |
|---|---|
| 1 | Clone okinawatrader-v4 and run npm install at the repo root. |
| 2 | cd contracts && forge build && forge test — expect 311 tests passing. |
| 3 | npm run sdk:build then npm run website:dev — the app boots at localhost:3010. |
| 4 | npm run server:dev — the read-side cache boots at localhost:3011. |
| 5 | Read CLAUDE.md (project root), docs/README.md, docs/ARCHITECTURE.md, docs/AUDIT_REPORT.md, docs/guides/CONTRIBUTING.md. Roughly in that order. |
Repo layout
| Folder | What is in it |
|---|---|
| contracts/ | Foundry — Solidity 0.8.24, 28 source files, vendored forge-std. |
| sdk/ | @okinawatrader/sdk-v4 — viem clients + domain services. |
| server/ | Express read-side cache. No write paths, no auth. |
| subgraph/ | The Graph (AssemblyScript). Sepolia hosted-service. |
| website/ | Next.js 14 web app. wagmi + viem. |
| solver/ | Reference RFC-005 permissionless solver — currently a scaffold. |
| ops/ | Deploy scripts. Mostly placeholders pending Round-3. |
| docs/ | Canonical documentation tree. Auto-updated by the bot. |
| .github/ | CI workflows, PR/issue templates, kanban automation. |
| scripts/ | Repo-wide tools (currently: kanban mover + issue sync). |
How to pick up work
Stories have IDs of the form OST-DP-NNNN. They live in docs/kanban/ under three lanes: BACKLOG (not yet started), IN_PROGRESS (a PR is open), DONE (merged).
- Pull from BACKLOG.md sorted by priority (P0 → P3).
- Read the story’s acceptance criteria before writing any code.
- Create the branch: <type>/OST-DP-NNNN-slug, where type is feat / fix / chore / docs / refactor / test / spike.
Branch, commit, and PR conventions
Conventional Commits with the story ID embedded:
- feat(OST-DP-3456): wire Sentry to website error boundary
- fix(OST-DP-3478): bust server cache on redeploy
- docs(OST-DP-3300): write canonical RUNBOOK
PR body must include a Closes: line listing the story IDs the PR satisfies — the kanban bot reads this line to move stories on open and merge. Squash-merge to main; the squash commit becomes the [Unreleased] entry in docs/CHANGELOG.md.
Required CI checks
Run locally before opening a PR. CI runs the same set; branch protection on main requires them all green.
- Contracts: forge build, forge test, forge fmt --check, bash scripts/no-admin-detector.sh
- SDK: npm run sdk:build
- Server: npm --workspace server run typecheck
- Subgraph: npm --workspace subgraph run build
- Website: npm --workspace website run typecheck && npm --workspace website run build
- Solver: npm --workspace solver run typecheck
- Root: npm run lint && npm run format:check
Things we never do
- Never bypass the kanban. Every PR must reference at least one OST-DP-NNNN story via Closes:. If there is no story, file one first.
- Never edit docs/kanban/IN_PROGRESS.md or DONE.md by hand. The bot owns them; manual edits will conflict.
- Never add admin functions to contracts. no-admin-detector.sh will fail CI.
- Never weaken or remove an audit-regression test (test_F01_*, test_F02_*, test_F03_*). Doing so requires an audit-note follow-up.
- Never deploy to mainnet from a developer machine. Mainnet deploys go through the out-of-tree wrapper cli-mainnet-v4.sh plus the deploy-gate workflow.
- Never paste a private key into a config file. Use .env (gitignored); the templates are .env.example.
Solidity
- pragma solidity 0.8.24; exactly — no caret range.
- Custom errors, not require("string").
- NatSpec on every external function, state variable, and custom error.
- One contract per file (libraries colocate under libraries/).
- Reentrancy: use the vendored ReentrancyGuard in contracts/src/libraries/. No OZ.
- ERC20 calls: SafeTransfer from contracts/src/libraries/. No raw transfer / transferFrom.
TypeScript
- strict: true, noUncheckedIndexedAccess: true.
- No any. If a third-party type is wrong, narrow with a type alias and a // reason: comment.
- Workspace boundaries: sdk does not import from server / website / solver. The other direction is fine.
- ESLint + Prettier (printWidth: 100) at repo root, both gating CI. Pre-commit hooks run on staged files automatically.
Three audit fixes you should know about
The 2026-Q2 internal security audit flagged three Medium findings. All three are fixed in main and covered by regression tests. Do not remove or weaken these tests:
| ID | What it covers | Test |
|---|---|---|
| F-01 | PerpetualVault TWAP-zero funding avoidance via _lastGoodMarkTwap fallback | contracts/test/PerpetualVault.t.sol → test_F01_* |
| F-02 | Math.mulDiv phantom overflow — 512-bit intermediate rewrite | contracts/test/AuditRegression.t.sol |
| F-03 | wadLn upper bound (MAX_LN_INPUT, WadLnOverflow) | contracts/test/AuditRegression.t.sol |
Who to ask
- Architecture and on-chain questions: #protocol channel.
- Web app and SDK: #dev channel.
- "How do I get this PR through CI?" — #ci channel.
- Suspected security issue: email security@openstreet.consulting. Do not post in chat.
Where to look next
- CLAUDE.md — the canonical orientation doc for AI agents working in the repo. Kept short and accurate. Read it.
- docs/guides/CONTRIBUTING.md — every detail of the PR flow.
- docs/operations/RUNBOOK.md — day-2 operational procedures (env vars, restart, cache bust).
- docs/operations/INCIDENT_RESPONSE.md — what to do when something is on fire.
- docs/rfc/ — every protocol-changing decision starts here.
07Auditor & Researcher Guide
This document is for external auditors, security researchers, and regulators looking at OkinawaTrader v4. It is a router — every section points to the canonical source. The goal is to help you verify claims independently, not to make claims that you have to take on trust.
Where to start
| Question | Canonical source |
|---|---|
| What does v4 do, at a contract level? | docs/ARCHITECTURE.md |
| What is the current state of every layer? | docs/AUDIT_REPORT.md |
| What are the invariants and the threat model? | docs/operations/SECURITY.md |
| What is the compliance posture and controls register? | docs/operations/COMPLIANCE.md |
| Where is the most recent internal audit report? | docs/operations/audit/2026-Q2-internal/REPORT.md |
| Where is the change history? | docs/CHANGELOG.md + git log |
| Where are the design proposals? | docs/rfc/RFC-001 … RFC-005 |
| Where is the AMM math specified? | docs/spec/LMSR.md |
| Where is the physical-fulfillment workflow specified? | docs/spec/FULFILLMENT.md |
Trust model in one paragraph
No admin keys, no pause, no upgrade, no proxy. All parameters are set at construction and are immutable. Funds move only by their owner’s signature; the protocol has no transferFrom over user balances. The server and subgraph are read-only and untrusted; the website never holds keys; the reference solver holds a hot key but can only grief (submit gas-burning invalid matches), not drain funds. Fiat ⇄ USDC is delegated to a licensed third-party vendor — OST is not a VASP.
Verifying the no-admin claim yourself
The cleanest invariant. Run:
- cd contracts && bash scripts/no-admin-detector.sh
The script greps every file under contracts/src/ for any reference to Ownable, AccessControl, Pausable, selfdestruct, delegatecall, or proxy patterns and fails if any match. CI runs it on every PR; compromised CI would also have to push a no-admin-detector change to sneak admin code in. The git history of the script is the trust root.
Invariants register
SECURITY.md §Invariants enumerates ten on-chain invariants (INV-1 to INV-10) and four off-chain invariants (OINV-1 to OINV-4). Each cites the enforcing test or detector. The high-value ones to verify independently:
| ID | Property | Enforcement |
|---|---|---|
| INV-1 | No admin keys / destructive opcodes | scripts/no-admin-detector.sh (CI gate) |
| INV-2 | cDEP total supply is monotonically non-increasing after sale closes | cDEP.t.sol, cDEPEchidna.sol |
| INV-3 | PoolFactoryV4.createPool always burns the required cDEP | PoolFactoryV4.t.sol — inv_factoryv4_burn_charged_per_pool, inv_factoryv4_no_free_create |
| INV-5 | First LP of a factory pool is always cDEPSeedDistribution (atomic seed) | LMSRAMMPool.t.sol atomic-seed test |
| INV-6 | WarehouseReceiptRegistry mints exactly the declared kg | WarehouseReceiptRegistryV4.t.sol |
| INV-7 | Dispute slashes only if stakeForSlash > stakeAgainstSlash; zero-vote disputes never slash | Dispute.t.sol — note: no quorum floor today (OST-DP-3733) |
| INV-8 | PurchaseOrder.deliver / confirmPickup reject unsigned or wrong-signer EIP-712 attestations | PurchaseOrder.t.sol |
| INV-9 | ProtocolMaster cannot spawn beyond 2× allocation ceiling | ProtocolMaster.t.sol — inv_protocolmaster_2x_ceiling |
Audit history
| Date | Scope | Auditor | Outcome |
|---|---|---|---|
| 2026-04-22 | Internal audit pass — pre-atomic-seed bytecode | OST internal (Slither + Mythril + Echidna + bytecode-equivalence) | Pass |
| 2026-Q2 (in flight) | Atomic-seed redesign re-audit | Cantina | In flight; pending |
The internal audit pass is stale with respect to current bytecode — five base contracts changed in the atomic-seed redesign. The mainnet promote gate refuses to run until the Cantina re-audit is linked at HEAD (CC-2, OST-DP-3501).
Three Medium findings already fixed
These came out of the internal pass and are landed with regression coverage. The fixes themselves are short and reviewable:
| Finding | Patch location | Regression test |
|---|---|---|
| F-01 — PerpetualVault TWAP-zero funding avoidance | contracts/src/PerpetualVault.sol — _lastGoodMarkTwap fallback | contracts/test/PerpetualVault.t.sol → test_F01_* |
| F-02 — Math.mulDiv phantom overflow | contracts/src/libraries/Math.sol — 512-bit intermediate rewrite | contracts/test/AuditRegression.t.sol |
| F-03 — wadLn upper bound | contracts/src/libraries/FixedPointMath.sol — MAX_LN_INPUT, WadLnOverflow | contracts/test/AuditRegression.t.sol |
Reproducing the test suite
- cd contracts && forge build && forge test — should show 311 tests, all green (as of 2026-05-17).
- forge test --profile deep — extended fuzz / invariant runs. Recommended for serious review.
- forge coverage — coverage threshold not yet enforced; expect ~ near-full on production contracts.
- slither . — currently advisory in CI; OST-DP-3501 plans to move to gating.
- cd subgraph && npm run build && graph test — handler-level tests.
Off-chain trust boundary
| Component | Worst-case if compromised |
|---|---|
| server (read cache) | Stale / wrong data served. Cannot move funds. SDK reconciles material decisions against RPC. |
| subgraph (indexer) | Stale / wrong data served. Public; multiple indexers may exist. SDK can run RPC-only. |
| solver | Griefing — burn gas on invalid match submissions. Cannot drain user funds; matches reverted by contracts if inconsistent. |
| website | Phishing-grade UI. Cannot move funds because keys live in user wallet. Mitigation: bookmark okinawatrader.io; check addresses against contracts/deployments/. |
| fiat ramp vendor | Vendor-side compromise. OST does not custody fiat; vendor licensing is the regulatory perimeter. |
Disclosure
Email security@openstreet.consulting with a short description, reproduction steps or PoC, and your severity assessment. Acknowledgement within 48 hours; triage within five business days. Researchers who report material vulnerabilities are credited (with permission) in the CHANGELOG and the relevant audit report.
| Severity | Patch timeline | Public disclosure timeline |
|---|---|---|
| Critical (funds at risk, INV violation) | 72 hours | 14 days post-patch |
| High (degraded service, exfil possible) | 7 days | 30 days post-patch |
| Medium / Low | Rolled into the next release | In the changelog at the relevant release |
Compliance touchpoints worth checking
- CC-1 — no admin keys. Verified by scripts/no-admin-detector.sh on every PR.
- CC-2 — mainnet deploy gate. Verified by cli-mainnet-v4.sh + the independent CI gate OST-DP-3501.
- CC-3 — deployed addresses are canonical and public. Sync-check between deployments JSON and server (OINV-1, OST-DP-3475).
- CC-4 — user-fund custody is by signature only. Verified by forge-test assertions on every state transition.
- CC-5 — disclosure process published. See SECURITY.md.
- CC-6 — supply-chain audit. npm audit gating, Dependabot weekly.
- CC-7 — accepted-risks documented. SECURITY.md §Known accepted risks.
- CC-8 — fiat conversion delegated; OST not a VASP. website/lib/ramp.ts + vendor licensing record.
Open questions worth raising
- No quorum floor on Dispute votes (OST-DP-3733). Acceptable risk today; a planned RFC change.
- Solver double-submission of proposeMatch across restarts (OST-DP-3550). Persistent checkpoint pending.
- Subgraph manifest 0x0 addresses for not-yet-deployed contracts (OINV-2, OST-DP-3476).
- Website CSP still permits unsafe-inline (Next 14 limitation; OST-DP-3417 plans the nonce migration).
- Multi-RPC fallback configured but unproven under load (OST-DP-3470).
Where to look next
- Read docs/AUDIT_REPORT.md end-to-end — it is the densest summary.
- Read docs/operations/SECURITY.md — invariants and trust model.
- Read docs/operations/COMPLIANCE.md — CC-1 to CC-8 with evidence.
- Read docs/rfc/ — every design decision is recorded there.
- Run forge test --profile deep and bash scripts/no-admin-detector.sh — the two highest-leverage manual verifications.
08RFC Mechanisms — Smart-Contract Audit Perspective
This document explains the five active RFC mechanisms in terms an audit team needs to scope a review: what each RFC introduces, the contracts and functions touched, the invariants the design depends on, the attack surfaces worth ranking, and the existing test coverage. The canonical specifications live in docs/rfc/RFC-001 through RFC-005.
| Audit posture Every RFC carries §0 acceptance criteria that gate Accepted status; the list always includes "Cantina engaged for the v4 audit scope". Treat the §Mechanism subsections as the canonical scope statement. Anything not listed there is out of audit scope for that RFC. |
|---|
Contracts in scope
| Contract | Audit weight |
|---|---|
| cDEP.sol (ERC-20 + ERC-2612 Permit) | Light. Standard token. Only post-construction mutating path is burn() / burnFrom(). |
| cDEPSeedDistribution.sol (one-shot fair-launch sale) | Heavy. Atomic-seed redesign; sale → seed default pool → close. |
| PoolFactoryV4.sol (createPool gate) | Medium. Burn-then-deploy; immutable burnAmount; CREATE2 deploys. |
Critical invariants
- inv_cdep_supply_monotone_nonincreasing — cDEP total supply only decreases after seed sale closes. No mint path post-construction.
- inv_factoryv4_burn_charged — every PoolCreated decreases caller balance by exactly burnAmount; no path emits the event with less burned.
- inv_factoryv4_no_free_create — createPool always reverts if caller balance < burnAmount.
- inv_factoryv4_no_admin — no function on PoolFactoryV4 can change burnAmount or depToken.
- inv_seed_no_admin — no path changes sale price, per-address cap, or proceeds destination on the seed contract.
- inv_seed_proceeds_lp — every wei of seed USDC ends up in the default pool reserveB or is burned. No proceeds reach OST.
- inv_seed_lp_unwithdrawable — the LP shares minted by seedDefaultPool are sent to address(0) atomically; no withdraw against them succeeds.
Attack-surface checklist
- Permit-replay across burnFrom: confirm chainId, deadline, and nonce handling on cDEP.permit are EIP-2612 standard and not custom.
- Re-entrancy on createPool: pool deploy + burn must be atomic; verify ReentrancyGuard is on the right function or the order is mint-then-burn.
- Sale-window edge cases: buys at startTimestamp and endTimestamp boundaries; per-address cap math; division rounding on cdepAmount.
- Seed-pool seeding: cDEPSeedDistribution.seedDefaultPool() can be called exactly once; reverts if called twice; reverts pre-end.
- Unsold-supply burn: close() must zero out the seed-contract cDEP balance via address(0) transfer (not burn() callable by anyone).
- USDC accept-list: the constructor immutable USDC address is the only acceptable input — confirm no fallback or proxy path.
Bucket A (in audit scope) — on-chain pokes with bounties
Each recurring on-chain effect becomes a public, idempotent pokeX() function on a keeper contract with a permanent USDC endowment. The precedent is v3’s PerpetualVault.pokeFunding(). Audit weight: medium.
| Contract pattern | Audit notes |
|---|---|
| OracleKeeper.poke(pool) | Idempotent; minInterval gate; bountyToken balance check non-blocking. No setter for bountyAmount / minInterval / bountyToken. |
| MerkleAttestationKeeper.poke() | Computes a merkle root from a windowed event set; anchors via Attestation. |
| PoolHealthKeeper.poke() | Emits a HealthSampled event with reserves, last-trade-age, share-supply. |
Critical invariants (Bucket A)
- inv_keeper_idempotent — pokeX() before minInterval is a no-op; never double-applies the side effect.
- inv_keeper_no_admin — no setter changes bountyToken, bountyAmount, or minInterval; new keeper deploys to change them.
- inv_keeper_endowment_only_paid_via_poke — the bounty token can only leave the keeper through poke()’s bounty path.
Bucket B / C are out of audit scope
Bucket B (pg_cron + HMAC webhook) and Bucket C (attestation-anchored deploys) live off-chain and are reviewed as ops surface, not contract surface. Bucket C does emit on-chain attestations via the existing Attestation.sol — that contract’s anchor() path is in scope as part of RFC-006 trust layer.
Mechanism
ProtocolMaster.spawn(params) deploys an isolated v4 protocol stack — fresh cDEP, cDEPSeedDistribution, PoolFactoryV4, and default LMSRAMMPool — in one tx, then transfers the mint authority to the seed contract and emits ProtocolSpawned. The master itself is deployed via Arachnid’s singleton factory at 0x4e59…4956C (RFC-004) so any wallet can deploy it at a deterministic, publicly-verifiable address.
Critical invariants
- inv_master_no_admin — no path mutates state on a spawned protocol after ProtocolSpawned.
- inv_master_2x_ceiling (INV-9) — ProtocolMaster cannot spawn beyond the 2× allocation ceiling enforced by ProtocolMaster.t.sol.
- inv_master_no_collision — protocolId is unique (computed from params + creator + spawnedAt); collisions revert.
- inv_master_size_eip170 — the master contract bytecode is under the 24,576-byte EIP-170 limit. Confirm empirically.
Attack-surface checklist
- Creation-code bundling: the master holds the creation bytecode of cDEP / seed / factory; ensure the embedded code matches the audited source 1:1 (bytecode hash check at construction).
- USDC address binding: master.usdc immutable must match the canonical USDC; otherwise spawned protocols silently bind to a malicious token.
- Spawn-frontrunning: an attacker frontrunning a user’s spawn() with the same params should fail (params include creator implicitly or via salt).
- Singleton-factory pre-image: confirm the singleton factory at 0x4e59…4956C is the canonical Arachnid factory and not a fork.
Contracts in scope (heavy)
| Contract | Role |
|---|---|
| BufferRegistry | Per-(ADC, grade) FIFO inventory ledger; ADC-signed attestations; permissionless slashAttestationDivergence (25% threshold, 10% slash, 2% bounty). |
| SupplierRegistry | Permissionless registry of supplier offers under USDC delivery bonds. OFFER_TTL = 30 days; bond locked at match; released or slashed at outcome. |
| PurchaseOrder | Buyer USDC escrow, match → deliver → pickup-or-conversion state machine. Multi-fill (per-fill bond, per-fill pickup window, per-fill terminal status). |
| BuyerEWR / SellerEWR (soulbound) | Non-transferable ERC-721 proof-of-completion at pickup. Records, not gates. |
Critical invariants
- inv_buffer_matches_cblg_supply — total cBLG supply equals total active-eWR kg at every block; perpetuals never back physical grain.
- INV-7 — Dispute slashes only when stakeForSlash > stakeAgainstSlash; zero-vote dispute never slashes. (No quorum floor today — OST-DP-3733.)
- INV-8 — PurchaseOrder.deliver / confirmPickup reject unsigned or wrong-signer EIP-712 attestations.
- INV-10 — SupplierRegistry releases bonded USDC iff offer state is Withdrawn, Expired, or Filled.
- inv_match_twap_floor — proposeMatch reverts unless offer.priceUsdcPerKg ≥ cblgPool.twapMidPerKg() (RFC-005 §7-F).
- inv_swap_twap_tolerance — convertAfterWindow’s auto-swap reverts when pool mark diverges from TWAP by more than 5% (SWAP_TWAP_TOLERANCE_BPS).
- inv_buffer_attestation_freshness — slashAttestationDivergence requires attestation between MIN_ATTESTATION_DELAY (24h) and MAX_ATTESTATION_AGE (7d).
- inv_fill_termination — every Fill reaches one of {PickedUp, Converted, Defaulted}; no fill remains Locked or Delivered after PO.deadline + grace.
Attack-surface checklist
- Re-entrancy on convertAfterWindow: buyer.cBLG → AMM swap → perp deposit chain must be atomic and reentrancy-guarded.
- TWAP manipulation: confirm the 24h TWAP source is the pool’s existing TWAP (same as PerpetualVault funding) — no new oracle.
- Solver griefing: proposeMatch is permissionless; verify that submitting an invalid match merely reverts (gas-burn only), never partially mutates PO state.
- Per-fill accounting: unmatchedKgOf, deliveredKg, matchedKg must reconcile after every state transition — invariant fuzzing recommended.
- FIFO queue corruption: BufferRegistry uses a doubly-linked list of eWR IDs; partial-drain corner cases must keep prev/next consistent at O(1).
- eWR re-enqueue on conversion: §3.6.1 re-enqueues the converted lot at its original position. Verify mintedAt anchor stability.
- Bond burn vs. bounty: default branch splits bond into bounty (0.5%) and burn (rest). Verify total = bond exactly; no rounding leak.
- Dual-receipt minting: confirmPickup mints soulbound buyerEWR + sellerEWR atomically with burns; partial-failure must revert all.
- openFromShortage callability: only WarehouseReceiptRegistry can call this; verify the immutable caller check.
No-admin invariant (INV-1)
contracts/scripts/no-admin-detector.sh greps every source file under contracts/src/ for Ownable, AccessControl, Pausable, selfdestruct, delegatecall, and proxy patterns; any match fails CI. Verify the detector script itself has not been weakened in git history. The detector is the load-bearing line of defence on this property.
Atomic-seed redesign
Five base contracts changed bytecode in the atomic-seed redesign. The internal audit is stale with respect to those bytecodes. Mainnet promote refuses to run until a Cantina audit at HEAD is linked (CC-2 + OST-DP-3501). Focus the re-audit on the changed contracts: cDEP, cDEPSeedDistribution, LMSRAMMPool, PoolFactoryV4, MasterCoDeployer.
Three Medium findings already fixed
| ID | Patch | Regression test |
|---|---|---|
| F-01 (PerpetualVault TWAP-zero) | contracts/src/PerpetualVault.sol — _lastGoodMarkTwap fallback | PerpetualVault.t.sol → test_F01_* |
| F-02 (Math.mulDiv overflow) | contracts/src/libraries/Math.sol — 512-bit intermediate rewrite | AuditRegression.t.sol |
| F-03 (wadLn upper bound) | contracts/src/libraries/FixedPointMath.sol — MAX_LN_INPUT, WadLnOverflow | AuditRegression.t.sol |
Test footprint
- 311 forge tests across 16 suites, all green as of 2026-05-17.
- Two Echidna invariant suites: cDEPEchidna and ProtocolMasterEchidna.
- SwapRouter.sol has no direct test file (covered indirectly through swap-path tests). Recommend adding direct coverage.
- forge test --profile deep — extended fuzz / invariant runs. Recommended for the audit pass.
Recommended audit workflow
- Run scripts/no-admin-detector.sh against the candidate branch as the first sanity check.
- Re-run forge test and forge test --profile deep; compare against the 311-test baseline.
- Scope review by RFC: RFC-001 + atomic-seed deltas first; then RFC-005; then ProtocolMaster (RFC-003) and singleton-factory (RFC-004) if those land in the same release.
- Cross-check every §Invariants section against the named forge / Echidna test. Missing or stale tests are findings.
- Independent slither + mythril runs; both are currently advisory in CI. Promote findings to gating per OST-DP-3501 timeline.
- Coordinated disclosure per docs/operations/SECURITY.md (Critical 72h, High 7d, Medium next release).
References
- docs/rfc/RFC-001-v4-pool-factory-gate.md
- docs/rfc/RFC-002-decentralize-scheduler.md
- docs/rfc/RFC-003-protocol-master-spawn.md
- docs/rfc/RFC-004-singleton-factory-deploy.md
- docs/rfc/RFC-005-buffer-and-po-settlement.md
- docs/operations/SECURITY.md — invariants register and trust model.
- docs/operations/audit/2026-Q2-internal/REPORT.md — most recent internal pass.
09RFC Mechanisms — Banking & Settlements Compliance Perspective
This document presents the five active RFCs to a banking and financial-settlements compliance audience. The objective is to map each mechanism onto the questions a compliance reviewer asks: who holds the money, who can move it, what is the finality model, who arbitrates disputes, what is the regulated perimeter, and where are the documented controls.
| OST posture in one paragraph OkinawaTrader v4 is non-custodial software. OST does not hold user funds, does not custody fiat, does not operate as a VASP or money-services business, and cannot pause, upgrade, or alter the protocol after deployment. The regulated perimeter is the OST-operated frontend and the third-party fiat ramp vendor — not the protocol. See docs/operations/COMPLIANCE.md for the CC-1 to CC-8 controls register. |
|---|
What it is
RFC-001 introduces a utility token (cDEP) that callers must burn to create a new pool on the protocol. The burn destroys value to address(0); no proceeds flow to OST. cDEP is minted once by a fair-launch sale contract; OST holds zero at genesis; unsold supply is burned at window close.
Compliance-relevant properties
| Question | Answer |
|---|---|
| Who holds cDEP at genesis? | The cDEPSeedDistribution contract. Not OST. |
| How is cDEP distributed? | A fixed-window, fixed-price USDC sale with a per-address cap. No Dutch auction, no bonding curve, no oracle dependency. |
| Where do sale proceeds go? | Paired with the matching cDEP supply and deposited as permanent LP into the default (cDEP, USDC) pool. LP shares are burned to address(0). Proceeds do not reach OST. Invariant: inv_seed_proceeds_lp. |
| Can the sale terms be changed after deploy? | No. Window, price, per-address cap, and proceeds destination are immutable. |
| Does cDEP confer governance, revenue, or claims? | No. cDEP has no voting power, no protocol-parameter authority, and no claim on OST or any entity. |
| Is OST an issuer? | OST deploys the contracts. After deploy, OST has no ongoing role; counsel-signed §5 of RFC-001 captures the analysis. |
Counsel-signed gates
- Howey / post-Coinbase analysis of cDEP must be captured in DECENTRALIZATION_MEMO_v4_ADDENDUM.md before mainnet.
- Fair-launch sale must be reviewed as a possible offering — RFC-001 §5.2 lists the specific items.
- OST LP participation cap (off-chain commitment, ≤ 10% of LP shares on the default pool) must be addressed in the addendum.
- CFTC commodities-overlap analysis: cDEP is not a derivative, but it gates creation of pools over commodity tokens.
What it is
RFC-002 replaces OST’s implicit reliance on GitHub Actions as a privileged scheduler with three buckets: (A) permissionless on-chain pokeX() functions with bounties, (B) HMAC-gated webhooks driven by pg_cron in OST’s Postgres, (C) attestation-anchored one-shot deploys. Legacy operator-run paths (IFSCA, FEMA, OFAC, dual-sign clearing, Banking-Partner reconciliation) are deleted, not migrated.
Compliance-relevant properties
- Bucket A: anyone may invoke keeper functions; bounty is paid from a permanent endowment seeded once at deploy. No setter for any keeper parameter. No party — OST or otherwise — controls execution.
- Bucket B: trust shifts from GitHub-the-company to OST’s Postgres. Single point, but one OST controls and any third party can replicate.
- Bucket C: deploys remain signed by a deployer wallet; every deploy emits an on-chain attestation binding source-tree hash to deployed bytecode hashes. The audit trail is on-chain.
- Retired paths: pre-pivot operator routes (FETERS / FEMA / IFSCA / OFAC rescreen / dual-sign clearing) are deleted as part of the cleanup — they are no longer in the source tree.
What it is
RFC-003 introduces ProtocolMaster.spawn(params), a permissionless contract that lets any address deploy a fresh isolated v4 protocol stack in one transaction. RFC-004 refines how the master itself is deployed — via Arachnid’s deterministic singleton factory at 0x4e59…4956C — so any wallet can deploy the master at a publicly-verifiable address.
Compliance-relevant properties
- After ProtocolMaster is deployed, OST no longer needs to deploy protocol instances. Anyone can.
- Each spawned protocol is fully isolated — its own cDEP, seed sale, factory, default pool — with no shared state across spawns.
- The master is immutable. The 2× allocation ceiling (INV-9) is enforced on every spawn.
- OST is removed as an ongoing deployer; the only deploy event is the master itself (one tx).
- The OST-operated frontend chooses which spawned protocols to surface. Curation is a frontend control, not a protocol control.
What it is
RFC-005 introduces a permissionless purchase-order layer for physical forward delivery of commodity, alongside the AMM. Buyers post USDC escrow; suppliers post a USDC delivery bond and an offer; permissionless solvers match offers to POs; ADCs perform automated QA and sign deliveries on-chain. Every non-pickup branch auto-converts to a PerpetualVault long so the buyer always retains exposure and inventory is never permanently encumbered.
Custody model
| Asset | Custodian during the workflow |
|---|---|
| Buyer USDC | PurchaseOrder contract escrow from open() until per-fill terminal status. Releases to supplier on confirmPickup, or to PerpetualVault on conversion. |
| Supplier delivery bond | SupplierRegistry contract from registerOffer until offer terminal (Withdrawn / Expired / Filled). Released on success; bond - bounty is burned on supplier default. |
| eWR (electronic warehouse receipt) | ADC-minted; held by buyer post-delivery; burned at pickup or flagged "converted" on auto-conversion. |
| Physical grain | ADC at all times. Buffer-stock attestations track inventory; total cBLG supply equals total active-eWR kg (invariant). |
| LP shares of the default pool | cDEPSeedDistribution holds them; sent to address(0) atomically after seed deposit. Unwithdrawable. |
Settlement finality
- On-chain finality: every state transition (open, proposeMatch, deliver, confirmPickup, convertAfterWindow, cancel) is atomic and final on inclusion in a confirmed block.
- Physical finality: pickup is the off-chain physical-grain hand-off; the on-chain confirmPickup attestation by the ADC is the protocol-level evidence of completion.
- 3-day pickup window: a fixed PICKUP_WINDOW = 3 days from deliver(); after this the cBLG is auto-swapped through the AMM and proceeds deposited into a perpetual long.
- Soulbound BuyerEWR / SellerEWR are non-transferable records — they exist for audit, tax, and dispute evidence; they do not gate any subsequent action.
Dispute and slashing
- Dispute.sol — stake-weighted bonded voting. INV-7: slashes only when stakeForSlash > stakeAgainstSlash. No quorum floor today (OST-DP-3733).
- Auto-slash for buffer divergence (RFC-005 §7-D): permissionless slashAttestationDivergence; threshold 25% over a fresh attestation between 24h and 7 days old; 10% of facility bond slashed, 2% to caller, remainder burned.
- Supplier default (failure to deliver before deadline): bond minus 0.5% bounty is burned; offer marked inactive permanently.
- No central arbitrator. No regulator-style operator. Disputes are deterministic outcomes of bonded votes.
What it is
The OST web app surfaces fiat on-ramp and off-ramp widgets that connect to a licensed third-party vendor (Transak, Onmeta, or Onramp.money). OST never custodies fiat, does not hold fiat balances, and is not a VASP or MSB.
Compliance-relevant properties
- Fiat ⇄ USDC conversion is delegated to the vendor; the vendor holds money-transmitter / VASP licensing.
- On-ramp: card / bank payment goes directly to the vendor; vendor releases USDC to user’s wallet.
- Off-ramp: a per-transaction deposit address is fetched server-side from the vendor’s sell-order API (so the vendor API key never reaches the browser); the on-chain leg is an ordinary user-signed USDC transfer to that deposit address. A static fallback (NEXT_PUBLIC_OFFRAMP_DEPOSIT_ADDRESS) is for testnet / demo only.
- KYC for fiat conversion is the vendor’s responsibility. OST collects no payment data.
- Tax disclosure (e.g. India 30% gains + 1% TDS) is informational only; OST does not withhold, remit, or report tax.
- On Sepolia the on-ramp mints test USDC from the MockUSDC faucet; no fiat involved on testnet.
Sanctions, OFAC, and KYC
| Concern | Control |
|---|---|
| Sanctioned-jurisdiction access (frontend tier) | IP geolocation at session start on okinawatrader.io; UI load blocked from sanctioned jurisdictions. |
| Sanctioned-wallet screening (frontend tier) | OFAC SDN list checked via Chainalysis oracle or equivalent — vendor selection tracked under OST-DP-3610. |
| Blocked-attempt audit trail | Logged with no PII (wallet address + jurisdiction code); 7-year retention per regulatory requirement. |
| Protocol-tier sanctions | None. The contracts are permissionless. OST does not represent that the protocol is unavailable to sanctioned users — OST represents that the OST-operated frontend is. |
| KYC at protocol tier | None. The contracts have no caller-identity gate. |
| KYC at ADC tier | Off-chain: ADCs wishing to appear in OST’s curated directory submit a KYC pack and post a USDC bond. The contract registration itself is permissionless. |
Controls register (CC-1 to CC-8)
| ID | Control | Enforcement |
|---|---|---|
| CC-1 | No admin keys in any contract. | no-admin-detector.sh runs in CI on every PR; failure blocks merge. |
| CC-2 | Mainnet deploy gated on RFC Accepted + counsel sign-off + Cantina audit at HEAD. | cli-mainnet-v4.sh + independent CI gate OST-DP-3501. |
| CC-3 | Deployed addresses are canonical and public. | OINV-1 sync-check between deployments JSON and server (OST-DP-3475). |
| CC-4 | User-fund custody by signature only; no protocol-level transferFrom over user balances. | Forge-test assertions on every state transition that moves user-held tokens. |
| CC-5 | Vulnerability disclosure — 48h ack, 5-business-day triage. | SECURITY.md published; INCIDENT_RESPONSE.md procedure. |
| CC-6 | Software supply chain — vendored Solidity deps, pinned JS deps, weekly Dependabot. | npm audit in CI; Dependabot in .github/dependabot.yml. |
| CC-7 | Accepted protocol risks enumerated. | SECURITY.md §Known accepted risks. |
| CC-8 | Fiat conversion delegated; OST is not a VASP / MSB. | website/lib/ramp.ts builds vendor widget URLs and a user-signed USDC transfer. Vendor licensing record is the audit trail. |
Data retention
- Server logs (no body) — 30 days, Vercel-managed.
- Subgraph state — public, indefinite, in The Graph hosted service.
- Solver logs (pino structured) — 30 days.
- CSP report-uri logs — 90 days.
- Compliance logs (sanctions / OFAC blocks) — 7 years.
- No user PII collected. The frontend takes a wallet address; no email, no name, no payment data.
- Fiat payment data, where collected for ramp, is held by the third-party vendor — never by OST.
Auditability
Every claim above is independently verifiable. Recommended workflow for a compliance reviewer:
- Verify CC-1 by running contracts/scripts/no-admin-detector.sh against the candidate branch.
- Verify CC-3 by diffing contracts/deployments/<env>.json against Etherscan-verified addresses for each contract.
- Verify CC-8 by reading website/lib/ramp.ts and the vendor’s public licensing record.
- Verify the no-quorum-floor disclosure (INV-7, OST-DP-3733) by reading Dispute.sol and confirming the test file.
- Verify settlement finality by tracing a single PO through the contract events on Etherscan / the subgraph.
- Independent retrieval of counsel-signed artifacts (DECENTRALIZATION_MEMO_v4_ADDENDUM.md and counsel opinions tracked under OST-DP-3601 / OST-DP-3602) is required for mainnet promotion; the documents live in OST’s governance repository, not this one.
References
- docs/operations/COMPLIANCE.md — CC-1 to CC-8 register, RFC acceptance gates, retention policies.
- docs/operations/SECURITY.md — invariants, trust model, accepted risks, disclosure timeline.
- docs/rfc/RFC-001 § Posture / legal preconditions — counsel sign-off scope.
- docs/rfc/RFC-005 § Mechanism — settlement custody and finality model.
- docs/operations/audit/2026-Q2-internal/REPORT.md — most recent internal pass.
10RFC Mechanisms — Engineering Perspective
This document explains the five active RFC mechanisms from an engineering perspective: the contracts each RFC introduces, the function signatures, the events you can index, where the code lives, where to integrate from the SDK and the website, and how to test each path. The canonical specifications are in docs/rfc/.
| Where to find each RFC RFC-001 — docs/rfc/RFC-001-v4-pool-factory-gate.md • RFC-002 — RFC-002-decentralize-scheduler.md • RFC-003 — RFC-003-protocol-master-spawn.md • RFC-004 — RFC-004-singleton-factory-deploy.md • RFC-005 — RFC-005-buffer-and-po-settlement.md. |
|---|
Mechanism in one paragraph
PoolFactoryV4.createPool burns a fixed amount of cDEP (immutable burnAmount) from the caller, then deploys an LMSRAMMPool via MasterCoDeployer (CREATE2). cDEP is minted once into cDEPSeedDistribution, which sells it for USDC over a fixed window and pairs the proceeds with the matching cDEP into a permanent LP position on the default (cDEP, USDC) pool. LP shares are sent to address(0). After the window, unsold supply is burned.
Contracts and signatures
| Contract | Key surface |
|---|---|
| cDEP.sol | constructor(seedDistribution, initialSupply); burn(amount); burnFrom(account, amount); permit(...); standard ERC-20. |
| cDEPSeedDistribution.sol | buy(cdepAmount) external; seedDefaultPool() external — callable once after endTimestamp; close() external — burns unsold supply. |
| PoolFactoryV4.sol | createPool(tokenA, tokenB, b, feeBps) external returns (LMSRAMMPool); allPools() view; poolFor(tokenA, tokenB) view. Emits PoolCreated. |
Where to integrate
- SDK — sdk/src/clients/PoolFactoryV4Client.ts and cDEPClient.ts (read + write).
- Website — app/pools/new (pool creation form) wires PoolFactoryV4Client.createPool through wagmi. The 501 stub is OST-DP-3100.
- Sale UI — app/sale + app/api/sale/snapshot. Buy path is OST-DP-3140.
- Subgraph — base PoolFactoryV4.PoolCreated handler is wired; per-pool Swap / Deposit / Withdraw handlers are wired.
Testing
- forge test --match-path test/cDEP.t.sol and cDEPEchidna for invariant fuzz.
- forge test --match-path test/PoolFactoryV4.t.sol — inv_factoryv4_burn_charged_per_pool, inv_factoryv4_no_free_create.
- forge test --match-path test/cDEPSeedDistribution.t.sol — sale window, per-address cap, seed-pool deposit atomicity.
- forge test --match-path test/LMSRAMMPool.t.sol — atomic-seed test (first LP is always cDEPSeedDistribution).
Bucket A — on-chain pokes
Pattern: a contract exposes pokeX() — public, idempotent, returns no-op before minInterval, pays a bounty from a permanent USDC endowment when the work actually runs. Precedent: PerpetualVault.pokeFunding(). No setter for bountyAmount, minInterval, or bountyToken — deploy a new keeper to change them.
| Today’s job | Becomes | Notes |
|---|---|---|
| oraclePricePush | OracleKeeper.poke(pool) per pool | Pushes TWAP sample. |
| attestationMerkleContinuous | MerkleAttestationKeeper.poke() | Anchors a windowed merkle root via Attestation.sol. |
| poolHealth | PoolHealthKeeper.poke(pool) | Emits HealthSampled — indexer consumer. |
| liquidityTopUp | (deleted) | Fundamentally user-driven; LPs call pool.deposit() themselves. |
Bucket B — pg_cron + HMAC webhook
pg_cron jobs running inside OST’s Postgres call HMAC-gated internal routes on server/. The HMAC secret lives in env; the webhook handler lives under server/src/internal/. Trust shifts from GitHub the company to OST’s Postgres — a single point OST controls.
Bucket C — attestation-anchored deploys
Deploys remain human-triggered and signed by a deployer wallet. Every deploy script emits a post-deploy attestation through Attestation.sol binding the source-tree hash (computed in CI) to the deployed contract addresses and bytecode hashes. The on-chain attestation is the trust root — GitHub uptime stops being load-bearing.
Mechanism
ProtocolMaster.spawn(SpawnParams) deploys cDEP + cDEPSeedDistribution + PoolFactoryV4 + default LMSRAMMPool in a single transaction, then emits ProtocolSpawned(protocolId, creator, token, seedDistribution, poolFactory, defaultPool, params). The master itself is deployed via Arachnid’s singleton factory at 0x4e59b44847b379578588920cA78FbF26c0B4956C so any wallet can deploy to a deterministic address.
SpawnParams fields
- name / symbol — cDEP name and symbol suffix (e.g. "Yellow Maize" / "cMZE-DEP").
- initialSupply, saleStartTimestamp, saleEndTimestamp, priceUsdcPerCdep, maxPerAddress, saleAllocation.
- poolBHint — LMSR liquidity constant for the default pool.
- poolFeeBps, factoryBurnAmount — pool fee tier and burn cost per createPool on the spawned factory.
Engineering notes
- EIP-170 byte budget: ProtocolMaster bundles creation-code for 4 contracts. Confirm bytecode size empirically (Q-001 in RFC-003 §7).
- Bytecode-hash check at construction so the master only deploys the exact audited source.
- protocolId is deterministic given (params + creator + spawnedAt). Collisions revert.
- The OST web app surfaces only listed protocols; curation is in ops/protocol-directory.json (frontend control, not protocol control).
Contracts
| Contract | Key responsibilities |
|---|---|
| BufferRegistry.sol | Per-(ADC, gradeID) FIFO inventory ledger. Doubly-linked list of eWR IDs with per-lot remainingKg. ADC-signed attestBuffer. Permissionless slashAttestationDivergence (25% threshold). |
| SupplierRegistry.sol | Permissionless offer registry under USDC delivery bond. OFFER_TTL = 30 days. Bond locks on match, releases or slashes at outcome. |
| PurchaseOrder.sol | Buyer escrow + multi-fill state machine: Open / PartiallyMatched / FullyMatched / Settling / Settled / Cancelled at PO level; Locked / Delivered / PickedUp / Converted / Defaulted per Fill. |
| BuyerEWR.sol / SellerEWR.sol | Soulbound (non-transferable) ERC-721 proof-of-completion receipts. |
Constants worth knowing
- PICKUP_WINDOW = 3 days (per-fill, immutable).
- DEFAULT_BOUNTY_BPS = 50 (0.5%) — supplier-default bounty to caller of cancel().
- CONVERT_BOUNTY_BPS = 10 (0.1%) — pickup-window-expiry bounty to caller of convertAfterWindow.
- MATCH_BOUNTY_BPS = 5 (0.05%) — solver bounty per filled slice.
- SWAP_TWAP_TOLERANCE_BPS = 500 (5%) — auto-swap reverts if pool mark below TWAP by more.
- ATTESTATION_DIVERGENCE_BPS = 2500 (25%), SLASH_FRACTION_BPS = 1000 (10%), ATTESTATION_SLASH_BOUNTY_BPS = 200 (2%).
- MIN_ATTESTATION_DELAY = 24h, MAX_ATTESTATION_AGE = 7 days.
Function surface (PurchaseOrder)
- open(gradeID, kg, adc, deadline, priceUsdc) — buyer-initiated, escrows USDC.
- openFromShortage(redeemer, gradeID, kg, adc, deadline, priceUsdc) — only callable by WarehouseReceiptRegistry.redeem when buffer is short.
- proposeMatch(poID, offerID, fillKg, solverNonce) — permissionless solver entry; one call per fill slice.
- deliver(poID, fillIndex, adcQaAttestation) — only the named ADC; per-fill.
- confirmPickup(poID, fillIndex) — only the named ADC; per-fill within pickup window.
- convertAfterWindow(poID, fillIndex) — permissionless after a fill’s pickupDeadline.
- cancel(poID) — permissionless after PO deadline; per-fill default + unmatched-residual refund + zero-fill refund.
Event taxonomy (subgraph wiring)
- BufferRegistry: BufferCredited, BufferDebited, BufferAttested.
- SupplierRegistry: OfferRegistered, OfferWithdrawn, OfferExpired, BondLocked, BondReleased, BondSlashed.
- PurchaseOrder: POOpened, FillLocked, FillDelivered, FillPickedUp, FillConverted, FillDefaulted, POCancelled, POSettled.
- Subgraph handler stubs tracked under OST-DP-3200, OST-DP-3210, OST-DP-3220 (and OST-DP-3333..3335 per AUDIT_REPORT).
Where to integrate
- SDK — sdk/src/services/PurchaseOrderService.ts (buyer flows), MatchingService.ts (solver flows), BufferService.ts (ADC flows).
- Website — app/settlement/ (UI) + app/api/settlement/* (10 routes currently 501; tracked under OST-DP-3300 / OST-DP-3323..3330).
- Server — settlement read routes are 501 stubs awaiting subgraph handlers. No write paths.
- Solver — watcher / matcher / submitter are stubs (OST-DP-3350 / 3351 / 3352). Persistent checkpoint pending (OST-DP-3550).
Testing
- forge test --match-path test/BufferRegistry.t.sol — FIFO queue, attestation slashing.
- forge test --match-path test/SupplierRegistry.t.sol — bond lifecycle (INV-10).
- forge test --match-path test/PurchaseOrder.t.sol — multi-fill state machine, EIP-712 signature checks (INV-8).
- forge test --match-path test/Dispute.t.sol — bonded-vote outcomes (INV-7); note no quorum floor (OST-DP-3733).
Repo map for engineers
| Layer | Path | Status |
|---|---|---|
| Contracts | contracts/src/ | Foundry, Solidity 0.8.24, 28 source files, 311 tests |
| SDK | sdk/ | @okinawatrader/sdk-v4, viem-based, hand-written ABIs |
| Server | server/ | Express read-side cache; no writes; no auth |
| Subgraph | subgraph/ | The Graph (AssemblyScript), Sepolia hosted-service |
| Website | website/ | Next.js 14, wagmi + viem |
| Solver | solver/ | Reference RFC-005 permissionless solver (scaffold) |
| Ops | ops/ | Deploy scripts; mostly placeholders pending Round-3 |
| Kanban | docs/kanban/ | BACKLOG / IN_PROGRESS / DONE — bot-managed |
Required CI on every PR
- contracts: forge build, forge test, forge fmt --check, bash scripts/no-admin-detector.sh.
- sdk: npm run sdk:build.
- server: npm --workspace server run typecheck.
- subgraph: npm --workspace subgraph run build.
- website: npm --workspace website run typecheck && npm --workspace website run build.
- solver: npm --workspace solver run typecheck.
- root: npm run lint && npm run format:check.
- audit: npm audit --omit=dev --audit-level=high.
- Optional: forge test --profile deep, slither ., website vercel build, playwright E2E (post-OST-DP-3490).
Things engineers should know
- Custom errors, not require("string"). NatSpec on every external function, state variable, and custom error.
- Use the vendored ReentrancyGuard in contracts/src/libraries/ — do NOT import OpenZeppelin.
- Use SafeTransfer from contracts/src/libraries/ — do NOT call raw .transfer / .transferFrom.
- Workspace boundaries are enforced: sdk/ does not import from server / website / solver.
- Every PR must reference at least one OST-DP-NNNN story via Closes: — the bot reads this to move the kanban.
- Never deploy to mainnet from a developer machine. The path is cli-mainnet-v4.sh + the deploy-gate workflow (OST-DP-3501).
- Three audit-regression test blocks (test_F01_*, test_F02_*, test_F03_*) must not be weakened. Removing one requires a follow-up audit note.
Where to look next
- CLAUDE.md — the canonical orientation doc for AI agents working in the repo.
- docs/ARCHITECTURE.md — protocol-level overview.
- docs/guides/CONTRIBUTING.md — branch / commit / PR conventions.
- docs/spec/LMSR.md — the AMM cost-function math (true LMSR, not CPMM, as of spike OST-DP-3010 / 2026-05-16).
- docs/spec/FULFILLMENT.md — physical-grain redemption mechanics.
- docs/operations/RUNBOOK.md — day-2 operational procedures (env vars, restart, cache bust).
- docs/operations/INCIDENT_RESPONSE.md — declare → assemble → triage → mitigate.