The pair is a dollar
A market is a vertical spread: one underlying, a lower strike, an upper strike and an expiry. It issues two coins against one dollar of USDG.
split 1 USDG -> 1 call coin + 1 put coin
merge 1 call + 1 put -> 1 USDG
Both doors are open to anybody, at par, at any time, including after expiry. That is what pins the two prices together. If the pair trades under a dollar, buying both and merging is free money; if it trades over a dollar, splitting and selling both is. Nothing else has to enforce it and no market maker has to be paid to.
Because coins are only ever minted in pairs and burned in pairs, the two supplies are always equal and the collateral behind them is always exactly the supply. There is no fractional anything here.
What each coin pays
With S the underlying at expiry and the strikes k1 < k2:
f = clamp((S - k1) / (k2 - k1), 0, 1)
call coin pays f dollars
put coin pays 1 - f
The call coin is a call spread struck k1/k2 with its payoff normalized to a dollar; the put coin is the put spread that is exactly its complement. A plain call would have no ceiling on what its writer owes, which is why plain calls need margin and liquidations. Capping the payoff at k2 caps the liability at a dollar, so the collateral can simply be posted up front and the entire machinery of margin disappears.
That is also what makes it a coin. A token that can pay an unbounded amount cannot be fully backed. One that pays at most a dollar can be, and this one always is.
The pool holds coins, not cash
Between the two sides sits a constant product pool holding call coins and put coins and no dollars at all. Its price for the call side is the reserve ratio:
price(call) = reservePut / (reservePut + reserveCall)
price(put) = 1 - price(call)
Which means a price here reads as a number rather than a quote: a call coin at 31¢ is the pool saying this spread finishes 31% of the way from the lower strike to the upper one.
Dollars get in and out through the split and the merge. A buy splits your dollars into a pair, keeps the side you asked for and sells the other leg into the pool. A sell pushes your coins in, takes the other side out and merges the pair back into dollars. The pool never holds USDG, so it can never be drained of it.
The exact arithmetic
buy x dollars of the call side, fee taken first:
out = (rCall + net) - ceil( rCall * rPut / (rPut + net) )
sell y call coins:
a = rCall + y, b = rPut
out = ( (a + b) - sqrt( (a + b)^2 - 4*y*b ) ) / 2
Both are rounded the pool's way. app/engine.js runs the same integers
in the browser that OptionMarket.sol runs on chain, and the two test
suites hold them to the same numbers rather than to two similar descriptions of
them.
Expiry
At or after the expiry anybody can call settle(). It reads the
Chainlink feed once, fixes f, and that is the end of the discretion in
this system: there is no price setter, no admin override and no dispute window,
because there is nothing to dispute.
If the feed is stale or unavailable the market stays unsettled, and holders can still merge a call and a put back into a dollar at par. A broken oracle cannot trap the collateral, because the collateral was never anywhere except the market's own balance.
Because the two supplies are equal, the two payouts add back to exactly the
collateral held: T·f + T·(1-f) = T. Redemption is first
come, and nobody's redemption can fail for lack of the money.
What you need in your wallet
Strikepad is not a venue you deposit into. There is no account, no balance held on your behalf and no withdrawal to wait for: every button on the site sends a transaction from your own wallet, and the money moves straight from you to a contract. So two things have to be in that wallet before any of it works.
USDG on Robinhood Chain. It is the dollar the coins are issued against — the seed behind a market you launch, and the money you spend buying a side. Nothing else is accepted as collateral, because the pair has to be redeemable for exactly one dollar of the same thing the market is holding.
A little ETH on the same chain. Gas is paid in the chain's own currency and never in the collateral, so a wallet holding a thousand USDG and no ETH cannot send anything at all. Fractions of a cent cover a trade.
There is no faucet here and no on-ramp. Bring both to the wallet yourself; once you connect one the site says what it can actually see in it.
Launching one
A launch is one call. The creator names the underlying, the two strikes, the expiry, the dollars they are putting behind it and where they want the coin to open. The factory splits the seed into a pair, puts both sides into the pool, and hands the creator back the surplus of the side they opened rich. That surplus is their position, and it is paid for out of their own collateral rather than out of anybody else's.
| Seed at least 50 USDG | A pool of two dollars is not a market, it is a spread. |
| Expiry 1 to 365 days out | Anything shorter is a coin flip on a feed's update; anything longer is a coin nobody will still be watching. |
| Strikes at least 1% apart | A narrower spread makes a cent of movement decide the whole dollar. |
| Opens between 5¢ and 95¢ | Outside that the pool is so lopsided that the first trade moves it to the wall. |
| The feed must exist | A market the chain cannot price is a market that can never settle. |
Fees
One percent of the dollars in a trade, charged on the way in and on the way out, split down the middle between the creator of the market and the protocol. Nothing is charged on a split, a merge, a settlement or a redemption, so the collateral itself is never taxed and a holder who never trades never pays.
What can go wrong
- It is unaudited. A Foundry suite and a JavaScript mirror of the same maths are evidence, not an audit.
- The oracle is Chainlink's. A feed that is wrong at the moment of settlement settles the market wrong, and there is no appeal.
- Liquidity is whoever put it there. A thin pool moves a lot on a small trade, and the price shown is the pool's, not a consensus.
- Coins expire. A call coin whose underlying finishes below the lower strike is worth exactly nothing, and it was always going to be.
- Impermanent loss is real here too. A liquidity provider in a market that resolves ends up holding mostly the losing side.
The contracts
| OptionMarket.sol | One market: the pair, the pool, the fees, the settlement and the redemption. No owner and no setters. |
| OutcomeToken.sol | One side, as an ERC-20 with six decimals. Only its market can mint or burn it. |
| StrikeFactory.sol | Launches markets, names the coins from the strikes, enforces the bounds. Its owner can pause new launches and nothing else. |
| ChainlinkOracle.sol | One aggregator per market, read straight, with a staleness limit per feed. |
Running it
npm run dev # the site and the API
npm test # the engine mirror
npm run test:contracts # the contracts, 49 tests
npm run deploy # oracle + factory, prints both addresses
Reading the deployment…