Skip to content

Latest commit

Β 

History

58 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Tempo Weather Market

CI Tempo Mainnet Tempo Testnet Solidity License

🌐 Live Demo β†’ tempo-weather-market.vercel.app

Prediction market infrastructure built natively on Tempo Network | Weather as first use case | MPP Β· Payment Memo Β· Fee Sponsorship Β· Scheduled Transactions | pathUSD/USDC settlement

Deployed on both Testnet and Mainnet.

Network Contract Address
Tempo Mainnet (4217) 0x072a3a0c04cf8cdcaf5b4a73a4ed4ff5a841531f
Tempo Moderato Testnet (42431) 0xcAC5B9d2817325E78090E3Ce4b9C299C819cF953

Why Tempo-Native

This project wasn't ported from another chain. Every design decision maps directly to a Tempo protocol capability, and the contract would look meaningfully different β€” or require off-chain workarounds β€” on a generic EVM chain.

Problem Generic EVM approach Tempo-native approach
Oracle needs compensation for settlement work Side payment, then manual verification MPP β€” fee collected atomically inside submitResult()
Prove what outcome was settled and why Parse event logs after the fact Payment Memo β€” structured string written on-chain at settlement time
Users shouldn't need gas tokens just to place a bet Require users to bridge and hold native token Fee Sponsorship β€” relayer submits the tx, gasTank covers the cost
Market must stop accepting bets at a precise time External keeper or cron job Scheduled Transactions β€” IScheduler precompile called from within createMarket() (built into the contract; not yet enabled on mainnet β€” see Scheduled Transactions below)

The result is a contract that handles its own lifecycle end-to-end: markets lock themselves, oracle payments are enforced by the contract rather than by trust, and bettors interact with a single stablecoin approval.

Architecture

oracle-server (Express + TypeScript)
    β”‚
    β”œβ”€β”€ POST /oracle/settle     ← n8n calls this after lockTime
    β”œβ”€β”€ GET  /oracle/market/:id ← n8n polls market status
    └── GET  /oracle/health
         β”‚
         β”œβ”€β”€ WeatherAPI.com + Open-Meteo Archive API  ← dual-source median oracle
         └── WeatherMarket.sol  ← submitResult() on-chain

Settlement uses a dual-source median oracle: WeatherAPI.com + Open-Meteo Archive API. Each source's individual reading is recorded in the Payment Memo (wa:/om:) alongside the median finalTemp. OpenWeather is used elsewhere in the stack (see below) but is not part of the settlement median.

Core Features

MPP (Monetized Protocol Primitives)

The oracle calls submitResult() to settle markets. When oracleFee > 0, the caller must first transfer stablecoins to the oracle address and provide a verified paymentTxHash. The contract records a SettlementReceipt for every settled market, queryable via getReceipt(marketId).

Payment Memo

Every settlement writes a structured memo to the contract:

{city}/{predictionType}/{finalTemp}/{outcome}
// e.g. "Tokyo/HIGH_TEMP/315/WIN"

The memo is emitted in the ResultSubmitted event and stored on-chain, providing a human-readable audit trail for each market outcome.

Fee Sponsorship

A gasTank mechanism allows the contract owner to pre-fund gas costs so that users can bet without holding the native fee token. Approved relayers call placeBetFor(marketId, bucket, amount, bettor) on behalf of users β€” bettors only need to hold and approve the stablecoin.

Scheduled Transactions

Status: implemented in the contract, not yet enabled on mainnet. The deployed mainnet contract's scheduler address is currently address(0) (see deployments/tempo.json), so this path is inactive. lockMarket() is called manually today β€” via the owner-only Admin UI (/admin) or maintenance scripts β€” once lockTime passes.

When a scheduler address is configured via setScheduler(), createMarket() calls the IScheduler precompile to schedule an automatic lockMarket() call at lockTime, removing the need for off-chain cron jobs or manual calls.

bytes32 taskId = IScheduler(scheduler).schedule(
    address(this),
    abi.encodeCall(this.lockMarket, (marketId)),
    lockTime
);

Network Information

