← Workspace Index
Architecture Proposal

Chainlink Oracle Integration Plan

Prediction markets need two oracle patterns: (1) on-chain price data for liquid assets and (2) verifiable decisions from off-chain APIs for bespoke events.

Goals

MarketRegistry

Records each market’s oracle config: type, sourceId, timeout, quorumRules.

OracleRouter

Single entry that markets call. Delegates to handlers and emits canonical resolution events.

PriceFeedHandler

Thin wrapper around Data Feeds. Validates heartbeat/staleness before returning standardized price.

Automation Upkeep

Triggers Functions requests on schedule or at market end, preventing manual intervention.

Operational Flows

Price Market

1) Market registers: type=price_feed, sourceId=Aggregator address, heartbeat, deviation.
2) Settlement call → OracleRouter → Handler: validates roundId freshness and writes resolved price.
3) Emits MarketResolved(marketId, price, roundId, updatedAt).

API-Decision Market

1) Market registers sourceId with subscriptionId, router, and expectedSchema.
2) Automation upkeep triggers at market end → calls Functions with specific question payload.
3) Functions JS executes HTTP call, normalizes to JSON, and returns verified status.
4) OracleRouter receives callback, validates signature/replay guard, and finalizes settlement.

Contract Sketches

interface IOracleRouter {
    function resolvePrice(uint256 marketId) external;
    function requestDecision(uint256 marketId) external;
    event MarketResolved(uint256 indexed marketId, bytes32 resolutionType, bytes data);
}

// Logic: Store feed decimals and convert to 18d fixed-point on read.
// Security: pendingRequestId[marketId] to prevent duplicate callbacks.

Off-Chain Functions Script

const res = await Functions.makeHttpRequest({ url, method: "GET", params });
const { outcome, evidenceUrl, signature } = normalize(res.data);
return Functions.encodeString(JSON.stringify({ outcome, evidenceUrl, signature }));

Rollout Roadmap

1. Network & Accounts: Create Functions subscription on Polygon Amoy/BSC. Record IDs in .env.
2. Contract Plumbing: Deploy Registry/Router in nostra-contracts. Wire into settlement entrypoints.
3. Automation Assets: CLI scripts to push Functions source and register Upkeep.
4. SDK & Server: Expose handlers in SDK. job-watch MarketResolved events for indexing.
5. Ops: Provision LINK, set up dashboards for feed freshness and runway alerts.

Adoption & Setup

Price Markets
  • 1. Feed Discovery: Find proxy addresses for BTC/USD, etc. on the Chainlink Directory.
  • 2. Registration: If the feed is restricted, add our OracleRouter address to the Proxy's "Consumers" list.
  • 3. Funding: No LINK tokens needed. Only native gas (MATIC/BNB) is charged for read/resolve transactions.
  • 4. Repo actions: Write the proxy + heartbeat into nostra-contracts/packages/contracts/reference/feeds.<chain>.json and surface through the SDK; expose the active feed/updatedAt in nostra-server/api and show it in the web market details.
API Decisions
  • 1. Acquire LINK: Request from Faucet (Testnet) or swap on DEX (Mainnet).
  • 2. Functions Sub: Create a Subscription ID on the Functions Portal and fund with LINK.
  • 3. Automation Portal: Register a Custom Logic Upkeep on the Automation Portal to trigger resolutions.

Price Feed Onboarding (Nostra-specific)

Detailed steps for using Chainlink price feeds in the Nostra stack:

Notes & Error Handling


Task-1: Claim "Not Resolved" Error Analysis

Date: December 12, 2025

Error Message: Failed to claim: execution reverted: "Not resolved"

1. Error Analysis

Error Origin

The error occurs in ConditionalTokens.sol:160

