Daily city-temperature strike ladders on Monad.
"Will Taipei's high reach ≥ 30 °C tomorrow?" One fully collateralized YES/NO pair per strike, one onchain Kuru order book per strike, settled from the airport's METAR reports by a Chainlink CRE workflow.
Monad Metropolis, Track 01 (Onchain Finance & Trading). Isotherm runs on Monad testnet (chain 10143) with faucet AUSD.
| Phone app (PWA) | https://isotherm.pages.dev |
| API (same origin) | /api/health, /api/snapshot, /api/stats, /api/settlements |
Contracts (Sourcify exact_match) |
Resolver · CollateralVault · IsothermZap · all addresses |
| Agent plugin | packages/mm-plugin: mm weather … for the MetaMask Agent Wallet; agent skill in skills/isotherm-weather-trading/SKILL.md |
| Videos | Technical demo (2:58) · Pitch (1:55) · Dynamic sign-in flow (1:12) · Chainlink CRE live run (1:41) · MetaMask Agent Wallet flow (3:07) |
| Design docs | ARCHITECTURE.md · docs/OPERATIONS.md · logo and cover in brand/ |
Explorer links (note added 2026-10-26, after submission; no code changed): MonadVision now keeps transaction pages for only about 9 days, so the transaction links in this README open an error page there. Every one of those transactions is still on chain with status success. To view one, open the same hash on MonadScan:
https://testnet.monadscan.com/tx/<hash>. Contract and address pages on MonadVision still load.
Polymarket already trades daily high-temperature markets that resolve on a public airport weather report. Each city-day is split into 11 mutually exclusive temperature buckets (as of October 2026), traded on an off-chain-matched order book. Isotherm is built for the traders and bot builders already on those markets, and for agents through the MetaMask plugin.
Isotherm rebuilds the same risk as a strike ladder: for one station and one local day, a set of cumulative "Tmax ≥ k °C" strikes, each with its own onchain Kuru order book on Monad. Each strike is a complete set, 1 YES + 1 NO backed by exactly 1 AUSD, minted as plain ERC-20s. YES trades on the strike's book; NO is one tap away. A market maker quotes every book around the Polymarket-implied probability, and after the local day ends a Chainlink CRE workflow reads the station's METAR reports from two independent archives and settles the whole ladder with one attested report. Winners redeem for 1 AUSD 15 minutes later.
The vision: weather risk as composable onchain instruments. Every strike is a public book that people, contracts and agents can read and trade, with settlement anyone can recompute from public data.
flowchart LR
subgraph DATA["Keyless public data"]
PM["Polymarket prices<br/>implied P(Tmax ≥ k)"]
OM["Open-Meteo forecasts<br/>guardrail model"]
IEM["IEM METAR archive"]
AWC["aviationweather.gov METAR"]
OG["Ogimet METAR<br/>fallback"]
end
subgraph SVC["Isotherm services"]
MK["Market maker<br/>daily roll + quotes"]
WEB["Phone PWA<br/>Dynamic email login"]
API["API<br/>drip + gasless relayer + stats"]
MMP["MetaMask Agent Wallet<br/>plugin"]
CRE["Chainlink CRE<br/>settlement workflow"]
end
subgraph CHAIN["Monad testnet"]
VAULT["CollateralVault<br/>YES/NO OutcomeTokens"]
ZAP["IsothermZap"]
KURU["Kuru order books<br/>one per strike"]
FWD["CRE forwarder<br/>(MockKeystoneForwarder)"]
RES["Resolver"]
end
PM & OM --> MK
MK -->|"createLadder, mintSet"| VAULT
MK -->|"new book per strike, quotes"| KURU
WEB --> API
API -->|"relays signed AUSD authorizations"| VAULT
WEB & MMP -->|"buy / sell"| ZAP
ZAP --> KURU & VAULT
IEM & AWC --> CRE
OG -.-> CRE
CRE -->|"EIP-712 attested report"| FWD
FWD --> RES
RES -->|"Tmax or void"| VAULT
- Open. From noon the day before, the roll job picks up to four strikes nearest the Polymarket-implied median (none with an implied probability outside 5–95 %; up to six before the 2026-10-09 release), creates the ladder (two token clones per strike, one transaction), opens one Kuru YES/AUSD book per strike and registers it as the strike's canonical market in the Zap.
- Quote. The maker mints inventory and quotes every book around the Polymarket-implied P(Tmax ≥ k), conditions on the day's observed METAR maximum, and pulls all quotes 10 minutes before close.
- Trade. Buy YES in one transaction through the Zap; buy NO as
mintSet+sellYeswith an exact price bound; sell, or merge a YES+NO pair back into 1 AUSD at any time. - Settle. From 02:00 local the next day, the CRE workflow fetches every METAR and SPECI report for the local day. Two complete sources that agree produce an attested report. Disagreement or missing data stays pending with hourly retries, and voids at 0.5/0.5 only after 36 hours.
- Redeem. A settled result is final after a 15-minute guardian challenge window; YES pays 1 AUSD if Tmax ≥ k, NO otherwise.
The settlement rule reproduces how Polymarket's daily-high markets resolve: the maximum integer °C in the METAR temperature group over all reports in the station's local day. Replayed against every Polymarket event that resolved on the same station (spikes/weather/RESULT.md):
| Station | Polymarket events | Isotherm rule agrees |
|---|---|---|
| Taipei Songshan (RCSS), 2026-04-05 → 10-05 | 184 | 183 / 184 (on the remaining day, two independent METAR archives both hold a 25 °C report) |
| Tokyo Haneda (RJTT), 2026-03-10 → 10-05 | 209 | 209 / 209 |
-
Daily Taipei ladders since 2026-10-07. Every day at 12:00 Taipei the next day's ladder opens, with one Kuru book per strike. By 2026-10-10 that is Oct 8 (≥ 28–31 °C), Oct 9 (≥ 28–32), Oct 10 (≥ 28–31) and Oct 11 (≥ 29–31): 16 Kuru books; the live app and
/api/snapshotshow the current ladder. The first went live with 27 transactions in 64 seconds: the ladder, four Kuru books, the maker's inventory and the opening quotes (createLadder,docs/evidence/golive/). -
Settled automatically, matching Polymarket. Each ladder settles at 02:05 Taipei after its day ends, through the official CRE CLI, and every result Polymarket has resolved so far equals its high for the same Taipei day (
/api/settlements):Taipei day High Polymarket Settlement tx Thu 2026-10-08 28 °C 28 °C 0x65eee8ef…963dFri 2026-10-09 29 °C 29 °C 0xdfd28ea5…3ecdSat 2026-10-10 30 °C 30 °C 0xacb724d3…bf9dSun 2026-10-11 30 °C 30 °C 0x0266fd63…7d1aMon 2026-10-12 30 °C 30 °C 0xc957ccc1…755fTue 2026-10-13 31 °C 31 °C 0xf3e19cbb…1a6b -
Market maker on Cloudflare (
apps/maker-worker, core inpackages/maker). Live since 2026-10-08 23:06 UTC as a Cloudflare Worker with a Durable Object, after a 6.5-hour shadow run. It quotes every strike around the Polymarket-implied probability, re-quotes when fair value moves or a side fills, rolls the daily ladder, pulls every quote before close with its kill switch, watches each settlement, tops up the role keys from a treasury within caps, and reads Monad through a fallback across public testnet RPC endpoints. -
Settlement workflow (
packages/cre-workflow). TypeScript compiled to WASM, run hourly at :05 on a small Linux server with the official CRE CLI v1.37.0 (cre workflow simulate --broadcast), delivering through Chainlink's MockKeystoneForwarder with an EIP-712 attestation. Its settlement core (settle-core.ts) is byte-identical to the module validated inspikes/weather(enforced by a test), its METAR rule is the one replayed above, and it is tested on real archive captures. Moving it onto a Chainlink DON is prepared and rehearsed on a fork (DON-CUTOVER.md). -
Phone app (
apps/web). Dynamic email sign-in with an embedded wallet, a test-funds drip, a gasless first deposit, Buy YES / Buy NO / Sell / Merge, a portfolio, redemption, and a Results screen that recovers each settlement's attestation signer in the browser. English and Traditional Chinese. -
Agent access (
packages/mm-plugin).mm weather markets | quote | edge | positions | buy | sell | redeem, plus generic Kuru limit orders. Every write goes through the MetaMask Agent Wallet's signing service and policy. -
API (
apps/api). A Cloudflare Worker with a single-sender Durable Object relayer, served same-origin at/api/*.
| Sponsor | How Isotherm uses it | Onchain proof (Monad testnet) |
|---|---|---|
| Kuru | One Kuru v1 YES/AUSD order book per strike (16 by 2026-10-10), created through the Kuru Router's deployProxy and validated on chain by the Zap (base, quote, precision, fee cap) |
Book for ≥ 28 °C created: 0x654807ef…d480; opening maker quotes: 0xb635cafc…c5da |
| Chainlink CRE | Cron-triggered TypeScript workflow, run hourly with the official CRE CLI (cre workflow simulate --broadcast); the Resolver is a CRE IReceiver and accepts EIP-712-attested reports through Chainlink's MockKeystoneForwarder |
Live Taipei ladders settled by the official CRE CLI v1.37.0 on Monad testnet: 2026-10-08, 28 °C (0x65eee8ef…963d, evidence); 2026-10-09, 29 °C (0xdfd28ea5…3ecd); 2026-10-10, 30 °C (0xacb724d3…bf9d); 2026-10-11, 30 °C (0x0266fd63…7d1a); 2026-10-12, 30 °C (0xc957ccc1…755f); 2026-10-13, 31 °C (0xf3e19cbb…1a6b). Each result equals Polymarket's resolved high; /api/settlements lists every settled day. On 2026-10-07, before the first live settlement, the same CLI settled RCSS + RJTT 2026-10-06 against the v1 contracts on a fork (sim-fork.txt) |
| Dynamic | Email sign-in with an embedded wallet, the default sign-in in the live app; the embedded wallet signs EIP-3009 authorizations for gasless deposits and sends its own trades | Recorded flow, 2026-10-09: email sign-in, test-funds drip, gasless YES+NO pair from a signed authorization (0x23fa1a42…650f), then the wallet's own Zap buy (0x120e87a2…09e6) (evidence). First proof, 2026-10-07: 0xca08d015…bf04, 0x361668d8…681c |
| MetaMask Agent Wallet | mm-plugin-isotherm, an oclif plugin for mm 7.x, installed from this repo (setup); writes go through walletExecutor, so MetaMask's signing service and Guard policy see each transaction |
Signed-in MetaMask Agent Wallet trade in Guard Mode, 2026-10-08: 2 AUSD → 6.054545 YES ≥ 31 °C: 0x82358f48…52ee (evidence) |
| Agora AUSD | The collateral: every YES+NO pair is backed by 1 AUSD. AUSD's EIP-3009 receiveWithAuthorization makes the first deposit gasless |
AUSD; the gasless deposit above moved 5 AUSD on a signed authorization |
- A real order book for every strike. Kuru is a fully onchain CLOB, so each strike is a book that contracts and agents can read and trade. Opening one costs 1,467,042 gas (about 0.15 testnet MON); a ladder opens up to four of them every day, which only makes sense where blockspace is cheap (gas table).
- Fast enough for a phone. Blocks average 0.302 s on both Monad mainnet and testnet (1,000-block windows, Oct 11; 0.305 s in the Oct 6 verification), and blocks are final two slots (about 0.6 s) after they land; send-to-receipt had a median of 1,403 ms over the public RPC across a 56-transaction live run (log); the go-live buy in a phone-size browser filled in 1.5 s (go-live).
- Cheap re-quotes. A live re-quote of one strike (cancel 2 + place 2) billed 0.055–0.058 MON, so the maker can follow the market through the day.
- Fast payout. Redemption opens 15 minutes after the CRE report, instead of an optimistic-oracle proposal and dispute period.
Source of truth: deployments/testnet.json. v1, deployed 2026-10-07 at block 68,884,377; Solidity 0.8.37. All four are Sourcify exact_match on sourcify-api-monad.blockvision.org and sourcify.dev.
| Contract | Monad testnet (10143) |
|---|---|
Resolver (CRE IReceiver) |
0x9c7876Bc27df6cB473f2eaFA296FdEC22747962B |
| CollateralVault + StrikeFactory | 0xae36cf0a163bAfCde4D40a6Ab7b5E3C762ad7B39 |
| IsothermZap | 0x1ACaf47987Fe570df5d136Ae1CaC0D45E2B8CFb0 |
| OutcomeToken implementation (each YES/NO is an EIP-1167 clone) | 0x5EfaB33DDad0715b66f514Fe12d78Ca23f3e31fC |
| AUSD (Agora, testnet) · AUSD faucet | 0xa9012a055bd4e0eDfF8Ce09f960291C09D5322dC · 0xd236c18D274E54FAccC3dd9DDA4b27965a73ee6C |
| Kuru v1 Router · MarginAccount | 0x7EFbE105Ca7415dE98F96622173458ac1c054630 · 0xd029C2D98ff85D8F64799017fE00a59B1159CE02 |
| CRE MockKeystoneForwarder (active) · KeystoneForwarder | 0xB9F79d863261869B234c481D1f9A7af84AeAd192 · 0xF8344CFd5c43616a4366C34E3EEE75af79a74482 |
Kuru books of the RCSS 2026-10-08 ladder: ≥ 28 0x171b…DBd7, ≥ 29 0x855e…c6c2, ≥ 30 0x702A…36DC, ≥ 31 0x4f5E…5813. Token addresses and seriesIds: docs/OPERATIONS.md.
Stack: Solidity 0.8.37 and Foundry · TypeScript on Node 22 and Bun · a Vite + React + viem PWA · Cloudflare Pages, Workers and Durable Objects · Chainlink CRE (TypeScript compiled to WASM) · Kuru v1 order books · Dynamic SDK · MetaMask Agent Wallet plugin (oclif) · Monad testnet (chain 10143). Architecture: ARCHITECTURE.md.
src/ test/ script/ Solidity contracts, Foundry tests (unit, fuzz, invariant, fork, security), deploy and e2e scripts
deployments/ testnet.json: every address, role and parameter
packages/forecast/ METAR sources, the settlement rule, Polymarket-implied fair values, guardrail model
packages/maker/ Market maker and daily ladder roll
packages/cre-workflow/ Chainlink CRE settlement workflow (TypeScript → WASM)
packages/mm-plugin/ MetaMask Agent Wallet plugin
skills/ Agent skill for the plugin (isotherm-weather-trading/SKILL.md, same file as packages/mm-plugin/SKILL.md)
packages/abi/ ABIs exported from forge
apps/web/ Phone PWA (Vite + React + viem, Dynamic)
apps/api/ Cloudflare Worker: drip, gasless relayer, snapshot, stats
apps/maker-worker/ The live market maker: Cloudflare Worker + Durable Object
docs/ brand/ spikes/ Runbook and evidence, logo and cover, feasibility spikes from 2026-10-06
Requirements: Node 22.18+ (it runs the .ts sources directly) and Foundry. The CRE workflow also needs bun and the CRE CLI v1.37.0, which packages/cre-workflow/setup.sh installs at pinned versions (the CLI download is checksum-verified); the script has pinned builds for macOS and Linux (arm64 and amd64). Keys live outside the repo (~/.config/isotherm/, one file per role); never commit them.
forge build && forge test # offline suite (fork tests skip)
MONAD_TESTNET_RPC=https://testnet-rpc.monad.xyz FORK_BLOCK=68898133 forge test # full suite: 149 tests in 18 suites
(cd packages/forecast && npm test && npm run settle -- RCSS 2026-10-04 2026-10-05) # recompute days from public archives
(cd packages/maker && npm ci && npm test)
(cd packages/maker && node src/cli.ts preflight --station RCSS --date tomorrow) # operator command: read-only, needs the maker key file
(cd apps/web && npm ci && npm test && npm run dev) # http://127.0.0.1:5173
(cd apps/api && npm ci && npm test) # npm run test:fork: anvil-fork integration tests
(cd packages/mm-plugin && npm ci && npm run build && npm test)
cd packages/cre-workflow && ./setup.sh && export PATH="$PWD/.tools/bin:$PWD/.tools/node_modules/.bin:$PATH"
(cd settle && bun test && bun run build) # unit + golden tests, WASM buildDeployment (script/deploy-testnet.sh, testnet only), the live maker, the API and the settlement job are documented in docs/OPERATIONS.md and each package's README.
Built with AI coding tools: Claude Code (Anthropic). Ted Chen chose the idea, directed the work and made the product decisions; Claude Code wrote most of the code, tests, scripts, documentation and the logo under that direction. Every commit made with Claude Code carries a Co-Authored-By: Claude trailer. The narration in the videos is a synthetic voice generated with VoxCPM2 (OpenBMB, Apache-2.0); no real person's voice was recorded or cloned.
Originality and pre-existing code. Isotherm's own code was all written during the Metropolis build window (Sep 1 – Oct 13, 2026); the first commit is dated 2026-10-06 and the commit history covers the build. No pre-existing code from earlier projects is included. The only code we did not write is the third-party software below. Isotherm builds on existing products and protocols, used as integrations: Monad testnet, Kuru v1 order books, Chainlink CRE, Dynamic, the MetaMask Agent Wallet and Agora AUSD (testnet), plus public Polymarket and weather data (below). The logo and video cover in brand/ are original artwork made for this project.
Summary in NOTICE.md; each component keeps its own license.
| Component | License | Use |
|---|---|---|
| OpenZeppelin Contracts 5.7.0 | MIT | Vendored in lib/: ERC-20, EIP-2612, EIP-712, clones, SafeERC20, reentrancy guard |
| forge-std 1.17.0 | MIT or Apache-2.0 | Tests and scripts |
Kuru v1 interfaces (Kuru-Labs/Kuru-contracts-dex-public, commit 2060bb2) |
GPL-2.0-or-later | src/interfaces/IKuru.sol, spikes/kuru/src/interfaces/IKuru.sol and spikes/e2e/src/IsothermZap.sol are derived from Kuru's public interfaces and carry GPL-2.0-or-later headers. src/IsothermZap.sol (MIT) imports that interface, so the compiled IsothermZap is distributed under GPL-2.0-or-later terms; text in LICENSES/GPL-2.0-or-later.txt. |
| Chainlink CRE TypeScript SDK 1.23.0 | BUSL-1.1 | npm dependency of the workflow |
Chainlink CRE CLI v1.37.0; KeystoneForwarder, MockKeystoneForwarder and the IReceiver interface |
MIT | Build and run the workflow; the forwarders are called onchain; src/interfaces/IReceiver.sol reproduces Chainlink's published IReceiver interface |
Dynamic SDK (@dynamic-labs/* 5.9.4, @dynamic-labs-sdk/* 1.38.0) |
MIT (@dynamic-labs-sdk/*: Dynamic's terms) |
Login and embedded wallet |
MetaMask Agent Wallet @metamask/agent-wallet 7.x |
MetaMask source-available license | Peer dependency of the plugin, not redistributed |
| viem, @noble/curves, oclif, React, Vite, zod | MIT | Libraries |
Attribution and terms for committed data are in DATA-NOTICE.md.
| Source | Used for | Terms |
|---|---|---|
| Iowa Environmental Mesonet (Iowa State University) | Settlement source A, history | Free public archive; cached, low request rates |
| aviationweather.gov (NOAA / NWS Aviation Weather Center) | Settlement source B | U.S. government data; API usage limits observed |
| Ogimet | Settlement fallback | Queried gently, at most once per workflow run |
| Open-Meteo | Guardrail forecast model, shown as "Model" in the app | Weather data by Open-Meteo.com, CC BY 4.0; values are bias-corrected and combined (modified). Free API for non-commercial use |
| Polymarket public Gamma and CLOB APIs | Reference fair value; fidelity check | Read-only public market data under Polymarket's Terms of Use; Isotherm is not affiliated with Polymarket |
MIT, see LICENSE. Files derived from Kuru's public contracts and the compiled IsothermZap are GPL-2.0-or-later as described above; third-party code and data keep their own terms (NOTICE.md, DATA-NOTICE.md).
Built by Ted Chen.
