UnitFlow LogoUnitFlow Docs

Developer Reference

This page covers the contract interfaces, key function signatures, React hooks, and integration patterns for building on top of UnitFlow Predict.

Contract Addresses (Arc Testnet)

ContractAddressType
PredictMarketFactory0x7Ec112983011db79f907285daBc759643A9D8304Proxy
PredictMarketFactory (impl)0x2EA73225038E9D8b9767B722425c1d69dB8EB748Implementation
PredictOracle0xc40E6653D3a76FAA8F3F68060f1D09AEB5153A15Proxy
PredictOracle (impl)0xbdB7FD8E6FB1a0F3976E46B49C2c92BC532450F1Implementation
FeeDistributor0x0d0425413284ebB4023913ef78Eb207241d3b2eCProxy
FeeDistributor (impl)0x7b528bC0AF9Cd45F3C37982987E99ADD43B9a49EImplementation
USDC0x3600000000000000000000000000000000000000ERC-20
EURC0x89b50855aa3be2f677cd6303cec089b5f319d72aERC-20

PredictMarketFactory — Key Functions

createMarket

function createMarket(MarketParams calldata params)
  external
  returns (address marketAddress)

struct MarketParams {
  string   question;
  string   description;
  string   category;
  string[] tags;
  address  currency;          // USDC or EURC
  uint256  resolutionDate;    // unix timestamp, must be future
  address  resolver;          // oracle proxy or custom address
  string   oracleSource;      // description of data source
  uint256  initialLiquidity;  // >= 10e6 (10 USDC/EURC)
}

// Caller must approve: initialLiquidity + marketCreationFee (5e6)

getAllMarkets

function getAllMarkets() external view returns (address[] memory)

marketCreationFee

function marketCreationFee() external view returns (uint256)
// Returns 5e6 (5 USDC/EURC)

PredictMarket — Key Functions

stakeYes / stakeNo

function stakeYes(uint256 amount) external
function stakeNo(uint256 amount)  external

// Requirements:
//   amount >= MIN_STAKE (1e6)
//   amount <= min(pool × 10%, 100_000e6)
//   market must be seeded and not resolved
//   block.timestamp < resolutionDate
//   caller must have approved this contract for amount

claimReward

function claimReward() external

// Requirements:
//   market must be resolved
//   caller must have a winning position
//   caller must not have already claimed

getUserPosition

function getUserPosition(address user)
  external view
  returns (
    uint256 yesShares,
    uint256 noShares,
    uint256 totalStaked,
    bool    claimed
  )

estimatePayout

function estimatePayout(address user)
  external view
  returns (
    uint256 grossPayout,
    uint256 netPayout,
    uint256 claimFee
  )
// Returns (0,0,0) if market not resolved or user has no winning position

getMarketInfo

function getMarketInfo()
  external view
  returns (
    bytes32  marketId,
    string   question,
    string   description,
    string   category,
    string[] tags,
    address  currency,
    uint256  resolutionDate,
    address  resolver,
    string   oracleSource,
    address  creator,
    uint256  createdAt
  )

getParticipants

function getParticipants() external view returns (address[] memory)
function getParticipantCount() external view returns (uint256)
function getParticipantsPaginated(uint256 offset, uint256 limit)
  external view
  returns (address[] memory page, uint256 total)

Pool state

function yesPool()     external view returns (uint256)
function noPool()      external view returns (uint256)
function totalStaked() external view returns (uint256)
function resolved()    external view returns (bool)
function outcome()     external view returns (bool)
function seeded()      external view returns (bool)

PredictOracle — Key Functions

// Propose outcome (owner or authorized resolver only)
function proposeResolution(address market, bool outcome) external

// Dispute during 24h window (anyone)
function disputeResolution(address market) external

// Finalize after window (anyone)
function finalizeResolution(address market) external

// Override disputed resolution (owner only)
function overrideResolution(address market, bool outcome) external

// Read resolution state
function resolutions(address market) external view returns (
  bool    proposedOutcome,
  uint256 proposedAt,
  address proposedBy,
  uint8   status,       // 0=None 1=Proposed 2=Disputed 3=Finalized
  address disputedBy
)

uint256 public constant DISPUTE_WINDOW = 24 hours

React Hooks

All hooks live in src/hooks/predict/.

HookReturns
useOnChainMarkets()All markets from factory via RPC. Refreshes every 20s.
useMarkets()Filtered/sorted market list for the UI. Falls back to mock if factory empty.
useMarket(idOrAddress)Single market with live pool state (15s refresh).
useStake(marketAddress, currency)stake(amount, isYes), balance, txState. Handles approve + stake in sequence.
useClaim(marketAddress)claim(), txState. Calls claimReward().
useUserPosition(marketAddress, ...)Connected wallet's position on one market. Includes payout estimate.
usePortfolio()All positions for connected wallet across all markets.
useOnChainLeaderboard()Full leaderboard from getParticipants() + getUserPosition().
useProtocolStats()Aggregate volume, market count, participants, fees.
useResolveMarket(marketAddress)propose(outcome), finalize(), oracle state.
useCreateMarket()createMarket(params), txState, creationFee.
useAmmEstimate(amount, isYes, pool)Live share estimate and updated odds for a given stake amount.

AMM Math Utilities

Pure functions in src/lib/predict/amm.ts. All use BigInt.

import {
  getOdds,          // { yesOdds, noOdds } in basis points
  estimateStakeYes, // { shares, newYesOdds, newNoOdds, fee }
  estimateStakeNo,
  estimateClaim,    // { grossPayout, claimFee, netPayout }
  formatTokenAmount,
  parseTokenAmount,
} from '@/lib/predict/amm'

// Example: get current odds
const { yesOdds, noOdds } = getOdds({ yesPool, noPool })
// yesOdds = 5800 means 58% implied probability of YES

// Example: estimate a stake
const est = estimateStakeYes(parseTokenAmount('100'), { yesPool, noPool })
// est.shares     → shares you receive
// est.newYesOdds → odds after your stake
// est.fee        → protocol fee deducted

Integration Example

Reading all markets and a user's positions from a custom component:

import { useOnChainMarkets } from '@/hooks/predict/useOnChainMarkets'
import { usePortfolio }      from '@/hooks/predict/usePortfolio'

function MyComponent() {
  const { markets, isLoading } = useOnChainMarkets()
  const { positions, stats }   = usePortfolio()

  return (
    <div>
      <p>{markets.length} markets, {stats.marketsParticipated} participated</p>
      {positions.map(p => (
        <div key={p.id}>
          {p.market.question} — {p.isYes ? 'YES' : 'NO'} — {p.status}
        </div>
      ))}
    </div>
  )
}

Source Code