Intermediate

Oracles & Off-Chain Data — Kudos build — oracle & randomness

Use oracles safely.

Developer Intermediate
5/6 — Oracles & Off-Chain Data — Kudos build — oracle & randomness

Objective Attach reliable pricing and VRF-based rarity to Kudos badges.

The root problem with conventional currency is all the trust that’s required to make it work.
— Satoshi Nakamoto

Flow Normalize feed data, guard freshness/deviation, request randomness, and store a rarity tier. Emit events so the DApp can render traits.

What to write

  1. Guarded read
    Check freshness and normalize decimals.
        (, int256 answer,, uint256 ts,) = feed.latestRoundData();
        require(ts + 1 hours > block.timestamp, "stale");
        uint256 normalized = uint256(answer) * 1e10;

    Rejects stale rounds and aligns decimals before use.

  2. VRF request
    Record pending rarity request.
        reqId = vrf.requestRandomWords(keyHash, subId, 3, 200000, 1);
        pending[reqId] = Pending({ tokenId: tokenId, price: normalized });
        emit RarityRequested(reqId, tokenId, normalized);

    Tracks pending state to match fulfillments; logs for monitoring.

  3. Fulfill
    Assign tier and clean up pending.
        Pending memory p = pending[reqId];
        require(p.tokenId != 0, "unknown");
        delete pending[reqId];
        uint256 roll = words[0] % 10;
        string memory tier = roll < 2 ? "LEGENDARY" : roll < 5 ? "RARE" : "COMMON";
        badgeTrait[p.tokenId] = tier;
        emit RarityAssigned(reqId, p.tokenId, tier, p.price);

    Prevents replay, assigns rarity, and emits for the UI.

Why Guards keep bad data out, pending maps stop VRF replay, and events let the UI trust on-chain rarity.

function requestRarity(uint256 tokenId) external onlyRole(MINTER_ROLE) returns (uint256 reqId) {
    (, int256 answer,, uint256 ts,) = feed.latestRoundData();
    require(ts + 1 hours > block.timestamp, "stale");
    uint256 normalized = uint256(answer) * 1e10;
    reqId = vrf.requestRandomWords(keyHash, subId, 3, 200000, 1);
    pending[reqId] = Pending({ tokenId: tokenId, price: normalized });
    emit RarityRequested(reqId, tokenId, normalized);
}

function fulfillRandomWords(uint256 reqId, uint256[] memory words) internal override {
    Pending memory p = pending[reqId];
    require(p.tokenId != 0, "unknown");
    delete pending[reqId];
    uint256 roll = words[0] % 10;
    string memory tier = roll < 2 ? "LEGENDARY" : roll < 5 ? "RARE" : "COMMON";
    badgeTrait[p.tokenId] = tier;
    emit RarityAssigned(reqId, p.tokenId, tier, p.price);
}

Why Guards keep bad data out, pending maps stop VRF replay, and events let the UI trust on-chain rarity.

Next Surface these traits in the DApp UX.

← Previous section
Next section →