Network Chain ID RPC Explorer
Tempo Moderato (Testnet) 42431 https://rpc.moderato.tempo.xyz https://explorer.moderato.tempo.xyz
Tempo (Mainnet) 4217 https://rpc.tempo.xyz https://explorer.tempo.xyz

Stablecoin addresses

Network Token Address
Testnet pathUSD 0x20c0000000000000000000000000000000000000
Mainnet USDC.e 0x20C000000000000000000000b9537d11c60E8b50

Quick Start

Prerequisites

  • Node.js 18+
  • Docker & Docker Compose (for Oracle server)
  • WeatherAPI.com key (free tier, 1M calls/month) β€” settlement oracle source
  • OpenWeather API key (free tier) β€” powers the live weather-strip display only, not settlement
  • Tempo testnet tokens from faucet

Local Setup

git clone https://github.com/pplmaverick/tempo-weather-market
cd tempo-weather-market
npm install

# Setup Oracle server
cd oracle-server
cp .env.example .env
# Edit .env: fill in WEATHERAPI_KEY, OPENWEATHER_API_KEY and ORACLE_PRIVATE_KEY
docker compose up -d

# Run tests
cd ..
npx hardhat test

Network Configuration

Network Chain ID RPC Stablecoin
Testnet (Moderato) 42431 https://rpc.moderato.tempo.xyz pathUSD
Mainnet 4217 https://rpc.tempo.xyz USDCE

Switch networks in oracle-server/.env:

TEMPO_NETWORK=testnet   # or mainnet

Contract Interface

// Owner: create a new market
createMarket(city, predictionType, targetDate, buckets, lockTime)

// Anyone: place a bet into a bucket
placeBet(marketId, bucket, amount)

// Relayer (Fee Sponsorship): place a bet on behalf of a user
placeBetFor(marketId, bucket, amount, bettor)

// Anyone: lock the market after lockTime (also called by Scheduler)
lockMarket(marketId)

// Oracle: settle with real weather data (MPP endpoint)
submitResult(marketId, finalTemp, memo)

// Winner: claim winnings after settlement
claimWinnings(marketId)

Temperature Encoding & Bucket System

Temperatures are stored as int256 with one decimal place precision:

315 = 31.5Β°C
-12 = -1.2Β°C

Bucket boundaries use the same x10 encoding as temperatures. Given buckets = [250, 280, 310, 340]:

Bucket Range
0 ≀ 25.0Β°C
1 25.1Β°C – 28.0Β°C
2 28.1Β°C – 31.0Β°C
3 31.1Β°C – 34.0Β°C
4 > 34.0Β°C

Fees & Security

Fees

  • Market fee: 2% of total pool, deducted from winnings
  • Oracle fee: configurable via setOracleFee(), paid in stablecoin per settlement
  • No winner: all bets refunded in full, no fee taken

Security

  • Oracle address set at deployment, updatable only by owner
  • Relayer whitelist managed by owner
  • ReentrancyGuard on claimWinnings
  • All token transfers use OpenZeppelin SafeERC20

Stack

Layer Technology
Smart contract Solidity ^0.8.28, OpenZeppelin 5.x
Development Hardhat 3 + Viem
Oracle server Node.js + Express + TypeScript
Automation n8n workflow
Testnet stablecoin pathUSD (0x20c000...)
Mainnet stablecoin USDC.e (0x20C000...)

Milestones

Milestone Description Status
M1 Deploy to testnet + mainnet, Oracle server on VPS, 44/44 tests βœ… Complete
M2 Oracle retry logic (3 attempts, 2s delay) βœ… Complete
M3 Multi-city support: Taipei, Tokyo, New York, Seoul βœ… Complete
M4 Developer docs: .env.example with inline comments, README setup guide βœ… Complete
M5 React frontend on Vercel βœ… Complete
M6 Dual-source weather median oracle: WeatherAPI + Open-Meteo (OpenWeather History API is not available on this key's plan, dropped from settlement) βœ… Complete
M7 TypeScript SDK πŸ“‹ Planned

Developer

GitHub: pplmaverick Wallet: 0xed2B...78F5 β€” deployed on both Tempo Testnet and Mainnet

License

MIT

About

Weather prediction market on Tempo network, settled in pathUSD/USDC.e

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Packages

Contributors

Languages