Medinah Country Club · Sept 22–27, 2026

Presidents Cup
Live on Base.

No Polymarket market. No external data feed. We deployed a contract on Base Sepolia, pointed Quicknode Streams at it, and every result lands here within seconds of the block. 81ms, no middlemen.

-- Days
-- Hours
-- Min
-- Sec
0
USA points
0
Intl points
5
Sessions total
81ms
Avg RPC latency
Two Teams. Five Sessions. One Chain.
USA
Loading roster…
International
Loading roster…
Scoreboard
Session Max pts USA Intl Tx
Thu · Four-ball 5
Fri · Foursomes 5
Sat · Foursomes 4
Sat · Four-ball 4
Sun · Singles 12
Total · first to 15.5 wins 0 0
Pushed by Quicknode Streams · Base Sepolia contract · updates every 30s
Call every session. Top predictor wins.
Top Predictors
One contract. Five sessions. Cloudflare KV cache.
01

Contract deployed on Base Sepolia

A single Solidity contract accepts five session results from one owner wallet. No funds held. The moment a session is official, one transaction writes it permanently onchain.

02

Quicknode Streams pushes the result

A filter function in the Streams dashboard decodes every ResultLogged event on Base Sepolia and pushes the decoded JSON to this server. No polling, no missed blocks.

03

Cloudflare KV caches the state

The webhook writes parsed standings to Cloudflare KV. This scoreboard reads KV on every poll — 81ms reads, no chain calls needed on page load. If KV is cold, a bounded RPC fallback reads the last 2,000 blocks.

// PresidentsCupResults.sol — single-purpose, no funds
pragma solidity ^0.8.20;

contract PresidentsCupResults {
    address public owner;
    mapping(uint256 => bool) public sessionLocked;

    event ResultLogged(
        uint256 indexed sessionId,
        uint8   indexed team,    // 0 = USA, 1 = Intl
        uint256         points
    );

    constructor() { owner = msg.sender; }

    function logResult(uint256 sessionId, uint8 team, uint256 points)
        external
    {
        require(msg.sender == owner, "not owner");
        require(!sessionLocked[sessionId], "locked");
        sessionLocked[sessionId] = true;
        emit ResultLogged(sessionId, team, points);
    }
}
// Quicknode Streams — filter function
// Paste into the Streams dashboard under "Filter Function"

function main(data) {
  const CONTRACT = process.env.CONTRACT_ADDRESS.toLowerCase();
  const TOPIC    = process.env.EVENT_TOPIC;   // keccak256("ResultLogged(...)")

  return data.logs
    .filter(log =>
      log.address.toLowerCase() === CONTRACT &&
      log.topics[0] === TOPIC
    )
    .map(log => ({
      sessionId: parseInt(log.topics[1], 16),
      team:      parseInt(log.topics[2], 16) === 0 ? "usa" : "intl",
      points:    parseInt(log.data, 16),
      txHash:    log.transactionHash,
      block:     parseInt(log.blockNumber, 16),
    }));
}
// Pages Function — bounded RPC fallback (KV is primary)
// fromBlock is current − 2000 to avoid block-range limits

const hRes = await fetch(env.QUICKNODE_BASE_ENDPOINT, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ jsonrpc: "2.0", id: 1,
    method: "eth_blockNumber", params: [] }),
});
const latest   = parseInt((await hRes.json()).result, 16);
const fromBlock = "0x" + Math.max(0, latest - 2000).toString(16);

const res = await fetch(env.QUICKNODE_BASE_ENDPOINT, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ jsonrpc: "2.0", id: 2,
    method: "eth_getLogs",
    params: [{ fromBlock, toBlock: "latest",
      address: env.PRESIDENTS_CUP_CONTRACT,
      topics:  [env.PRESIDENTS_CUP_TOPIC] }] }),
  signal: AbortSignal.timeout(4000),
});

const { result: logs } = await res.json();
// decode topics[1] → sessionId, topics[2] → usaX2, topics[3] → intlX2
Every piece runs on Quicknode.
Streams

Watch any event, forever

One filter function in the Streams dashboard watches every Base block. Fires on every ResultLogged event — no missed blocks, no polling.

Streams docs →
Webhooks

Decoded events to any destination

Streams delivers the decoded JSON payload within seconds of the block landing. Point it at Slack, a database, or a custom endpoint — same config.

Configure destinations →
Core RPC

81ms reads on Base

Every scoreboard cell is a direct chain read via eth_getLogs. 99.99% uptime. No third-party data feed. The contract is the source of truth.

Base docs →
SQL Explorer · optional

Prediction market layer

If a Presidents Cup outcome market lists on a supported chain, one SQL query pulls live odds from the indexed tables.

SQL Explorer →

Deploy this on any
EVM chain in an afternoon.

The contract is 30 lines. The Streams filter is 15. The scoreboard reads directly from the chain. Start free — no credit card.

Start building for free Read the Streams docs