Docs v2 · testnet

Markets · Integrate your market

Use Novak as your market's dispute layer

Keep your own trading. Hand the question “what actually happened?” to Novak. Independent resolvers observe the source and must agree. Anyone can dispute, which escalates to bonded Tier-1 (≤7) and Tier-2 (≤15) committees needing 66% agreement, never a token vote. An event that can never resolve is Voided instead of left hanging. You read the final answer from the Event Bus.

install
npm i https://github.com/mesayanroy/novak/releases/download/sdk-v0.2.0/novakoracle-sdk-0.2.0.tgz viem

Installs the sdk-v0.2.0 release with npm, pnpm or yarn. You import it as @novakoracle/sdk. The npm registry release is on its way, and your imports stay the same.

1. Reuse or create the event

An event is observed, disputed and finalized once, and every market that reads it settles on the same answer. Reuse first: each event page has a Reuse this event panel, and the Builder warns you before you create a duplicate. To ask something new, pick price at a time (stocks, SGOV), corporate actions, trading status or the Fed funds rate. The catalog is on the Robinhood Chain page.

TypeScript
const from = BigInt(getDeployment(46630).startBlock);

// Same question already on Novak? Settle on it instead of creating a duplicate.
const [existing] = await novak.findMatchingEvents({ sourceId, spec, onlyOpen: true }, from);

// Range market: reuse the "NVDA ≥ X at T" boundaries that already exist.
const rungs = await novak.findPriceLadder({ feed: CHAINLINK_FEEDS_MAINNET.NVDA, at: T }, from);
const { reuse, create } = planLadder(rungs, thresholds); // create only what's missing

2a. Settle on-chain: inherit NovakConsumer

Solidity
import { NovakConsumer } from "novak/contracts/integrations/NovakConsumer.sol";

contract MyMarket is NovakConsumer {        // holds ONLY the Event Bus
    bytes32 public immutable eventId;
    constructor(address bus, bytes32 id) NovakConsumer(bus) { eventId = id; }

    function deposit(bool yes) external payable whenNovakPending(eventId) { /* … */ }
    function resolve() external { _settleWithNovak(eventId); }

    function _onNovakResolved(bytes32, bool yes) internal override { /* pay winners */ }
    function _onNovakVoided(bytes32) internal override { /* refund everyone */ }
}
// Range markets: (Resolution status, uint256 bucket) = novakRange(boundaries);

You get novakResolution (Pending / True / False / Voided), whenNovakPending, _settleWithNovak, novakRange and _novakOutcome (with occurredAt). It ships as contracts/integrations/NovakConsumer.sol, with tests that include the guard that it holds only the Bus.

2b. Conditional Tokens venues: NovakCTFAdapter

TypeScript
// NovakCTFAdapter(eventBus, conditionalTokens) is your condition's oracle.
await novak.ctfPrepareBinary(adapter, eventId, account);         // slot 0 = YES
await novak.ctfPrepareRange(adapter, boundaryEventIds, account); // n boundaries ⇒ n+1 slots
// … trade outcome tokens exactly as today …
if (await novak.ctfCanResolve(adapter, questionId))
  await novak.ctfResolve(adapter, questionId, account);          // voided ⇒ equal payouts (refund)

Polymarket-style venues keep their CTF positions. The adapter becomes the condition's oracle and reports Novak's answer once any dispute has run its course. Question IDs are deterministic per event, so venues on the same event share one condition.

2c. Settle off-chain

TypeScript
import { NovakClient, getDeployment, robinhoodTestnet } from "@novakoracle/sdk";

const novak = new NovakClient(publicClient, undefined, getDeployment(robinhoodTestnet.id));
const result = await novak.waitForOutcome(eventId);   // pull loop over the Event Bus
if (result.voided) await refundMarket(marketId);
else await settleMarket(marketId, result.outcome);

Every observation's evidence is public at each resolver's /evidence/:hash, matching the on-chain commitment.

3. Share fees with the layer (optional)

Solidity
usdg.approve(address(vault), fee);
ITreasuryVault(vault).depositFees(eventIds, fee);
// per event, once decided: 1/3 correct resolvers · 1/3 correct committee voters
// (or insurance reserve) · 1/3 treasury

The TreasuryVault pays the people who keep resolution honest. Novak's own markets route every fee through it.

4. Run the dispute layer yourself

  • Use the shared network (nothing to run). Point your market at the testnet Event Bus. Resolvers, keeper and committees are already live, and anyone can dispute from the Dispute Center.
  • Run a resolver. Once the Registry owner authorizes your address, run the resolver image with your key. It observes events, submits evidence, votes in committees and earns its fee third.
  • Run your own Novak instance. Deploy your own Registry, DisputeManager and Event Bus with script/Deploy.s.sol, then start your resolvers and web app with docker compose up.
bash
git clone https://github.com/mesayanroy/novak && cd novak
cp .env.example .env               # RESOLVER_KEYS=0xr1,0xr2,0xr3 (+ ALCHEMY_API_KEY)
docker compose up --build -d       # resolvers :8787/health · web app :3000

Step-by-step commands for all three are in docs/INTEGRATE.md.