function redeemPositions(...) external { Condition storage condition = conditions[conditionId]; require(condition.isResolved, "Not resolved"); // <-- Line 160 ... }

The redeemPositions function checks if the condition has been resolved on-chain before allowing token redemption.

2. Root Cause: Uncommitted Code Regression

Key Discovery: Committed Code is Correct

The committed code (HEAD) uses the correct function adminFinalizeResolution().

The uncommitted changes introduced a broken function call resolveMarket() which does NOT exist in the contract ABI.

Version Comparison

Version Function Called Status
Committed (HEAD) adminFinalizeResolution(questionId, outcomeSlotCount, payouts) CORRECT
Uncommitted (working) resolveMarket(questionId, payoutIndex) BROKEN

Committed Code (Working)

// Committed version - CORRECT const outcomeSlotCount = mkt.outcomes.length; // 2 for binary markets const tx = await resolutionOracle.adminFinalizeResolution( mkt.questionId, outcomeSlotCount, payouts ); await tx.wait(); txHashes.push(tx.hash);

Uncommitted Code (Broken)

// Uncommitted changes - BROKEN (resolveMarket does NOT exist!) try { const tx = await resolutionOracle.resolveMarket(mkt.questionId, payoutIndex); await tx.wait(); txHashes.push(tx.hash); } catch (e: any) { console.warn(`On-chain resolution failed: ${e.message}`); // Silently continues - DB updated but on-chain NOT resolved! }

3. The Problem with Uncommitted Changes

Why the Uncommitted Code Fails

  1. resolveMarket() does NOT exist in the ResolutionOracle contract ABI
  2. The call throws an error, caught by try/catch
  3. Code continues and updates DB to RESOLVED
  4. On-chain condition.isResolved remains false
  5. User tries to claim → redeemPositions() reverts with "Not resolved"

Available Functions in ResolutionOracle

Function Parameters Access Description
proposeResolution questionId, outcomeSlotCount, payouts[] onlyResolver Proposes resolution, starts dispute period
finalizeResolution questionId, outcomeSlotCount onlyResolver Finalizes after dispute period (1 day)
adminFinalizeResolution questionId, outcomeSlotCount, payouts[] onlyOwner Immediate resolution - bypasses dispute period (CORRECT FUNCTION)
resolveMarket - - DOES NOT EXIST

4. Execution Flow Comparison

Uncommitted Code Flow (BROKEN)

Admin clicks Resolve resolveMarket() throws error catch: console.warn() DB Updated (RESOLVED) User clicks Claim redeemPositions() REVERTS

Committed Code Flow (CORRECT)

Admin clicks Resolve adminFinalizeResolution() reportPayouts() called condition.isResolved = true DB Updated (RESOLVED) User clicks Claim redeemPositions() SUCCESS

5. Solution

Option 1: Discard Uncommitted Changes (Recommended)

If the uncommitted changes to admin.ts are not needed, simply restore the committed version:

git checkout api/src/routes/admin.ts

This will restore the working adminFinalizeResolution() calls.

Option 2: Fix the Uncommitted Changes

If the uncommitted changes contain other necessary modifications, replace the broken resolveMarket() calls:

// Replace this (BROKEN): const tx = await resolutionOracle.resolveMarket(mkt.questionId, payoutIndex); // With this (CORRECT): const outcomeSlotCount = 2; const payouts = payoutIndex === 0 ? [100, 0] : [0, 100]; const tx = await resolutionOracle.adminFinalizeResolution( mkt.questionId, outcomeSlotCount, payouts );

6. Payout Array Format

Important: Payouts Array

The adminFinalizeResolution function requires a payouts array, not just an index:

Winning Outcome payoutIndex payouts Array
YES (first outcome) 0 [100, 0]
NO (second outcome) 1 [0, 100]

The values represent percentage (100 = full payout to that outcome, 0 = no payout).

7. Markets Affected

Markets Resolved with Broken Code

Any markets resolved while running the uncommitted code are in an inconsistent state:

To fix these markets:

  1. Identify all markets with status: 'RESOLVED' in the database
  2. For each market, call adminFinalizeResolution manually or via a migration script
  3. Verify on-chain resolution using conditionalTokens.conditions(conditionId).isResolved

8. Summary

Aspect Details
Root Cause Uncommitted changes introduced broken resolveMarket() call
Committed Code Correct - uses adminFinalizeResolution()
Fix Discard changes or fix resolveMarketadminFinalizeResolution
Restart Required Yes - API server restart needed after fix

9. Git Commands Reference

# View current changes git diff api/src/routes/admin.ts # Discard uncommitted changes to admin.ts (restore working version) git checkout api/src/routes/admin.ts # Or selectively stage other changes and reset admin.ts git stash git checkout api/src/routes/admin.ts git stash pop