Unfodl.

UMA Optimistic Oracle V3: Integration

Cover Image for UMA Optimistic Oracle V3: Integration
Marco Montes
Marco Montes

UMA Optimistic Oracle V3: Integration

What is UMA

UMA stands for Universal Market Access. It is a protocol that lets smart contracts safely use facts from the real world. Two core parts make this work:

  • Optimistic Oracle (OO)
    A system where a person or contract posts a claim and a bond. If nobody disputes the claim during a set time window, the claim is accepted. If there is a dispute, UMA voters decide the truth.

  • Data Verification Mechanism (DVM)
    The voting and arbitration layer. If a claim is disputed, voters review the claim and the rules, then return a final answer. The losing side pays costs by losing bonds and fees.

Why this matters

With UMA, your smart contract can react to real world events. It can trigger payouts if a price goes up, if a match was won, or if a weather limit was passed.

Simple analogies before we code

  • Assertion
    Think of an assertion like pinning a statement on a community board with a kitchen timer beside it. The statement must be true or false. Example: “The daily max temperature in Manhattan NY exceeded 35 C on 2022-07-25 UTC.” You start the timer.

  • Bond
    The bond is a refundable security deposit. It says “I believe this statement.” If nobody objects before the timer ends, the statement is accepted and the bond returns. If someone proves it wrong, the bond helps cover costs.

  • Liveness
    Liveness is the length of that timer. While it is ticking, anyone can dispute.

With UMA OO V3 you submit a claim, post a bond in an ERC-20 token, wait through liveness, then settle to get a boolean result that your contract can use.


What we will build

A tiny contract that:

First, stores a clear yes or no claim, second starts an OO V3 assertion after a chosen time, Third, waits for the liveness window, and lastly settles and stores YES or NO. This will also expose a simple getter for UIs or payout logic.


Minimal V3 contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@uma/core/contracts/optimistic-oracle-v3/interfaces/OptimisticOracleV3Interface.sol";

contract OOv3_GettingStarted {
    // UMA V3 oracle and settings
    OptimisticOracleV3Interface public immutable oo;
    IERC20  public immutable bondCurrency;         // ERC20 used for fees and bond, for example WETH or USDC
    bytes32 public immutable identifier;           // usually bytes32("ASSERT_TRUTH")
    uint64  public immutable liveness;             // seconds to allow disputes
    uint256 public immutable resolutionTime;       // when the fact is considered fixed

    // Human readable statement (your question phrased as a claim)
    string  public claimText;

    // Optional extra metadata for routing or context
    bytes   public extraAncillary;

    // Assertion lifecycle
    bytes32 public assertionId;
    enum Outcome { UNRESOLVED, YES, NO }
    Outcome public outcome = Outcome.UNRESOLVED;

    constructor(
        address _oo,
        address _bondCurrency,
        bytes32 _identifier,        // bytes32("ASSERT_TRUTH")
        uint64  _liveness,          // example: 1800
        uint256 _resolutionTime,    // unix timestamp when the fact is fixed
        string memory _claimText,   // statement that is true or false
        bytes memory _extraAncillary// optional, can be ""
    ) {
        oo = OptimisticOracleV3Interface(_oo);
        bondCurrency = IERC20(_bondCurrency);
        identifier = _identifier;
        liveness = _liveness;
        resolutionTime = _resolutionTime;
        claimText = _claimText;
        extraAncillary = _extraAncillary;
    }

    /// Start the assertion after the resolution time.
    /// Fund this contract with enough bondCurrency to cover UMA fees and any extra bond.
    function startAssertion(uint256 bond) external {
        require(block.timestamp >= resolutionTime, "Too early");
        require(assertionId == bytes32(0), "Already asserted");

        // Approve the oracle to pull fee and bond from this contract
        bondCurrency.approve(address(oo), type(uint256).max);

        // Convert the human readable claim to bytes
        bytes memory claim = bytes(claimText);

        assertionId = oo.assertTruth(
            claim,
            address(this),      // asserter
            address(0),         // no callback in this minimal example
            address(0),         // no custom escalation manager
            liveness,
            bondCurrency,
            bond,               // extra bond (can be 0 for demos)
            identifier,         // bytes32("ASSERT_TRUTH")
            extraAncillary      // optional metadata, can be ""
        );
    }

    /// Settle with UMA and store the final outcome.
    function finalize() external returns (bool yes) {
        require(assertionId != bytes32(0), "No assertion");
        require(outcome == Outcome.UNRESOLVED, "Already finalized");
        yes = oo.settleAndGetAssertionResult(assertionId);
        outcome = yes ? Outcome.YES : Outcome.NO;
    }

    function getResult() external view returns (string memory) {
        if (outcome == Outcome.UNRESOLVED) return "UNRESOLVED";
        return outcome == Outcome.YES ? "YES" : "NO";
    }
}

What is the question vs ancillary data in V3

  • claim is the actual statement that must be true or false. Example:
    The daily max temperature in Manhattan NY exceeded 35 C on 2022-07-25 UTC.
    Voters judge this statement.

  • extraAncillary is optional machine readable bytes for context or routing. You can leave it empty, or include JSON like bytes that describe the source and method.

  • identifier is usually bytes32("ASSERT_TRUTH") for boolean assertions.


Step by step flow

  1. Prepare
    Deploy the contract with the OO V3 address, ERC-20 bond token, identifier, liveness, resolution time, and claim.

  2. Fund
    Transfer the bond token to this contract. The contract will approve OO to pull fees and the extra bond during startAssertion.

  3. Assert
    After resolutionTime, call startAssertion(bond). UMA records your claim and starts liveness.

  4. Wait
    During liveness, anyone can dispute. If nobody disputes before time runs out, your claim is accepted.

  5. Settle
    Call finalize(). UMA returns true or false. The contract stores YES or NO.

  6. Read
    Call getResult() or read outcome. Use it for payouts or UI.


Common pitfalls

  • Do not pass native ETH as the bond currency. Use an ERC-20 like WETH or USDC.
  • Make the claim precise. Include date, time zone, place, units, and a clear rule.
  • Set a realistic liveness so challengers have time to react.
  • Fund and approve the bond currency before asserting.
  • Use the correct OO V3 address and identifier for your network.
  • Store and reuse assertionId. You need it for settlement and checks.